aboutsummaryrefslogtreecommitdiffhomepage
path: root/core/src/Text/Edifact/Inspect.hs
blob: fb3755a5140821480950cc404b2cbabc692dbee6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings          #-}

module Text.Edifact.Inspect
  ( inspect
  ) where

import           Text.Edifact.Types

import           Control.Monad.Reader (Reader, ask, local, runReader)
import           Data.Maybe           (catMaybes)
import           Data.String          (IsString)
import           Data.Text            (Text)
import           Formatting

type Indent = Int

type Rendering = Reader Indent

indent :: Rendering a -> Rendering a
indent = local (+1)

getIndentation :: Rendering Int
getIndentation = ask

inspect :: Value -> Text
inspect = renderInspection . valueRenderer

renderInspection :: Rendering a -> a
renderInspection r = runReader r 0

valueRenderer :: Value -> Rendering Text
valueRenderer (Simple    _ primitive) = primitiveRenderer primitive
valueRenderer (Composite _ values)    = sformat inBrackets . commaSeparated . catMaybes <$> traverse positionRenderer values
valueRenderer (Segment code values)   = indentedPrefix fSegmentCode code " " . spaceSeparated . catMaybes =<< traverse positionRenderer values
valueRenderer (Group   code values)   = indentedPrefix fGroupCode code "\n" . lineSeparated =<< indent (traverse silentPositionRenderer values)
valueRenderer (Message code values)   = indentedPrefix fMessageCode code "\n" . lineSeparated =<< indent (traverse silentPositionRenderer values)

inBrackets :: Format r (Text -> r)
inBrackets = "[" % stext % "]"

indentedPrefix :: Format (String -> Text -> Text) (code -> String -> Text -> Text) -> code -> String -> Text -> Rendering Text
indentedPrefix codeFormatter code sep t = do
  i <- getIndentation
  let prefix = replicate (i * 2) ' '
  pure (sformat (string % codeFormatter % string % stext) prefix code sep t)

fMessageCode :: Format r (MessageCode -> r)
fMessageCode = mapf getMessageCode ("message " % string)

fGroupCode :: Format r (GroupCode -> r)
fGroupCode = mapf getGroupCode ("Segment Group - " % string)

fSegmentCode :: Format r (SegmentCode -> r)
fSegmentCode = mapf getSegmentCode string

positionRenderer :: (Position, Maybe Value) -> Rendering (Maybe Text)
positionRenderer (pos, value) = fmap (flip (sformat (stext % fPosition)) pos)  <$> traverse valueRenderer value

silentPositionRenderer :: (Position, [Value]) -> Rendering Text
silentPositionRenderer (_, value) = lineSeparated <$> traverse valueRenderer value

fPosition :: Format r (Position -> r)
fPosition = mapf getPosition ("@" % string)

primitiveRenderer :: Primitive -> Rendering Text
primitiveRenderer (String t) = pure (sformat ("\"" % stext % "\"") t)
primitiveRenderer (Number s) = pure (sformat shown s)

newtype CommaSeparated = CommaSeparated { getCommaSeparated :: Text } deriving newtype (IsString, Eq)

instance Semigroup CommaSeparated where
  t1 <> "" = t1
  "" <> t2 = t2
  t1 <> t2 = CommaSeparated (getCommaSeparated t1 <> "," <> getCommaSeparated t2)

instance Monoid CommaSeparated where
  mempty = ""

commaSeparated :: Foldable f => f Text -> Text
commaSeparated = getCommaSeparated . foldMap CommaSeparated

newtype SpaceSeparated = SpaceSeparated { getSpaceSeparated :: Text } deriving newtype (IsString, Eq)

instance Semigroup SpaceSeparated where
  t1 <> "" = t1
  "" <> t2 = t2
  t1 <> t2 = SpaceSeparated (getSpaceSeparated t1 <> " " <> getSpaceSeparated t2)

instance Monoid SpaceSeparated where
  mempty = ""

spaceSeparated :: Foldable f => f Text -> Text
spaceSeparated = getSpaceSeparated . foldMap SpaceSeparated

newtype LineSeparated = LineSeparated { getLineSeparated :: Text } deriving newtype (IsString, Eq)

instance Semigroup LineSeparated where
  t1 <> "" = t1
  "" <> t2 = t2
  t1 <> t2 = LineSeparated (getLineSeparated t1 <> "\n" <> getLineSeparated t2)

instance Monoid LineSeparated where
  mempty = ""

lineSeparated :: Foldable f => f Text -> Text
lineSeparated = getLineSeparated . foldMap LineSeparated