1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DuplicateRecordFields #-}
3 {-# LANGUAGE ExistentialQuantification #-}
4 {-# LANGUAGE FlexibleContexts #-}
5 {-# LANGUAGE OverloadedLists #-}
6 {-# LANGUAGE OverloadedStrings #-}
7 {-# LANGUAGE TupleSections #-}
8 {-# LANGUAGE TypeApplications #-}
9 {-# LANGUAGE TypeFamilies #-}
10 {-# OPTIONS_GHC -fno-warn-orphans #-}
13 -- import Data.Reflection (reify, Reifies(..))
14 -- import qualified Data.Text.IO as Text
15 import Control.Applicative (pure)
16 import Control.Monad (Monad(..), forM_, when)
18 import Data.Default.Class (Default(..))
19 import Data.Either (Either(..))
20 import Data.Eq (Eq(..))
21 import Data.Foldable (Foldable(..))
22 import Data.Function (($), (.))
23 import Data.Functor ((<$>))
25 import Data.Maybe (Maybe(..), fromMaybe, maybe)
26 import Data.Monoid (Monoid(..))
27 import Data.Ord (Ord(..))
28 import Data.Semigroup (Semigroup(..))
29 import Data.String (String, IsString(..))
30 import Data.Void (Void)
31 import GHC.Exts (IsList(..))
32 import Prelude (error)
33 import System.FilePath as FilePath
34 import System.IO (IO, FilePath)
35 import Text.Show (Show(..))
36 import qualified Data.ByteString as BS
37 import qualified Data.ByteString.Lazy as BSL
38 import qualified Data.Char as Char
39 import qualified Data.List as List
40 import qualified Data.Map.Strict as Map
41 import qualified Data.Set as Set
42 import qualified Data.Text as Text
43 import qualified Data.Text.Lazy as TL
44 import qualified Data.Text.Lazy.Encoding as TL
45 import qualified Data.Text.Lazy.IO as TL
46 import qualified Data.TreeSeq.Strict as Tree
47 import qualified Data.Version as Version
48 import qualified Paths_hdoc as Hdoc
49 import qualified System.Directory as IO
50 import qualified System.Environment as Env
51 import qualified System.IO as IO
52 import qualified System.IO.Error as IO
53 import qualified Text.Blaze.Renderer.Utf8 as Blaze
54 import qualified Text.Blaze.Utils as Blaze
55 import qualified Text.Megaparsec as P
57 import qualified Hdoc.TCT as TCT
58 import qualified Hdoc.TCT.Write.HTML5 as TCT
59 import qualified Hdoc.TCT.Write.Plain as TCT
60 import qualified Hdoc.TCT.Write.XML as TCT
61 import qualified Hdoc.DTC.Read.TCT as DTC
62 import qualified Hdoc.DTC.Sym as DTC
63 import qualified Hdoc.DTC.Write.HTML5 as DTC
64 import qualified Hdoc.DTC.Write.XML as DTC
65 import qualified Hdoc.RNC.Write as RNC
66 import qualified Text.Blaze.DTC as Blaze.DTC
67 import qualified Text.Blaze.HTML5 as Blaze.HTML5
69 import Language.Symantic.CLI hiding (main)
70 import qualified Language.Symantic.CLI as CLI
71 import qualified Language.Symantic.Document.Term.IO as Doc
73 import qualified Language.Symantic.CLI.Plain as Plain
74 import qualified Language.Symantic.CLI.Help as Help
75 import qualified Language.Symantic.CLI.Read as Read
78 version = "hdoc-" <> TL.pack (Version.showVersion Hdoc.version)
84 readArgs lang args >>= \case
86 Just (Left err) -> onExit err
87 Just (Right cmd) -> onCommand cmd
92 Lang -> [String] -> IO.IO (Maybe (Either (Exit (Doc d)) Command))
95 Read.readArgs (cli (loqualize lang) lang) $
96 Read.Args $ Read.Arg <$> ("hdoc":args) of
97 Right a -> return $ Just $ Right a
100 P.FancyError pos es ->
101 case Set.toList es of
102 [P.ErrorCustom (Read.ErrorRead e)] ->
105 IO.hPutStr IO.stderr $
106 P.parseErrorPretty @Read.Arg @(Read.ErrorRead Error) $
107 P.FancyError pos $ Set.singleton $ P.ErrorCustom $
110 _ -> return $ Just $ Left e
112 P.TrivialError pos e es -> do
113 IO.hPutStr IO.stderr $
114 P.parseErrorPretty @_ @Void $
115 P.TrivialError pos e es
122 | Exit_License (Loq d)
125 instance Show (Loqualization q) where
126 show _ = "Loqualization"
128 onExit :: Exit (Doc Doc.TermIO) -> IO ()
129 onExit (Exit_Help d) =
130 Doc.runTermIO IO.stdout $
131 Doc.withBreakable (Just 80) (runDoc d) <>
133 onExit Exit_Version =
135 onExit (Exit_License (Loqualization l)) =
136 Doc.runTermIO IO.stdout $
139 onExit Exit_Error{} =
144 = Error_Locale String
149 = Command_Source CommandSource
150 | Command_Compile CommandCompile
151 | Command_Schema CommandSchema
154 onCommand :: Command -> IO ()
155 onCommand cmd@(Command_Source CommandSource{..}) = do
156 IO.hPrint IO.stderr cmd
157 TCT.readTCT source_input >>= \case
158 Left err -> error $ show err
160 when source_dump_tct $
161 writeFile (source_output-<.>"tct.dump") $
162 TL.pack $ Tree.prettyTrees tct
163 case source_format of
164 CommandSourceFormat_Plain ->
165 writeFile source_output $
167 CommandSourceFormat_HTML5 ->
168 withFile source_output IO.WriteMode $ \h ->
169 Blaze.renderMarkupToByteStringIO (BS.hPutStr h) $
171 onCommand cmd@(Command_Compile CommandCompile{..}) = do
172 IO.hPrint IO.stderr cmd
173 TCT.readTCT compile_input >>= \case
174 Left err -> error $ show err
176 when compile_dump_tct $ do
177 writeFile (compile_output-<.>"tct.dump") $
178 TL.pack $ Tree.prettyTrees tct
179 let xml = TCT.writeXML tct
180 when compile_dump_xml $ do
181 writeFile (compile_output-<.>"xml.dump") $
182 TL.pack $ Tree.prettyTrees xml
183 case DTC.readDTC xml of
185 removeFile $ compile_output-<.>"deps"
186 error $ P.parseErrorPretty err
188 when compile_dump_deps $ do
189 writeFile (compile_output-<.>"deps") $
190 writeDependencies compile_input tct
191 when compile_dump_xml $ do
192 writeFile (compile_output-<.>"dtc.dump") $
194 case compile_format of
195 CommandCompileFormat_XML ->
196 withFile compile_output IO.WriteMode $ \h ->
197 Blaze.prettyMarkupIO Blaze.DTC.isInlinedElement (BS.hPutStr h) $
198 DTC.writeXML compile_locale dtc
199 CommandCompileFormat_HTML5{..} -> do
201 src <- Hdoc.getDataFileName "style/dtc-html5.css"
202 case compile_html5_output_css of
203 Nothing -> Right <$> readFile src
204 Just "" -> return $ Left ""
206 IO.createDirectoryIfMissing True $ FilePath.takeDirectory dst
209 let conf = DTC.Config
211 , DTC.config_locale = compile_locale
212 , DTC.config_generator = version
214 withFile compile_output IO.WriteMode $ \h ->
215 Blaze.prettyMarkupIO Blaze.HTML5.isInlinedElement (BS.hPutStr h) $
216 DTC.writeHTML5 conf dtc
217 onCommand Command_Schema{} =
218 forM_ DTC.schema $ \ru ->
219 TL.hPutStrLn IO.stdout $ RNC.renderWriter ru
221 writeDependencies :: FilePath -> TCT.Roots -> TL.Text
222 writeDependencies input tct =
223 let dir = FilePath.takeDirectory input in
224 TL.pack input <> ":" <>
230 (TCT.dependencies tct) <>
233 -- ** Type 'CommandSource'
236 { source_output :: FilePath
237 , source_dump_tct :: Bool
238 , source_format :: CommandSourceFormat
239 , source_input :: FilePath
243 -- *** Type 'CommandSourceFormat'
244 data CommandSourceFormat
245 = CommandSourceFormat_Plain
246 | CommandSourceFormat_HTML5
248 instance Default CommandSourceFormat where
249 def = CommandSourceFormat_Plain
251 -- *** Type 'CommandSourceDump'
252 data CommandSourceDump
253 = CommandSourceDump_TCT
254 | CommandSourceDump_XML
255 deriving (Eq, Ord, Show)
257 -- ** Type 'CommandCompile'
260 { compile_output :: FilePath
261 , compile_locale :: Lang
262 , compile_dump_tct :: Bool
263 , compile_dump_xml :: Bool
264 , compile_dump_deps :: Bool
265 , compile_format :: CommandCompileFormat
266 , compile_input :: FilePath
267 -- , compile_dump :: Set CommandCompileDump
271 -- *** Type 'CommandCompileFormat'
272 data CommandCompileFormat
273 = CommandCompileFormat_HTML5
274 { compile_html5_output_css :: Maybe FilePath
275 , compile_html5_dump_dtc :: Bool
277 | CommandCompileFormat_XML
281 instance Default CommandCompileFormat where
282 def = CommandCompileFormat_HTML5
283 { compile_html5_output_css = def
284 , compile_html5_dump_dtc = False
287 -- *** Type 'CommandCompileDump'
288 data CommandCompileDump
289 = CommandCompileDump_TCT
290 | CommandCompileDump_XML
291 | CommandCompileDump_DTC
292 | CommandCompileDump_Deps
293 deriving (Eq, Ord, Show)
295 -- ** Type 'CommandSchema'
308 , Sym_Interleaved repr
314 ) => CLI d repr where
315 cli :: Loq d -> Lang -> repr (Exit d) ArgCommand Command
316 cli loq@(Loqualization l) lang =
317 help @d (l10n_cli l) $
318 CLI.main "hdoc" $ opts **> cmds
323 <<$? option_help loq (help_usage $ cli loq lang)
324 <<|?>> option_version loq
325 <<|?>> option_license loq
327 Command_Source <$$> command_source loq <||>
328 Command_Compile <$$> command_compile loq lang <||>
329 Command_Schema <$$> command_schema loq
331 option_help :: Loq d -> d -> ((), repr (Exit d) ArgOption ())
332 option_help (Loqualization l) d =
334 help @d (l10n_help_opt_help l) $
335 opt (OptionName 'h' "help") $
337 option_version :: Loq d -> ((), repr (Exit d) ArgOption ())
338 option_version (Loqualization l) = ((),) $
339 help @d (l10n_help_version l) $
340 long "version" $ exit $ Exit_Version
341 option_license :: Loq d -> ((), repr (Exit d) ArgOption ())
342 option_license loq@(Loqualization l) = ((),) $
343 help @d (l10n_help_license l) $
344 long "license" $ exit $ Exit_License loq
345 option_input :: Loq d -> (FilePath, repr (Exit d) ArgOption FilePath)
346 option_input (Loqualization l) =
348 help @d (l10n_help_opt_input l) $
349 opt (OptionName 'i' "input") $
350 string $ l10n_var_file l
351 option_output :: Loq d -> (FilePath, repr (Exit d) ArgOption FilePath)
352 option_output (Loqualization l) =
354 help @d (l10n_help_opt_output l) $
355 opt (OptionName 'o' "output") $
356 string $ l10n_var_file l
357 option_lang :: Loq d -> Lang -> (Lang, repr (Exit d) ArgOption Lang)
358 option_lang (Loqualization l) lang =
360 help @d (l10n_help_opt_lang l) $
362 var (l10n_var_locale l) $ \s ->
363 maybe (Left $ Exit_Error $ Error_Locale s) Right $
364 Map.lookup (Text.pack s) $
366 option_dump_tct :: Loq d -> (Bool, repr (Exit d) ArgOption Bool)
367 option_dump_tct (Loqualization l) =
368 help @d (l10n_help_opt_dump_tct l) <$>
369 flag (OptionNameLong "dump-tct")
370 option_dump_xml :: Loq d -> (Bool, repr (Exit d) ArgOption Bool)
371 option_dump_xml (Loqualization l) =
372 help @d (l10n_help_opt_dump_xml l) <$>
373 flag (OptionNameLong "dump-xml")
374 option_dump_deps :: Loq d -> (Bool, repr (Exit d) ArgOption Bool)
375 option_dump_deps (Loqualization l) =
376 help @d (l10n_help_opt_dump_deps l) <$>
377 flag (OptionNameLong "dump-deps")
378 option_dump_dtc :: Loq d -> (Bool, repr (Exit d) ArgOption Bool)
379 option_dump_dtc (Loqualization l) =
380 help @d (l10n_help_opt_dump_dtc l) <$>
381 flag (OptionNameLong "dump-dtc")
383 command_source :: Loq d -> repr (Exit d) ArgCommand CommandSource
384 command_source loq@(Loqualization l) =
385 help @d (l10n_help_command_source l) $
389 <<$? option_help loq (help_usage $ command_source loq)
390 <<|?>> option_output loq
391 <<|?>> option_dump_tct loq)
392 <**> (command_source_plain loq
393 <||> command_source_html5 loq)
394 <**> string (l10n_var_file l)
395 command_source_plain :: Loq d -> repr (Exit d) ArgCommand CommandSourceFormat
396 command_source_plain loq@(Loqualization l) =
397 help @d (l10n_help_format_plain l) $
400 CommandSourceFormat_Plain
401 <<$? option_help loq (help_usage $ command_source_plain loq)
402 command_source_html5 :: Loq d -> repr (Exit d) ArgCommand CommandSourceFormat
403 command_source_html5 loq@(Loqualization l) =
404 help @d (l10n_help_format_html5 l) $
407 CommandSourceFormat_HTML5
408 <<$? option_help loq (help_usage $ command_source_html5 loq)
410 command_compile :: Loq d -> Lang -> repr (Exit d) ArgCommand CommandCompile
411 command_compile loq@(Loqualization l) lang =
412 help @d (l10n_help_command_compile l) $
417 <<$? option_help loq (help_usage $ command_compile loq lang)
418 <<|?>> option_output loq
419 <<|?>> option_lang loq lang
420 <<|?>> option_dump_tct loq
421 <<|?>> option_dump_xml loq
422 <<|?>> option_dump_deps loq)
423 <**> (command_compile_html5 loq
424 <||> command_compile_xml loq)
425 <**> string (l10n_var_file l)
427 setDefault a@CommandCompile{..}
428 | null compile_output = (a::CommandCompile){compile_output=compile_input-<.>fmt compile_format}
431 CommandCompileFormat_XML{} -> "xml"
432 CommandCompileFormat_HTML5{} -> "html"
433 command_compile_html5 :: Loq d -> repr (Exit d) ArgCommand CommandCompileFormat
434 command_compile_html5 loq@(Loqualization l) =
435 help @d (l10n_help_format_html5 l) $
438 CommandCompileFormat_HTML5
439 <<$? option_help loq (help_usage $ command_compile_html5 loq)
440 <<|?>> option_html5_output_css
441 <<|?>> option_dump_dtc loq
443 option_html5_output_css =
446 help @d (l10n_help_opt_output_css l) $
447 opt (OptionNameLong "output-css") $
448 string $ l10n_var_file l
449 command_compile_xml :: Loq d -> repr (Exit d) ArgCommand CommandCompileFormat
450 command_compile_xml loq@(Loqualization l) =
451 help @d (l10n_help_format_xml l) $
454 CommandCompileFormat_XML
455 <<$? option_help loq (help_usage $ command_compile_xml loq)
457 command_schema :: Loq d -> repr (Exit d) ArgCommand CommandSchema
458 command_schema loq@(Loqualization l) =
459 help @d (l10n_help_command_schema l) $
463 <<$? option_help loq (help_usage $ command_schema loq)
464 instance Plain.Doc d => CLI d (Plain.Plain d)
465 instance Plain.Doc d => CLI d Read.Parser
466 instance Plain.Doc d => CLI d (Help.Help d)
468 help_usage :: Plain.Doc d => Help.Help d (Exit d) t a -> d
469 help_usage = Help.textHelp Help.defReader
470 { Help.reader_command_indent = 2
471 , Help.reader_option_indent = 12
475 -- | Supported locales
476 type Langs = '[FR, EN]
477 type Lang = LocaleIn Langs
481 (\v -> Map.findWithDefault
482 (LocaleIn @Langs en_US)
483 (Text.pack $ List.takeWhile (\c -> Char.isAlphaNum c || c == '_') v)
486 <$> Env.lookupEnv "LANG"
489 type Loq d = Loqualization (L10n d)
491 class L10n_Var lang => L10n d lang where
492 l10n_cli :: FullLocale lang -> d
493 l10n_license :: FullLocale lang -> d
494 l10n_help_version :: FullLocale lang -> d
495 l10n_help_license :: FullLocale lang -> d
496 l10n_help_command_source :: FullLocale lang -> d
497 l10n_help_command_compile :: FullLocale lang -> d
498 l10n_help_command_schema :: FullLocale lang -> d
499 l10n_help_opt_lang :: FullLocale lang -> d
500 l10n_help_opt_output :: FullLocale lang -> d
501 l10n_help_opt_output_css :: FullLocale lang -> d
502 l10n_help_opt_dump_tct :: FullLocale lang -> d
503 l10n_help_opt_dump_xml :: FullLocale lang -> d
504 l10n_help_opt_dump_deps :: FullLocale lang -> d
505 l10n_help_opt_dump_dtc :: FullLocale lang -> d
506 l10n_help_format :: FullLocale lang -> d
507 l10n_help_format_plain :: FullLocale lang -> d
508 l10n_help_format_html5 :: FullLocale lang -> d
509 l10n_help_format_xml :: FullLocale lang -> d
510 l10n_help_opt_input :: FullLocale lang -> d
511 l10n_help_opt_help :: FullLocale lang -> d
512 class L10n_Var lang where
513 l10n_var_file :: FullLocale lang -> Name
514 l10n_var_locale :: FullLocale lang -> Name
521 instance (Semigroup d, IsString d) => Semigroup (Doc d) where
522 Doc x <> Doc y = Doc (x<>y)
523 x <> y = Doc $ runDoc x <> runDoc y
524 instance (Semigroup d, Monoid d, IsString d) => Monoid (Doc d) where
527 instance Doc.Breakable d => IsString (Doc d) where
528 fromString = Doc . Plain.words
529 instance (IsString d, Semigroup d, Monoid d) => IsList (Doc d) where
530 type Item (Doc d) = Doc d
532 fromList = Doc . foldMap runDoc
533 instance (IsString d, Semigroup d) => Doc.Trans (Doc d) where
534 type ReprOf (Doc d) = d
537 instance Doc.Breakable d => Doc.Textable (Doc d)
538 instance (Doc.Breakable d, Doc.Indentable d) => Doc.Indentable (Doc d)
539 instance Doc.Breakable d => Doc.Breakable (Doc d)
540 instance (IsString d, Semigroup d, Doc.Decorable d) => Doc.Decorable (Doc d)
541 instance (IsString d, Semigroup d, Doc.Colorable d) => Doc.Colorable (Doc d)
542 instance Plain.Doc d => Plain.Doc (Doc d)
544 runDoc :: (IsString d, Semigroup d) => Doc d -> d
547 Var n -> "<"<>fromString n<>">"
549 instance (IsString d, Semigroup d, Monoid d, Doc.Breakable d) => L10n (Doc d) EN where
550 l10n_cli _ = "Compiler of Convivial Technical Document (DTC)."
553 List.intercalate "\n"
554 [ "License: GNU GPLv3+"
555 , "Copyright: Julien Moutinho <julm+hdoc@autogeree.net>"
557 , "hdoc is free software: you can redistribute it and/or modify it"
558 , "under the terms of the GNU General Public License (GPL)"
559 , "as published by the Free Software Foundation;"
560 , "either in version 3, or (at your option) any later version."
562 , "hdoc is distributed in the hope that it will be useful,"
563 , "but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY"
564 , "or FITNESS FOR A PARTICULAR PURPOSE."
566 , "See the GNU GPL for more details."
567 , "You should have received a copy of the GNU GPL along with hdoc."
568 , "If not, see: http://www.gnu.org/licenses/"
570 l10n_help_version _ = "Show the version of this program."
571 l10n_help_license _ = "Inform about the license of this program."
572 l10n_help_command_source _ = "Format the source code of a TCT document."
573 l10n_help_command_compile _ = "Compile a TCT document into a format optimized for reading."
574 l10n_help_command_schema _ = "Show in RNC (RelaxNG Compact) format the XML schema of the DTC format."
575 l10n_help_opt_lang l = ["Use the language given by ", Var $ l10n_var_locale l, "."]
576 l10n_help_opt_output l = ["Output document into ", Var $ l10n_var_file l]
577 l10n_help_opt_output_css l = [ "Output CSS stylesheet into "
578 , Var $ l10n_var_file l
579 , " (if any), instead of incorporating it into the HTML."
581 l10n_help_opt_dump_tct _ = "Dump internal representation of TCT."
582 l10n_help_opt_dump_xml _ = "Dump internal representation of XML."
583 l10n_help_opt_dump_deps _ = "Dump dependencies, in Makefile format."
584 l10n_help_opt_dump_dtc _ = "Dump internal representation of DTC."
585 l10n_help_opt_help _ = "Show this help."
586 l10n_help_format _ = "Output format."
587 l10n_help_format_plain _ = "Output as plain text."
588 l10n_help_format_html5 _ = "Output as HTML5."
589 l10n_help_format_xml _ = "Output as XML."
590 l10n_help_opt_input l = ["Read input from ", Var $ l10n_var_file l, "."]
591 instance (IsString d , Semigroup d, Monoid d, Doc.Breakable d) => L10n (Doc d) FR where
592 l10n_cli _ = "Compilateur de Document Technique Convivial (DTC)."
595 List.intercalate "\n"
596 [ "Licence : GPLv3+ GNU"
597 , "Droit d’auteur : Julien Moutinho <julm+hdoc@autogeree.net>"
599 , "hdoc est un logiciel libre : vous pouvez le redistribuer et/ou le modifier"
600 , "selon les termes de la Licence Publique Générale (GPL) GNU"
601 , "telle que publiée par la Free Software Foundation ;"
602 , "en version 3, ou (à votre choix) n’importe quelle version ultérieure."
604 , "hdoc est distribué dans l’espoir qu’il sera utile,"
605 , "mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION"
606 , "ou de CONVENANCE À UN BUT PARTICULIER."
608 , "Voyez la GPL pour davantage de détails."
609 , "Vous devriez avoir reçu une copie de la GPL avec hdoc."
610 , "Si non, voyez : http://www.gnu.org/licenses/"
612 l10n_help_version _ = "Affiche la version de ce logiciel."
613 l10n_help_license _ = "Informe sur la licence de ce logiciel."
614 l10n_help_command_source _ = "Lit un document TCT et écrit un rendu préservant sa syntaxe."
615 l10n_help_command_compile _ = "Compile un document TCT vers un format optimisé pour la lecture."
616 l10n_help_command_schema _ = "Affiche au format RNC (RelaxNG Compact) le schéma XML du format DTC."
617 l10n_help_opt_lang l = ["Utilise le langage indiqué par ", Var $ l10n_var_locale l, "."]
618 l10n_help_opt_output l = ["Écrit dans ", Var $ l10n_var_file l, "."]
619 l10n_help_opt_output_css l = [ "Écrit la feuille de style CSS dans "
620 , Var $ l10n_var_file l
621 , ", au lieu de l’incorporer dans le HTML."
623 l10n_help_opt_dump_tct _ = "Écrit la représentation interne du TCT."
624 l10n_help_opt_dump_xml _ = "Écrit la représentation interne du XML."
625 l10n_help_opt_dump_deps _ = "Écrit les dépendences, au format Makefile."
626 l10n_help_opt_dump_dtc _ = "Écrit la représentation interne du DTC."
627 l10n_help_opt_help _ = "Affiche cette aide."
628 l10n_help_format _ = "Format de sortie."
629 l10n_help_format_plain _ = "Produit du texte brut."
630 l10n_help_format_html5 _ = "Produit du HTML5."
631 l10n_help_format_xml _ = "Produit du XML."
632 l10n_help_opt_input l = ["Lit depuis ", Var $ l10n_var_file l, "."]
633 instance L10n_Var EN where
634 l10n_var_file _ = "file"
635 l10n_var_locale _ = "locale"
636 instance L10n_Var FR where
637 l10n_var_file _ = "fichier"
638 l10n_var_locale _ = "locale"
640 -- * Filesystem utilities
641 readFile :: FilePath -> IO TL.Text
642 readFile fp = TL.decodeUtf8 <$> BSL.readFile fp
644 writeFile :: FilePath -> TL.Text -> IO ()
645 writeFile fp s = BSL.writeFile fp $ TL.encodeUtf8 s
647 withFile :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a
648 withFile = IO.withFile
650 removeFile :: FilePath -> IO ()
652 IO.removeFile f `IO.catchIOError` \e ->
653 if IO.isDoesNotExistError e