aboutsummaryrefslogtreecommitdiffhomepage
path: root/scaffolder/src/Text/Edifact/Scaffolder/Commons
diff options
context:
space:
mode:
Diffstat (limited to 'scaffolder/src/Text/Edifact/Scaffolder/Commons')
-rw-r--r--scaffolder/src/Text/Edifact/Scaffolder/Commons/Formatters.hs88
-rw-r--r--scaffolder/src/Text/Edifact/Scaffolder/Commons/Language.hs286
-rw-r--r--scaffolder/src/Text/Edifact/Scaffolder/Commons/Logging.hs11
-rw-r--r--scaffolder/src/Text/Edifact/Scaffolder/Commons/Parsing.hs122
-rw-r--r--scaffolder/src/Text/Edifact/Scaffolder/Commons/Text.hs91
-rw-r--r--scaffolder/src/Text/Edifact/Scaffolder/Commons/Types.hs72
6 files changed, 670 insertions, 0 deletions
diff --git a/scaffolder/src/Text/Edifact/Scaffolder/Commons/Formatters.hs b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Formatters.hs
new file mode 100644
index 0000000..6f0210b
--- /dev/null
+++ b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Formatters.hs
@@ -0,0 +1,88 @@
1{-# LANGUAGE OverloadedStrings #-}
2
3module Text.Edifact.Scaffolder.Commons.Formatters
4 ( -- *
5 fMessageCode
6 , fMessageParserFunction
7 , fGroupCode
8 , fSegmentCode
9 , fSegmentParserFunction
10 , fCompositeCode
11 , fCompositeParserFunction
12 , fSimpleCode
13 , fSimpleParserFunction
14
15 -- *
16 , fParserSignature
17 , fParserDeclaration
18 -- *
19 , fModuleName
20 , fPosition
21 , fPresence
22 -- *
23 , quoted
24 , simpleQuoted
25 , parens
26 , notYetImplemented
27 ) where
28
29import Text.Edifact.Scaffolder.Commons.Types
30
31import Formatting as F
32
33fMessageCode:: Format r (MessageCode -> r)
34fMessageCode = mapf getMessageCode F.string
35
36fMessageParserFunction :: Format r (MessageCode -> r)
37fMessageParserFunction = mapf getMessageCode ("message" % F.string)
38
39fGroupCode :: Format r (GroupCode -> r)
40fGroupCode = mapf getGroupCode F.string
41
42fSegmentCode :: Format r (SegmentCode -> r)
43fSegmentCode = mapf getSegmentCode F.string
44
45fSegmentParserFunction :: Format r (SegmentCode -> r)
46fSegmentParserFunction = mapf getSegmentCode ("segment" % F.string)
47
48fCompositeCode :: Format r (CompositeCode -> r)
49fCompositeCode = mapf getCompositeCode F.string
50
51fCompositeParserFunction :: Format r (CompositeCode -> r)
52fCompositeParserFunction = mapf getCompositeCode ("composite" % F.string)
53
54fSimpleCode :: Format r (SimpleCode -> r)
55fSimpleCode = mapf getSimpleCode F.string
56
57fSimpleParserFunction :: Format r (SimpleCode -> r)
58fSimpleParserFunction = mapf getSimpleCode ("simple" % F.string)
59
60fParserSignature :: Format r a -> Format r a
61fParserSignature f = f % " :: Parser Value"
62
63fParserDeclaration :: Format r a -> Format r a
64fParserDeclaration f = f % " ="
65
66fModuleName :: Format r (ModuleName -> r)
67fModuleName = mapf getModuleName string
68
69fPosition :: Format r (Position -> r)
70fPosition = mapf getPosition F.string
71
72fPresence :: Format r (Presence -> r)
73fPresence =
74 let f Mandatory = "mandatory"
75 f Optional = "optional "
76 in mapf f F.string
77
78quoted :: Format r a -> Format r a
79quoted f = "\"" % f % "\""
80
81simpleQuoted :: Format r a -> Format r a
82simpleQuoted f = "'" % f % "'"
83
84parens :: Format r a -> Format r a
85parens f = "(" % f % ")"
86
87notYetImplemented :: Format r a -> Format r a
88notYetImplemented desc = "notYetImplemented " % quoted (desc % " not yet implemented")
diff --git a/scaffolder/src/Text/Edifact/Scaffolder/Commons/Language.hs b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Language.hs
new file mode 100644
index 0000000..214ee43
--- /dev/null
+++ b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Language.hs
@@ -0,0 +1,286 @@
1{-# LANGUAGE DerivingStrategies #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE OverloadedLists #-}
4{-# LANGUAGE OverloadedStrings #-}
5
6module Text.Edifact.Scaffolder.Commons.Language
7 ( -- *
8 saveHaskellModule
9 , includeSpecification
10 -- *
11 , scaffoldModule
12 -- *
13 , getRootModuleName
14 , getRootModuleNameFor
15 -- *
16 , scaffoldElements
17 , ElementWithDefinition
18 -- *
19 , parentModule
20 -- *
21 , haddockDependencies
22 -- *
23 , reexportDependencies
24 -- *
25 , importDependencies
26 , importCombinators
27 , importNotYetImplementedHelper
28 -- *
29 , moduleDeclaration
30 , Export(..)
31 -- *
32 , reexportAlias
33 , singleImport
34 , ImportGroup(..)
35 , Import(..)
36 , ImportName(..)
37 , ModuleAlias(..)
38 , LanguageExtension(..)
39 ) where
40
41import Text.Edifact.Scaffolder.Commons.Formatters (fModuleName,
42 parens,
43 simpleQuoted)
44import Text.Edifact.Scaffolder.Commons.Logging (say)
45import Text.Edifact.Scaffolder.Commons.Text (commaSeparated,
46 extensions,
47 formatSpecification,
48 indent, newline)
49import Text.Edifact.Scaffolder.Commons.Types (LanguageExtension (..),
50 ModuleName (..),
51 Scaffolding,
52 getRevision,
53 hostModule,
54 revision,
55 targetDirectory,
56 (<.>))
57
58import Control.Monad ((>=>))
59import Control.Monad.IO.Class (liftIO)
60import Control.Monad.Reader (asks)
61import Data.Bifunctor (bimap)
62import Data.Foldable (traverse_)
63import Data.List (intercalate,
64 uncons)
65import Data.List.NonEmpty (NonEmpty, nonEmpty)
66import qualified Data.List.NonEmpty as NE (cons, toList)
67import Data.List.Split (splitOn)
68import Data.String (IsString (..))
69import Data.Text as T (Text, drop,
70 lines,
71 unlines)
72import qualified Data.Text.IO as TIO (readFile,
73 writeFile)
74import Data.Tuple (swap)
75import Formatting as F (Format,
76 bprint, later,
77 mapf, sformat,
78 stext, (%))
79import System.Directory (createDirectoryIfMissing)
80import System.FilePath ((</>))
81import System.Process (callCommand)
82
83getRootModuleName :: Scaffolding ModuleName
84getRootModuleName =
85 let prefix host rev = host <.> ModuleName (getRevision rev)
86 in asks (prefix . hostModule) <*> asks revision
87
88getRootModuleNameFor :: ModuleName -> Scaffolding ModuleName
89getRootModuleNameFor name =
90 let suffix root = root <.> name
91 in suffix <$> getRootModuleName
92
93saveHaskellModule :: ModuleName -> [Text] -> Scaffolding ()
94saveHaskellModule mn body =
95 let sources = T.unlines body
96 saveModule file = liftIO (saveFile file >> stylishHaskell file)
97 saveFile = flip TIO.writeFile sources
98 stylishHaskell file = callCommand ("stylish-haskell -i " <> file)
99 doNothing = pure ()
100 in say ("module " % fModuleName) mn >> mkSourceFile mn >>= maybe doNothing saveModule
101
102mkSourceFile :: ModuleName -> Scaffolding (Maybe FilePath)
103mkSourceFile = locateSourceFile >=> traverse prepareHierarchy
104
105type FileInDirectory = (Directory, FileName)
106type Directory = FilePath
107type FileName = FilePath
108
109prepareHierarchy :: FileInDirectory -> Scaffolding FilePath
110prepareHierarchy (directory, file) =
111 let fullPath = directory </> file
112 in fullPath <$ liftIO (createDirectoryIfMissing True directory)
113
114locateSourceFile :: ModuleName -> Scaffolding (Maybe FileInDirectory)
115locateSourceFile (ModuleName mn) =
116 let hierarchy = splitOn "." mn
117 toFile n = n <> ".hs"
118 path :: Directory -> Maybe (Directory, FileName)
119 path directory = fmap toFile . swap . fmap (foldl (</>) directory . reverse) <$> uncons (reverse hierarchy)
120 in asks (path . targetDirectory)
121
122includeSpecification :: FilePath -> Scaffolding [Text]
123includeSpecification = fmap (formatSpecification . T.lines) . liftIO . TIO.readFile
124
125type ElementWithDefinition elt = (FilePath, elt)
126
127scaffoldElements :: (NonEmpty (ElementWithDefinition element) -> Scaffolding ())
128 -> ( ElementWithDefinition element -> Scaffolding ())
129 -> ([ ElementWithDefinition element] -> Scaffolding ())
130scaffoldElements parentScaffolder elementScaffolder =
131 let doNothing = pure ()
132 scaffolder elts = parentScaffolder elts >> traverse_ elementScaffolder elts
133 in maybe doNothing scaffolder . nonEmpty
134
135parentModule :: ModuleName -> ModuleAlias -> (ModuleName -> element -> ModuleName) -> NonEmpty (ElementWithDefinition element) -> Scaffolding ()
136parentModule elementName alias nameModule elements =
137 getRootModuleNameFor elementName >>= generateRootModule alias nameModule (snd <$> elements)
138
139generateRootModule :: ModuleAlias -> (ModuleName -> element -> ModuleName) -> NonEmpty element -> ModuleName -> Scaffolding ()
140generateRootModule alias nameModule codes mn =
141 let importElement code = ImportAll (ImportAs (nameModule mn code) alias)
142 imports = [ ImportGroup (importElement <$> codes) ]
143 exports = [ reexportAlias alias ]
144 in saveHaskellModule mn $
145 moduleDeclaration mn exports imports
146
147haddockDependencies :: Format Text (elt -> Text) -> NonEmpty elt -> Scaffolding [Text]
148haddockDependencies formatter elts =
149 let formattedDependencies = commaSeparated . fmap (sformat (simpleQuoted formatter))
150 formatHaddock = sformat ("-- Dependencies: " % F.stext % ".")
151 in pure [ "--"
152 , formatHaddock (formattedDependencies elts)
153 ]
154
155reexportDependencies :: Format Text (elt -> Text) -> NonEmpty elt -> Scaffolding [Export]
156reexportDependencies formatter =
157 let mkReexport = Name . sformat formatter
158 prependTitle = NE.cons (Comment "* Dependencies")
159 in pure . NE.toList . prependTitle . fmap mkReexport
160
161importDependencies :: ModuleName -> Format Text (elt -> Text) -> NonEmpty elt -> Scaffolding Import
162importDependencies moduleName formatter elts =
163 let imports = NE.toList (sformat formatter <$> elts)
164 mkImport mn = Import (BasicImport mn) imports
165 in mkImport <$> getRootModuleNameFor moduleName
166
167importCombinators :: ImportGroup
168importCombinators =
169 ImportGroup
170 [ ImportAll "Text.Edifact.Parsing"
171 , Import "Text.Edifact.Types" [ "Value" ]
172 ]
173
174importNotYetImplementedHelper :: ImportGroup
175importNotYetImplementedHelper =
176 ImportGroup
177 [ Import "Text.Edifact.Parsing.Commons" [ "notYetImplemented" ]
178 ]
179
180moduleDeclaration :: ModuleName -> [Export] -> [ImportGroup] -> [Text]
181moduleDeclaration moduleName exports imports =
182 let decl mn [] = [sformat ("module " % fModuleName % " where") mn]
183 decl mn ex = sformat ("module " % fModuleName) mn
184 : renderExports ex
185 in intercalate newline [ decl moduleName exports
186 , renderImports imports
187 ]
188
189machineGeneratedWarning :: [Text]
190machineGeneratedWarning =
191 [ "---- Machine generated code."
192 , "---- Output of edi-parser-scaffolder"
193 ]
194
195scaffoldModule :: ModuleName -> [LanguageExtension] -> [Export] -> [ImportGroup] -> [Text] -> Scaffolding ()
196scaffoldModule mn exts exports imports code =
197 saveHaskellModule mn $
198 intercalate newline
199 [ extensions exts
200 , machineGeneratedWarning
201 , moduleDeclaration mn exports imports
202 , code
203 ]
204
205renderExports :: [Export] -> [Text]
206renderExports exports =
207 let formatExport (First e) = sformat (" " % fExport) e
208 formatExport (Following e) = sformat (", " % fExport) e
209 formatExport (Skipped e) = sformat (" " % fExport) e
210 fExport =
211 let f (Comment t) = bprint ("-- " % stext) t
212 f (Name t) = bprint stext t
213 in later f
214 parensOnFirstLine [] = []
215 parensOnFirstLine (firstLine : rest) = ("(" <> T.drop 1 firstLine) : rest
216 ls = parensOnFirstLine (formatExport <$> tag exports) <> [ ") where" ]
217 in indent <$> ls
218
219data Export = Name Text
220 | Comment Text
221
222instance IsString Export where
223 fromString = Name . fromString
224
225data Tag a = First a
226 | Following a
227 | Skipped a
228
229tag :: [Export] -> [Tag Export]
230tag =
231 let skipAll = fmap Skipped
232 tagFirst [] = []
233 tagFirst (elt : others) = First elt : tagOthers others
234 tagOthers = fmap tagOther
235 tagOther v | isComment v = Skipped v
236 | otherwise = Following v
237 merge (xs, ys) = xs <> ys
238 in merge . bimap skipAll tagFirst . span isComment
239
240isComment :: Export -> Bool
241isComment (Comment _) = True
242isComment _ = False
243
244newtype ModuleAlias = ModuleAlias { getModuleAlias :: Text } deriving newtype (IsString)
245
246singleImport :: Import -> ImportGroup
247singleImport = ImportGroup . pure
248
249newtype ImportGroup = ImportGroup (NonEmpty Import) deriving newtype Semigroup
250
251data Import = Import ImportName [Text]
252 | ImportAll ImportName
253
254data ImportName = BasicImport ModuleName
255 | ImportAs ModuleName ModuleAlias
256 | ImportQualified ModuleName
257 | ImportQualifiedAs ModuleName ModuleAlias
258
259instance IsString ImportName where
260 fromString = BasicImport . fromString
261
262renderImports :: [ImportGroup] -> [Text]
263renderImports = intercalate newline . fmap renderImportGroup
264
265reexportAlias :: ModuleAlias -> Export
266reexportAlias = Name . sformat ("module " % fModuleAlias)
267
268renderImportGroup :: ImportGroup -> [Text]
269renderImportGroup (ImportGroup imports) = NE.toList (renderImport <$> imports)
270
271renderImport :: Import -> Text
272renderImport (ImportAll name) = sformat fImportName name
273renderImport (Import name references) =
274 sformat (fImportName % " " % parens stext) name (commaSeparated references)
275
276fImportName :: Format r (ImportName -> r)
277fImportName =
278 let
279 build (BasicImport name) = bprint ("import " % fModuleName) name
280 build (ImportAs name alias) = bprint ("import " % fModuleName % " as " % fModuleAlias) name alias
281 build (ImportQualified name) = bprint ("import qualified " % fModuleName) name
282 build (ImportQualifiedAs name alias) = bprint ("import qualified " % fModuleName % " as " % fModuleAlias) name alias
283 in later build
284
285fModuleAlias :: Format r (ModuleAlias -> r)
286fModuleAlias = mapf getModuleAlias stext
diff --git a/scaffolder/src/Text/Edifact/Scaffolder/Commons/Logging.hs b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Logging.hs
new file mode 100644
index 0000000..1287f7f
--- /dev/null
+++ b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Logging.hs
@@ -0,0 +1,11 @@
1module Text.Edifact.Scaffolder.Commons.Logging
2 ( say
3 ) where
4
5import Control.Monad.IO.Class (MonadIO, liftIO)
6import qualified Data.Text.Lazy.Builder as TLB (toLazyText)
7import qualified Data.Text.Lazy.IO as TLIO (putStrLn)
8import Formatting as F (Format, runFormat)
9
10say :: MonadIO m => Format (m ()) a -> a
11say m = runFormat m (liftIO . TLIO.putStrLn . TLB.toLazyText)
diff --git a/scaffolder/src/Text/Edifact/Scaffolder/Commons/Parsing.hs b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Parsing.hs
new file mode 100644
index 0000000..24f8f80
--- /dev/null
+++ b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Parsing.hs
@@ -0,0 +1,122 @@
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE TupleSections #-}
3
4module Text.Edifact.Scaffolder.Commons.Parsing
5 ( -- *
6 maybeParse
7 , skipBeginning
8 , single
9 , silent
10 -- *
11 , listElements
12 -- *
13 , presenceParser
14 , stringToPresenceParser
15 -- *
16 , messageCodeParser
17 -- *
18 , scanDependencies
19 , scan
20 , scanUntil
21 ) where
22
23import Text.Edifact.Scaffolder.Commons.Logging (say)
24import Text.Edifact.Scaffolder.Commons.Types
25
26import Control.Monad.IO.Class (liftIO)
27import Control.Monad.Identity (Identity)
28import Control.Monad.Reader (asks, local)
29import Data.Bifunctor (first)
30import Data.List (sort)
31import Data.List.NonEmpty (NonEmpty, nonEmpty)
32import Data.Maybe (catMaybes)
33import Data.String (fromString)
34import Data.Text (Text)
35import Formatting as F (shown)
36import System.Directory (listDirectory)
37import System.FilePath ((</>))
38import Text.Parsec (Parsec, SourceName,
39 Stream, anyChar, char,
40 choice, count,
41 endOfLine, eof,
42 lookAhead, many,
43 many1, manyTill,
44 oneOf, optionMaybe,
45 runParser, string,
46 try, upper, (<?>))
47
48maybeParse :: (Show a, Stream s Identity t, Monoid u) => SourceName -> Parsec s u a -> s -> Scaffolding (Maybe a)
49maybeParse source parser input =
50 let interpretParsingResult (Right v) _ = pure (Just v)
51 interpretParsingResult e True = Nothing <$ say shown e
52 interpretParsingResult _ False = pure Nothing
53 shouldDebug = asks debugParsing
54 in shouldDebug >>= interpretParsingResult (runParser parser mempty source input)
55
56-- | Disable parsing error logging locally
57silent :: Scaffolding a -> Scaffolding a
58silent = local disableDebugging
59
60-- | Let you traverse a directory and filter files matching a parser.
61-- The filename is then paired with the matched value
62listElements :: (Show elt, Ord elt) => FilePath -> Parsec String () elt -> Scaffolding [(FilePath, elt)]
63listElements subpath parser = do
64 home <- getSpecificationHome
65 let directory = home </> subpath
66 files <- sort <$> liftIO (listDirectory directory)
67 let prependDirectory f = directory </> f
68 fmap (first prependDirectory) . catMaybes <$> traverse (extractElement parser) files
69
70getSpecificationHome :: Scaffolding FilePath
71getSpecificationHome =
72 let concatenate path (Revision rev) = path </> rev
73 in asks (concatenate . specificationsHome) <*> asks revision
74
75extractElement :: Show elt => Parsec String () elt -> FilePath -> Scaffolding (Maybe (FilePath, elt))
76extractElement parser path = silent (fmap (path,) <$> maybeParse path parser path)
77
78skipBeginning :: Stream s Identity Char => Parsec s () a -> Parsec s () a
79skipBeginning p = manyTill anyChar (try (lookAhead p)) *> p
80
81single :: Stream s Identity Char => Parsec s u a -> Parsec s u [a]
82single = count 1
83
84presenceParser :: Stream s Identity Char => Parsec s u Presence
85presenceParser =
86 choice [ Mandatory <$ char 'M'
87 , Optional <$ char 'C'
88 ] <?> "Presence"
89
90stringToPresenceParser :: Stream s Identity Char => Parsec s u Text
91stringToPresenceParser = fromString <$>
92 manyTill anyChar (try $ lookAhead $ many1 (string " ") >> presenceParser >> string " " >> many (oneOf " 0123456789"))
93 <?> "Description"
94
95messageCodeParser :: Stream s Identity Char => Parsec s u MessageCode
96messageCodeParser = fromString <$> count 6 upper
97
98scanDependencies :: (Monoid u, Show result) => FilePath -> Parsec String u [result] -> Scaffolding (Maybe (NonEmpty result))
99scanDependencies file parser =
100 let readLines = liftIO (readFile file)
101 in readLines >>= fmap (nonEmpty =<<) . maybeParse file parser
102
103scan :: Stream s Identity Char => [Parsec s u [a]] -> Parsec s u [a]
104scan scanners =
105 let parsers = (scanLine <$> scanners) <> [skipLine]
106 end = choice [ () <$ try endOfLine
107 , () <$ eof
108 ]
109 scanLine p = optionMaybe (try p) <* end
110 skipLine = Nothing <$ manyTill anyChar end
111 in concat . catMaybes <$> manyTill (choice parsers) eof
112
113scanUntil :: Stream s Identity Char => [Parsec s u a] -> Parsec s u a
114scanUntil scanners =
115 let parsers = scanLine <$> scanners
116 end = choice [ () <$ try endOfLine
117 , () <$ eof
118 ]
119 searching = choice $ fmap (() <$) parsers <> [ () <$ eof ]
120 scanLine p = p <* end
121 skipLine = manyTill anyChar end
122 in manyTill skipLine (try $ lookAhead searching) >> try (choice parsers)
diff --git a/scaffolder/src/Text/Edifact/Scaffolder/Commons/Text.hs b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Text.hs
new file mode 100644
index 0000000..ef4e805
--- /dev/null
+++ b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Text.hs
@@ -0,0 +1,91 @@
1{-# LANGUAGE DerivingStrategies #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE OverloadedStrings #-}
4
5module Text.Edifact.Scaffolder.Commons.Text
6 ( -- *
7 indent
8 , quote
9 , haskellList
10 , commaSeparated
11 -- *
12 , newline
13 -- *
14 , formatSpecification
15 -- *
16 , extensions
17 ) where
18
19
20import Text.Edifact.Scaffolder.Commons.Types (LanguageExtension (..))
21
22import Control.Category ((>>>))
23import Data.Char (isSpace)
24import Data.List (dropWhileEnd)
25import Data.String (IsString)
26import Data.Text (Text)
27import qualified Data.Text as T (all, dropWhileEnd,
28 null)
29import Formatting as F (mapf, sformat,
30 stext, string, (%))
31
32formatSpecification :: [Text] -> [Text]
33formatSpecification = cleanEmptyLines
34 >>> fmap quoteLine
35 >>> prependQuote
36
37prependQuote :: [Text] -> [Text]
38prependQuote ls =
39 [ "-- | Derived from this specification:"
40 , "--"
41 ] <> ls
42
43cleanEmptyLines :: [Text] -> [Text]
44cleanEmptyLines = dropWhile blank >>> dropWhileEnd blank
45
46blank :: Text -> Bool
47blank t = T.null t || T.all isSpace t
48
49quoteLine :: Text -> Text
50quoteLine = haskellQuote >>> cleanWhitespaces
51
52haskellQuote :: Text -> Text
53haskellQuote line = "-- > " <> line
54
55cleanWhitespaces :: Text -> Text
56cleanWhitespaces = T.dropWhileEnd (== ' ')
57
58indent :: Text -> Text
59indent t = " " <> t
60
61quote :: Text -> Text
62quote t = "'" <> t <> "'"
63
64haskellList :: [Text] -> [Text]
65haskellList =
66 let prefix :: Int -> Text -> Text
67 prefix 1 dep = sformat ("[ " % F.stext) dep
68 prefix _ dep = sformat (", " % F.stext) dep
69 suffix deps = deps <> ["]"]
70 in suffix . zipWith prefix [1..]
71
72newtype CommaSeparated = CommaSeparated { getCommaSeparated :: Text } deriving newtype (IsString, Eq)
73
74instance Semigroup CommaSeparated where
75 t1 <> "" = t1
76 "" <> t2 = t2
77 t1 <> t2 = CommaSeparated (getCommaSeparated t1 <> ", " <> getCommaSeparated t2)
78
79instance Monoid CommaSeparated where
80 mempty = ""
81
82commaSeparated :: Foldable f => f Text -> Text
83commaSeparated = getCommaSeparated . foldMap CommaSeparated
84
85newline :: [Text]
86newline = [""]
87
88extensions :: [LanguageExtension] -> [Text]
89extensions =
90 let fExtension = "{-# LANGUAGE " % mapf getLanguageExtension F.string % " #-}"
91 in fmap (sformat fExtension)
diff --git a/scaffolder/src/Text/Edifact/Scaffolder/Commons/Types.hs b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Types.hs
new file mode 100644
index 0000000..4d1c0a6
--- /dev/null
+++ b/scaffolder/src/Text/Edifact/Scaffolder/Commons/Types.hs
@@ -0,0 +1,72 @@
1{-# LANGUAGE DerivingStrategies #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3
4module Text.Edifact.Scaffolder.Commons.Types
5 ( -- * Codes of elements
6 MessageCode(..)
7 , GroupCode(..)
8 , SegmentCode(..)
9 , SegmentName(..)
10 , CompositeCode (..)
11 , CompositeName (..)
12 , SimpleCode(..)
13 , SimpleName(..)
14 -- * Ordering of elements
15 , Position(..)
16 -- * Attributes
17 , Presence(..)
18 -- *
19 , ModuleName(..)
20 , (<.>)
21 -- *
22 , LanguageExtension(..)
23 -- *
24 , Scaffolding
25 , Revision(..)
26 , ScaffoldingEnv(..)
27 , disableDebugging
28 ) where
29
30import Control.Monad.Reader (ReaderT)
31import Data.String (IsString)
32
33newtype MessageCode = MessageCode { getMessageCode :: String } deriving newtype (Show, Eq, Ord, IsString)
34newtype GroupCode = GroupCode { getGroupCode :: String } deriving newtype (Show, Eq, Ord, IsString)
35newtype SegmentCode = SegmentCode { getSegmentCode :: String } deriving newtype (Show, Eq, Ord, IsString)
36newtype SegmentName = SegmentName { getSegmentName :: String } deriving newtype (Show, Eq, Ord, IsString)
37newtype CompositeCode = CompositeCode { getCompositeCode :: String } deriving newtype (Show, Eq, Ord, IsString)
38newtype CompositeName = CompositeName { getCompositeName :: String } deriving newtype (Show, Eq, Ord, IsString)
39newtype SimpleCode = SimpleCode { getSimpleCode :: String } deriving newtype (Show, Eq, Ord, IsString)
40newtype SimpleName = SimpleName { getSimpleName :: String } deriving newtype (Show, Eq, Ord, IsString)
41
42newtype Position = Position { getPosition :: String } deriving newtype (Show, Eq, Ord, IsString)
43
44data Presence = Mandatory
45 | Optional
46 deriving (Show, Eq, Ord)
47
48newtype ModuleName = ModuleName { getModuleName :: String } deriving newtype (Show, Eq, IsString)
49
50instance Semigroup ModuleName where
51 (<>) = (<.>)
52
53(<.>) :: ModuleName -> ModuleName -> ModuleName
54(ModuleName parent) <.> (ModuleName child) = ModuleName (parent <> "." <> child)
55
56newtype LanguageExtension = LanguageExtension { getLanguageExtension :: String } deriving newtype IsString
57
58type Scaffolding = ReaderT ScaffoldingEnv IO
59
60newtype Revision = Revision { getRevision :: String } deriving newtype (Show, Eq, IsString)
61
62data ScaffoldingEnv =
63 ScaffoldingEnv
64 { revision :: Revision
65 , hostModule :: ModuleName
66 , specificationsHome :: FilePath
67 , targetDirectory :: FilePath
68 , debugParsing :: Bool
69 }
70
71disableDebugging :: ScaffoldingEnv -> ScaffoldingEnv
72disableDebugging env = env { debugParsing = False }