1 {-# LANGUAGE DataKinds #-}
2 {-# LANGUAGE DuplicateRecordFields #-}
3 {-# LANGUAGE OverloadedLists #-}
4 {-# LANGUAGE TypeApplications #-}
5 {-# LANGUAGE TypeFamilies #-}
6 {-# LANGUAGE UndecidableInstances #-}
7 {-# OPTIONS_GHC -fno-warn-orphans #-}
10 import Control.Monad (Monad(..), forM_, when)
12 import Data.Either (Either(..))
13 import Data.Eq (Eq(..))
14 import Data.Foldable (Foldable(..))
15 import Data.Function (($), (.))
16 import Data.Functor ((<$>))
18 import Data.Maybe (Maybe(..), fromMaybe)
19 import Data.Monoid (Monoid(..))
20 import Data.Ord (Ord(..))
21 import Data.Semigroup (Semigroup(..))
23 import Data.String (String)
24 import Data.Tuple (fst)
25 import GHC.Exts (IsList(..))
26 import Options.Applicative as Opt
27 import Prelude (error)
28 import System.FilePath as FilePath
29 import System.IO (IO, FilePath)
30 import Text.Show (Show(..))
31 import qualified Data.ByteString as BS
32 import qualified Data.ByteString.Lazy as BSL
33 import qualified Data.Char as Char
34 import qualified Data.List as List
35 import qualified Data.Map.Strict as Map
36 import qualified Data.Set as Set
37 import qualified Data.Text as Text
38 import qualified Data.Text.IO as Text
39 import qualified Data.Text.Lazy as TL
40 import qualified Data.Text.Lazy.Encoding as TL
41 import qualified Data.TreeSeq.Strict as Tree
42 import qualified System.Environment as Env
43 import qualified System.IO as IO
44 import qualified Text.Blaze.Renderer.Utf8 as Blaze
45 import qualified Text.Blaze.Utils as Blaze
46 import qualified Text.Megaparsec as P
49 import qualified Language.TCT as TCT
50 import qualified Language.TCT.Write.HTML5 as TCT.Write.HTML5
51 import qualified Language.TCT.Write.Plain as TCT.Write.Plain
52 import qualified Language.TCT.Write.XML as TCT.Write.XML
55 import qualified Language.DTC.Read.TCT as DTC.Read.TCT
56 import qualified Language.DTC.Sym as DTC
57 import qualified Language.DTC.Write.HTML5 as DTC.Write.HTML5
58 import qualified Language.DTC.Write.XML as DTC.Write.XML
59 import qualified Language.RNC.Write as RNC
60 import qualified Text.Blaze.DTC as Blaze.DTC
61 import qualified Text.Blaze.HTML5 as Blaze.HTML5
63 type Langs = '[FR, EN]
64 type Lang = LocaleIn Langs
69 (\v -> Map.findWithDefault
70 (LocaleIn @Langs en_US)
71 (Text.pack $ List.takeWhile (\c -> Char.isAlphaNum c || c == '_') v)
74 <$> Env.lookupEnv "LANG"
75 cmd <- execParser $ pArgv lang
79 info (pCommand lang <**> helper) $ mconcat
81 , progDesc "document tool"
82 , header "hdoc - command line tool for TCT and DTC technical documents"
85 mainWithCommand :: Command -> IO ()
86 mainWithCommand (CommandTCT ArgsTCT{..}) = do
87 TCT.readFile input >>= \case
88 Left err -> IO.hPrint IO.stderr err
90 case TCT.readTCTWithoutIncludes input txt of
91 Left err -> error $ show err
93 when (DumpTCT_TCT`elem`dump) $
94 writeFile (output`FilePath.replaceExtension`".tct.dump") $
95 TL.pack $ Tree.prettyTrees tct
96 when (DumpTCT_XML`elem`dump) $
97 let xml = TCT.Write.XML.document tct in
98 writeFile (output`FilePath.replaceExtension`".xml.dump") $
99 TL.pack $ Tree.prettyTrees xml
103 TCT.Write.Plain.document tct
105 withFile output IO.WriteMode $ \h ->
106 Blaze.renderMarkupToByteStringIO (BS.hPutStr h) $
107 TCT.Write.HTML5.document tct
108 mainWithCommand (CommandDTC ArgsDTC{..}) =
109 TCT.readTCT input >>= \case
110 Left err -> error $ show err
112 when (DumpDTC_TCT`elem`dump) $ do
113 writeFile (input`FilePath.replaceExtension`".tct.dump") $
114 TL.pack $ Tree.prettyTrees tct
115 let xml = TCT.Write.XML.document tct
116 when (DumpDTC_XML`elem`dump) $ do
117 writeFile (input`FilePath.replaceExtension`".xml.dump") $
118 TL.pack $ Tree.prettyTrees xml
119 case DTC.Read.TCT.readDTC xml of
120 Left err -> error $ P.parseErrorPretty err
122 when (DumpDTC_DTC`elem`dump) $ do
123 writeFile (input`FilePath.replaceExtension`".dtc.dump") $
127 withFile output IO.WriteMode $ \h ->
128 Blaze.prettyMarkupIO Blaze.DTC.isInlinedElement (BS.hPutStr h) $
129 DTC.Write.XML.document locale dtc
131 withFile output IO.WriteMode $ \h ->
132 Blaze.prettyMarkupIO Blaze.HTML5.isInlinedElement (BS.hPutStr h) $
133 DTC.Write.HTML5.document locale dtc
134 mainWithCommand (CommandRNC ArgsRNC{}) =
135 forM_ DTC.schema $ \rule ->
136 Text.hPutStrLn IO.stdout $ RNC.renderWriter rule
138 -- * Filesystem utilities
139 writeFile :: FilePath -> TL.Text -> IO ()
140 writeFile fp s = BSL.writeFile fp $ TL.encodeUtf8 s
142 withFile :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a
143 withFile = IO.withFile
145 -- * Options utilities
146 instance IsList (Opt.Mod f a) where
147 type Item (Opt.Mod f a) = Opt.Mod f a
151 readList :: [(String, a)] -> ReadM a
154 case s`List.lookup`m of
157 "cannot parse value \"" <> s
158 <> "\"\nexpecting one of: "
159 <> List.intercalate ", " (fst <$> m)
167 pCommand :: Lang -> Parser Command
172 info (CommandTCT <$> pArgsTCT) $
173 progDesc "TCT (Texte Convivial Technique) rendition."
178 info (CommandDTC <$> pArgsDTC lang) $
179 progDesc "DTC (Document Technique Convivial) rendition."
184 info (CommandRNC <$> pArgsRNC) $
185 progDesc "RNC (RelaxNG Compact) schema."
188 pDump :: Ord a => [(String, a)] -> Parser (Set a)
193 (Set.singleton <$> readList formats)
195 , help $ "Dump an intermediate format. (choices: "
196 <> List.intercalate ", " (fst <$> formats) <> ")"
204 , format :: FormatTCT
205 , dump :: Set DumpTCT
208 pArgsTCT :: Parser ArgsTCT
212 <$> argument str (metavar "FILE")
213 <*> strOption [ long "output"
216 , help "write output to FILE"
219 <*> pDump [ ("tct", DumpTCT_TCT)
220 , ("xml", DumpTCT_XML) ]
222 setDefault a@ArgsTCT{..}
223 | null output = (a::ArgsTCT){output=input`FilePath.replaceExtension`ext format}
226 FormatTCT_Plain -> ".txt"
227 FormatTCT_HTML5 -> ".html"
229 -- *** Type 'FormatTCT'
234 pFormatTCT :: Parser FormatTCT
236 flag FormatTCT_Plain FormatTCT_Plain
238 , help "Render as plain text."
240 flag FormatTCT_HTML5 FormatTCT_HTML5
242 , help "Render as HTML5."
245 -- *** Type 'DumpTCT'
249 deriving (Eq, Ord, Show)
256 , format :: FormatDTC
258 , dump :: Set DumpDTC
260 pArgsDTC :: Lang -> Parser ArgsDTC
264 <$> argument str (metavar "FILE")
265 <*> strOption [ long "output"
268 , help "write output to FILE"
272 <*> pDump [ ("tct", DumpDTC_TCT)
273 , ("xml", DumpDTC_XML)
274 , ("dtc", DumpDTC_DTC) ]
276 setDefault a@ArgsDTC{..}
277 | null output = (a::ArgsDTC){output=input`FilePath.replaceExtension`fmt format}
280 FormatDTC_XML -> ".xml"
281 FormatDTC_HTML5 -> ".html"
283 pLocale :: Lang -> Parser (LocaleIn Langs)
286 (maybeReader $ \s -> Map.lookup (Text.pack s) $ locales @Langs)
294 -- *** Type 'FormatDTC'
299 pFormatDTC :: Parser FormatDTC
301 flag FormatDTC_HTML5 FormatDTC_HTML5
303 , help "Render as HTML5."
305 flag FormatDTC_HTML5 FormatDTC_XML
307 , help "Render as XML."
310 -- *** Type 'DumpDTC'
315 deriving (Eq, Ord, Show)
321 pArgsRNC :: Parser ArgsRNC
322 pArgsRNC = pure ArgsRNC