ormolu
Getty Ritter
4 years ago
| 1 | 1 | -- «front-matter» |
| 2 | 2 | module Main where |
| 3 | ||
| 3 | 4 | -- «end» |
| 4 | 5 | |
| 5 | 6 | -- «functions» |
| 6 | 7 | main :: IO () |
| 7 | 8 | main = return () |
| 9 | ||
| 8 | 10 | -- «end» |
| 1 | 1 | {-# LANGUAGE OverloadedStrings #-} |
| 2 | 2 | |
| 3 |
module Bricoleur.Commands.Splice |
|
| 3 | module Bricoleur.Commands.Splice (runSplice) where | |
| 4 | 4 | |
| 5 |
import |
|
| 5 | import Bricoleur.Config | |
| 6 | import Bricoleur.Opts | |
| 7 | import Bricoleur.Utils | |
| 8 | import Control.Monad (foldM) | |
| 6 | 9 | import qualified Data.Map as M |
| 7 | 10 | import qualified Data.Text as T |
| 8 | 11 | import qualified Data.Text.Lazy as TL |
| 9 | 12 | import qualified Data.Text.Lazy.IO as TL |
| 10 |
import |
|
| 13 | import System.FilePath ((</>)) | |
| 11 | 14 | import qualified System.FilePath as Sys |
| 12 | ||
| 13 | import Bricoleur.Config | |
| 14 | import Bricoleur.Opts | |
| 15 | import Bricoleur.Utils | |
| 16 | 15 | |
| 17 | 16 | findFragmentIdentifiers :: TL.Text -> [T.Text] |
| 18 | 17 | findFragmentIdentifiers t |
| 19 | | (_, rs) <- TL.break (== '«') t | |
| 20 | , (f, rs') <- TL.break (== '»') (TL.drop 1 rs) | |
| 21 |
|
|
| 18 | | (_, rs) <- TL.break (== '«') t, | |
| 19 | (f, rs') <- TL.break (== '»') (TL.drop 1 rs), | |
| 20 | not (TL.null f) = | |
| 21 | TL.toStrict f : findFragmentIdentifiers rs' | |
| 22 | 22 | | otherwise = [] |
| 23 | 23 | |
| 24 | 24 | newtype Fragment = Fragment [T.Text] deriving (Eq, Show) |
| 28 | 28 | |
| 29 | 29 | findFragments :: FilePath -> T.Text -> [Source] -> Either TL.Text (IO TL.Text) |
| 30 | 30 | findFragments root s sources = case (T.splitOn "/" s) of |
| 31 | [] -> throw "Invalid empty fragment name!" | |
| 32 | (x:rs) -> case [ src | |
| 33 | | src@Source { sourceName = n } <- sources | |
| 34 | , n == x | |
| 31 | [] -> throw "Invalid empty fragment name!" | |
| 32 | (x : rs) -> case [ src | |
| 33 | | src@Source {sourceName = n} <- sources, | |
| 34 | n == x | |
| 35 | 35 | ] of |
| 36 | [src] -> go src rs (sourceExpose src) | |
| 37 | [] -> throw ("Unable to find source named " % stext) x | |
| 38 |
|
|
| 36 | [src] -> go src rs (sourceExpose src) | |
| 37 | [] -> throw ("Unable to find source named " % stext) x | |
| 38 | _ -> throw ("Ambiguous source name: " % stext) x | |
| 39 | 39 | where |
| 40 | ||
| 41 | 40 | go q [] (ExposeFile path) = |
| 42 | 41 | return (cOpenFile "file source" (root </> sourceDir q </> path)) |
| 43 | go _ [] ExposeSections{} = | |
| 44 | throw ("Splice identifier «" % stext % | |
| 45 | "» matches a file with sections") s | |
| 46 | go _ [] NamedMap{} = | |
| 47 | throw ("Splice identifier «" % stext % | |
| 48 | "» matches a map, but does not specify a key!") s | |
| 49 | ||
| 42 | go _ [] ExposeSections {} = | |
| 43 | throw | |
| 44 | ( "Splice identifier «" % stext | |
| 45 | % "» matches a file with sections" | |
| 46 | ) | |
| 47 | s | |
| 48 | go _ [] NamedMap {} = | |
| 49 | throw | |
| 50 | ( "Splice identifier «" % stext | |
| 51 | % "» matches a map, but does not specify a key!" | |
| 52 | ) | |
| 53 | s | |
| 50 | 54 | go q [section] (ExposeSections path) = |
| 51 | 55 | return (readSection (root </> sourceDir q </> path) section) |
| 52 | ||
| 53 | go _ (_:_) (ExposeSections path) = | |
| 54 | throw ("Splice identifier «" % stext % | |
| 55 | "» indexes too far into the path " % string % "!") | |
| 56 | s path | |
| 57 | ||
| 58 | go _ (_:_) (ExposeFile path) = | |
| 59 | throw ("Splice identifier «" % stext % | |
| 60 | "» indexes too far into the file " % string % "!") | |
| 61 | s path | |
| 62 | ||
| 63 | go q (k:rs) (NamedMap m) | |
| 56 | go _ (_ : _) (ExposeSections path) = | |
| 57 | throw | |
| 58 | ( "Splice identifier «" % stext | |
| 59 | % "» indexes too far into the path " | |
| 60 | % string | |
| 61 | % "!" | |
| 62 | ) | |
| 63 | s | |
| 64 | path | |
| 65 | go _ (_ : _) (ExposeFile path) = | |
| 66 | throw | |
| 67 | ( "Splice identifier «" % stext | |
| 68 | % "» indexes too far into the file " | |
| 69 | % string | |
| 70 | % "!" | |
| 71 | ) | |
| 72 | s | |
| 73 | path | |
| 74 | go q (k : rs) (NamedMap m) | |
| 64 | 75 | | Just e <- M.lookup k m = go q rs e |
| 65 | | otherwise = throw | |
| 66 | ("Splice identifier «" % stext % | |
| 67 | "» references a key " % stext % | |
| 68 | " which cannot be found.") s k | |
| 76 | | otherwise = | |
| 77 | throw | |
| 78 | ( "Splice identifier «" % stext | |
| 79 | % "» references a key " | |
| 80 | % stext | |
| 81 | % " which cannot be found." | |
| 82 | ) | |
| 83 | s | |
| 84 | k | |
| 69 | 85 | |
| 70 | 86 | doReplacements :: FilePath -> [Source] -> TL.Text -> IO TL.Text |
| 71 | 87 | doReplacements root e original = |
| 74 | 90 | doReplace file frag = do |
| 75 | 91 | new <- case findFragments root frag e of |
| 76 | 92 | Left err -> cDie ("Fragment error: " % text) err |
| 77 |
Right x |
|
| 93 | Right x -> x | |
| 78 | 94 | return (TL.replace (toIdent frag) new file) |
| 79 | 95 | |
| 80 | 96 | readSection :: FilePath -> T.Text -> IO TL.Text |
| 81 | 97 | readSection path f = do |
| 82 | 98 | contents <- cOpenFile "section source" path |
| 83 | 99 | case TL.breakOn (toIdent f) contents of |
| 84 | (_, "") -> cDie ("Unable to find section " % shown % | |
| 85 | " in file " % string) f path | |
| 100 | (_, "") -> | |
| 101 | cDie | |
| 102 | ( "Unable to find section " % shown | |
| 103 | % " in file " | |
| 104 | % string | |
| 105 | ) | |
| 106 | f | |
| 107 | path | |
| 86 | 108 | (_, rs) -> |
| 87 | 109 | let (_, skipLine) = TL.breakOn "\n" rs |
| 88 | 110 | (section, _) = TL.breakOn "«end»" (TL.drop 1 skipLine) |
| 89 | 111 | (skipLine', _) = TL.breakOnEnd "\n" section |
| 90 |
|
|
| 112 | in return (TL.init skipLine') | |
| 91 | 113 | |
| 92 | 114 | runSplice :: Config -> Options -> IO () |
| 93 | 115 | runSplice conf opts = do |
| 2 | 2 | |
| 3 | 3 | module Bricoleur.Commands.Test (runTest) where |
| 4 | 4 | |
| 5 |
import |
|
| 5 | import Bricoleur.Config | |
| 6 | import Bricoleur.Opts | |
| 7 | import Bricoleur.Utils | |
| 8 | import Control.Monad (forM_) | |
| 6 | 9 | import qualified Data.ByteString.Lazy.Char8 as B |
| 7 | 10 | import qualified GHC.IO.Exception as Exn |
| 8 |
import |
|
| 11 | import System.FilePath ((</>)) | |
| 9 | 12 | import qualified System.FilePath as Sys |
| 10 | 13 | import qualified System.Process as Sys |
| 11 | ||
| 12 | import Bricoleur.Opts | |
| 13 | import Bricoleur.Config | |
| 14 | import Bricoleur.Utils | |
| 15 | ||
| 16 | 14 | |
| 17 | 15 | runTest :: Config -> Options -> IO () |
| 18 | 16 | runTest conf opts = do |
| 19 | 17 | let root = Sys.takeDirectory (optFile opts) |
| 20 | 18 | cDebug ("running tests for " % shown) (confDocument conf) |
| 21 |
forM_ (confSources conf) $ \ |
|
| 19 | forM_ (confSources conf) $ \samp -> do | |
| 22 | 20 | cDebug ("- running test for " % stext) (sourceName samp) |
| 23 | 21 | runCommand root samp |
| 24 | ||
| 25 | 22 | |
| 26 | 23 | runCommand :: FilePath -> Source -> IO () |
| 27 | 24 | runCommand root src = do |
| 29 | 26 | let dir = root </> sourceDir src |
| 30 | 27 | cDebug (" $ " % string % " (in '" % string % "')") ln dir |
| 31 | 28 | (outH, inH) <- Sys.createPipe |
| 32 | let process = (Sys.shell ln) { Sys.cwd = Just (root </> sourceDir src) | |
| 33 | , Sys.std_out = Sys.UseHandle inH | |
| 34 | , Sys.std_err = Sys.UseHandle inH | |
| 35 | } | |
| 29 | let process = | |
| 30 | (Sys.shell ln) | |
| 31 | { Sys.cwd = Just (root </> sourceDir src), | |
| 32 | Sys.std_out = Sys.UseHandle inH, | |
| 33 | Sys.std_err = Sys.UseHandle inH | |
| 34 | } | |
| 36 | 35 | (_, _, _, p) <- Sys.createProcess process |
| 37 | 36 | bufOutput <- B.hGetContents outH |
| 38 | 37 | code <- Sys.waitForProcess p |
| 40 | 39 | Exn.ExitSuccess -> return () |
| 41 | 40 | Exn.ExitFailure n -> do |
| 42 | 41 | let idtext = map (B.append " - ") (B.lines bufOutput) |
| 43 | cError ("\nCommand '" % string % | |
| 44 | "' exited with error (" % shown % | |
| 45 |
|
|
| 42 | cError | |
| 43 | ( "\nCommand '" % string | |
| 44 | % "' exited with error (" | |
| 45 | % shown | |
| 46 | % ")" | |
| 47 | ) | |
| 48 | ln | |
| 49 | n | |
| 46 | 50 | mapM_ bsErrorLn idtext |
| 1 | 1 | {-# LANGUAGE OverloadedStrings #-} |
| 2 | 2 | |
| 3 | 3 | module Bricoleur.Config |
| 4 | ( Config(..) | |
| 5 | , Source(..) | |
| 6 | , Expose(..) | |
| 7 | , getConfig | |
| 8 | , example | |
| 9 | ) where | |
| 4 | ( Config (..), | |
| 5 | Source (..), | |
| 6 | Expose (..), | |
| 7 | getConfig, | |
| 8 | example, | |
| 9 | ) | |
| 10 | where | |
| 10 | 11 | |
| 11 |
import |
|
| 12 | import Control.Applicative ((<|>)) | |
| 12 | 13 | import qualified Data.Adnot as A |
| 13 | 14 | import qualified Data.ByteString as B |
| 14 | 15 | import qualified Data.Map.Strict as M |
| 16 | 17 | import qualified Data.Vector as V |
| 17 | 18 | |
| 18 | 19 | data Config = Config |
| 19 | { confDocument :: FilePath | |
| 20 | , confSources :: [Source] | |
| 21 |
|
|
| 20 | { confDocument :: FilePath, | |
| 21 | confSources :: [Source] | |
| 22 | } | |
| 23 | deriving (Eq, Show) | |
| 22 | 24 | |
| 23 | 25 | instance A.FromAdnot Config where |
| 24 | 26 | parseAdnot = A.withSumNamed "config file" "document" go |
| 25 | 27 | where |
| 26 | 28 | go payload |
| 27 | | Just file <- payload V.!? 0 | |
| 28 | = Config <$> A.parseAdnot file | |
| 29 |
|
|
| 29 | | Just file <- payload V.!? 0 = | |
| 30 | Config <$> A.parseAdnot file | |
| 31 | <*> mapM A.parseAdnot (V.toList (V.tail payload)) | |
| 30 | 32 | | otherwise = Left "expected source file in config" |
| 31 | 33 | |
| 32 | ||
| 33 | 34 | data Source = Source |
| 34 | { sourceName :: T.Text | |
| 35 | , sourceDir :: FilePath | |
| 36 | , sourceCommands :: [String] | |
| 37 | , sourceExpose :: Expose | |
| 38 |
|
|
| 35 | { sourceName :: T.Text, | |
| 36 | sourceDir :: FilePath, | |
| 37 | sourceCommands :: [String], | |
| 38 | sourceExpose :: Expose | |
| 39 | } | |
| 40 | deriving (Eq, Show) | |
| 39 | 41 | |
| 40 | 42 | instance A.FromAdnot Source where |
| 41 | 43 | parseAdnot = A.withProduct "source" $ \p -> do |
| 42 | name <- p A..: "name" | |
| 43 | dir <- p A..: "dir" | |
| 44 |
|
|
| 44 | name <- p A..: "name" | |
| 45 | dir <- p A..: "dir" | |
| 46 | cmds <- p A..: "cmd" | |
| 45 | 47 | expose <- p A..: "expose" |
| 46 | 48 | return (Source name dir cmds expose) |
| 47 | ||
| 48 | 49 | |
| 49 | 50 | data Expose |
| 50 | 51 | = ExposeFile FilePath |
| 51 | 52 | | ExposeSections FilePath |
| 52 | 53 | | NamedMap (M.Map T.Text Expose) |
| 53 |
|
|
| 54 | deriving (Eq, Show) | |
| 54 | 55 | |
| 55 | 56 | instance A.FromAdnot Expose where |
| 56 | 57 | parseAdnot v = file v <|> sections v <|> namedMap v |
| 57 | 58 | where |
| 58 |
file = A.withSumNamed "exposed fragments" "file" $ \ |
|
| 59 | file = A.withSumNamed "exposed fragments" "file" $ \ps -> | |
| 59 | 60 | case V.toList ps of |
| 60 |
[] |
|
| 61 | [] -> Left "Expected name for file" | |
| 61 | 62 | [f] -> ExposeFile <$> A.parseAdnot f |
| 62 |
_ |
|
| 63 | _ -> Left "Too many arguments to file" | |
| 63 | 64 | |
| 64 |
sections = A.withSumNamed "exposed fragments" "sections" $ \ |
|
| 65 | sections = A.withSumNamed "exposed fragments" "sections" $ \ps -> | |
| 65 | 66 | case V.toList ps of |
| 66 |
[] |
|
| 67 | [] -> Left "Expected name for sections" | |
| 67 | 68 | [f] -> ExposeSections <$> A.parseAdnot f |
| 68 |
_ |
|
| 69 | _ -> Left "Too many arguments to sections" | |
| 69 | 70 | |
| 70 |
namedMap = A.withProduct "exposed fragments" $ \ |
|
| 71 | namedMap = A.withProduct "exposed fragments" $ \p -> | |
| 71 | 72 | NamedMap <$> mapM A.parseAdnot p |
| 72 | 73 | |
| 73 | 74 | parseConfig :: B.ByteString -> Either String Config |
| 79 | 80 | return (parseConfig conf) |
| 80 | 81 | |
| 81 | 82 | example :: Config |
| 82 | example = Config | |
| 83 | { confDocument = "main.md" | |
| 84 | , confSources = | |
| 85 | [ Source | |
| 86 | { sourceName = "rust-sample" | |
| 87 | , sourceDir = "s1" | |
| 88 | , sourceCommands = ["cargo clean", "cargo build"] | |
| 89 | , sourceExpose = ExposeFile "src/main.rs" | |
| 90 | } | |
| 91 | , Source | |
| 92 | { sourceName = "haskell-sample" | |
| 93 | , sourceDir = "s2" | |
| 94 | , sourceCommands = ["cabal new-build"] | |
| 95 | , sourceExpose = ExposeSections "Main.hs" | |
| 96 | } | |
| 97 | ] | |
| 98 | } | |
| 83 | example = | |
| 84 | Config | |
| 85 | { confDocument = "main.md", | |
| 86 | confSources = | |
| 87 | [ Source | |
| 88 | { sourceName = "rust-sample", | |
| 89 | sourceDir = "s1", | |
| 90 | sourceCommands = ["cargo clean", "cargo build"], | |
| 91 | sourceExpose = ExposeFile "src/main.rs" | |
| 92 | }, | |
| 93 | Source | |
| 94 | { sourceName = "haskell-sample", | |
| 95 | sourceDir = "s2", | |
| 96 | sourceCommands = ["cabal new-build"], | |
| 97 | sourceExpose = ExposeSections "Main.hs" | |
| 98 | } | |
| 99 | ] | |
| 100 | } | |
| 1 | 1 | module Bricoleur.Opts |
| 2 | ( Command(..) | |
| 3 | , Options(..) | |
| 4 | , getOpts | |
| 5 | ) where | |
| 2 | ( Command (..), | |
| 3 | Options (..), | |
| 4 | getOpts, | |
| 5 | ) | |
| 6 | where | |
| 6 | 7 | |
| 7 |
import |
|
| 8 | import Control.Applicative ((<|>)) | |
| 8 | 9 | import qualified Options.Applicative as Opt |
| 9 | 10 | import qualified System.Directory as Sys |
| 10 | 11 | import qualified System.FilePath as Sys |
| 12 | 13 | data Command |
| 13 | 14 | = Test |
| 14 | 15 | | Splice |
| 15 |
|
|
| 16 | deriving (Eq, Show) | |
| 16 | 17 | |
| 17 | 18 | data Options = Options |
| 18 | { optFile :: FilePath | |
| 19 | , optVerbose :: Bool | |
| 20 | , optCommand :: Command | |
| 21 | } deriving (Eq, Show) | |
| 19 | { optFile :: FilePath, | |
| 20 | optVerbose :: Bool, | |
| 21 | optCommand :: Command | |
| 22 | } | |
| 23 | deriving (Eq, Show) | |
| 22 | 24 | |
| 23 | 25 | desc :: String |
| 24 | 26 | desc = "Bricoleur: a tool for testing and stiching code into documents" |
| 25 | 27 | |
| 26 | 28 | opts :: Opt.ParserInfo Options |
| 27 | opts = Opt.info (p Opt.<**> Opt.helper) | |
| 28 | (Opt.progDesc desc <> | |
| 29 | Opt.fullDesc <> | |
| 30 | Opt.header "arglbargl") | |
| 29 | opts = | |
| 30 | Opt.info | |
| 31 | (p Opt.<**> Opt.helper) | |
| 32 | ( Opt.progDesc desc | |
| 33 | <> Opt.fullDesc | |
| 34 | <> Opt.header "arglbargl" | |
| 35 | ) | |
| 31 | 36 | where |
| 32 | p = Options <$> (path <|> pure "") | |
| 33 | <*> verbose | |
| 34 |
|
|
| 37 | p = | |
| 38 | Options <$> (path <|> pure "") | |
| 39 | <*> verbose | |
| 40 | <*> Opt.subparser (test <> splice) | |
| 35 | 41 | |
| 36 | path = Opt.strOption | |
| 37 | (Opt.short 'f' <> | |
| 38 | Opt.long "file" <> | |
| 39 | Opt.metavar "PATH" <> | |
| 40 |
|
|
| 42 | path = | |
| 43 | Opt.strOption | |
| 44 | ( Opt.short 'f' | |
| 45 | <> Opt.long "file" | |
| 46 | <> Opt.metavar "PATH" | |
| 47 | <> Opt.help "The path to the project file" | |
| 48 | ) | |
| 41 | 49 | |
| 42 | verbose = Opt.switch | |
| 43 | (Opt.short 'v' <> | |
| 44 | Opt.long "verbose" <> | |
| 45 | Opt.help "Show debug messages") | |
| 50 | verbose = | |
| 51 | Opt.switch | |
| 52 | ( Opt.short 'v' | |
| 53 | <> Opt.long "verbose" | |
| 54 | <> Opt.help "Show debug messages" | |
| 55 | ) | |
| 46 | 56 | |
| 47 | test = Opt.command "test" $ Opt.info | |
| 48 | (pure Test Opt.<**> Opt.helper) | |
| 49 |
|
|
| 57 | test = | |
| 58 | Opt.command "test" $ | |
| 59 | Opt.info | |
| 60 | (pure Test Opt.<**> Opt.helper) | |
| 61 | (Opt.progDesc "test the provided sources") | |
| 50 | 62 | |
| 51 | splice = Opt.command "splice" $ Opt.info | |
| 52 | (pure Splice Opt.<**> Opt.helper) | |
| 53 |
|
|
| 63 | splice = | |
| 64 | Opt.command "splice" $ | |
| 65 | Opt.info | |
| 66 | (pure Splice Opt.<**> Opt.helper) | |
| 67 | (Opt.progDesc "splice sources into a final draft") | |
| 54 | 68 | |
| 55 | 69 | getOpts :: IO Options |
| 56 | 70 | getOpts = do |
| 57 | 71 | cwd <- Sys.getCurrentDirectory |
| 58 | 72 | options <- Opt.execParser opts |
| 59 | return $ if null (optFile options) | |
| 60 | then options { optFile = cwd Sys.</> "bricoleur" } | |
| 61 |
|
|
| 73 | return $ | |
| 74 | if null (optFile options) | |
| 75 | then options {optFile = cwd Sys.</> "bricoleur"} | |
| 76 | else options | |
| 1 | 1 | {-# LANGUAGE OverloadedStrings #-} |
| 2 | 2 | |
| 3 | 3 | module Bricoleur.Utils |
| 4 | ( cOutput | |
| 5 | , cDebug | |
| 6 | , cWarn | |
| 7 | , cError | |
| 8 | , cDie | |
| 9 | , bsErrorLn | |
| 4 | ( cOutput, | |
| 5 | cDebug, | |
| 6 | cWarn, | |
| 7 | cError, | |
| 8 | cDie, | |
| 9 | bsErrorLn, | |
| 10 | cOpenFile, | |
| 11 | throw, | |
| 12 | F.format, | |
| 13 | (F.%), | |
| 14 | F.stext, | |
| 15 | F.text, | |
| 16 | F.string, | |
| 17 | F.shown, | |
| 18 | ) | |
| 19 | where | |
| 10 | 20 | |
| 11 | , cOpenFile | |
| 12 | ||
| 13 | , throw | |
| 14 | ||
| 15 | , F.format | |
| 16 | , (F.%) | |
| 17 | , F.stext | |
| 18 | , F.text | |
| 19 | , F.string | |
| 20 | , F.shown | |
| 21 | ) where | |
| 22 | ||
| 23 | import qualified Formatting as F | |
| 24 | 21 | import qualified Data.ByteString.Lazy.Char8 as BS |
| 25 | 22 | import qualified Data.Text.Lazy as TL |
| 26 | 23 | import qualified Data.Text.Lazy.Builder as TL |
| 27 | 24 | import qualified Data.Text.Lazy.IO as TL |
| 25 | import qualified Formatting as F | |
| 28 | 26 | import qualified System.Directory as Sys |
| 29 | 27 | import qualified System.Exit as Sys |
| 30 | 28 | import qualified System.IO as Sys |
| 31 | 29 | import qualified System.Posix.IO as Posix |
| 32 | 30 | import qualified System.Posix.Terminal as Posix |
| 33 | ||
| 34 | import Prelude (FilePath, IO, Either(Left), ($)) | |
| 31 | import Prelude (Either (Left), FilePath, IO, ($)) | |
| 35 | 32 | |
| 36 | 33 | -- | Produce a 'Left' value from a format string |
| 37 | 34 | throw :: F.Format (Either TL.Text r) a -> a |
| 38 | 35 | throw f = |
| 39 | F.runFormat f (\ b -> Left (TL.toLazyText b)) | |
| 40 | ||
| 36 | F.runFormat f (\b -> Left (TL.toLazyText b)) | |
| 41 | 37 | |
| 42 | 38 | stderr :: TL.Text -> IO () |
| 43 | 39 | stderr = TL.hPutStr Sys.stderr |
| 44 | ||
| 45 | 40 | |
| 46 | 41 | -- | Write output to stdout |
| 47 | 42 | cOutput :: TL.Text -> IO () |
| 48 | 43 | cOutput = TL.putStrLn |
| 49 | 44 | |
| 50 | ||
| 51 | 45 | -- | Write a debug message to stderr. |
| 52 | 46 | cDebug :: F.Format (IO ()) a -> a |
| 53 |
cDebug msg = F.runFormat msg $ \ |
|
| 47 | cDebug msg = F.runFormat msg $ \b -> | |
| 54 | 48 | TL.hPutStrLn Sys.stderr (TL.toLazyText b) |
| 55 | ||
| 56 | 49 | |
| 57 | 50 | -- | Write a warning message to stderr. If we are connected to a TTY, |
| 58 | 51 | -- then this will write in an orange color. |
| 60 | 53 | cWarn msg = F.runFormat msg $ \b -> do |
| 61 | 54 | isTTY <- Posix.queryTerminal Posix.stdOutput |
| 62 | 55 | if isTTY |
| 63 | then do stderr "\x1b[93m" | |
| 64 | stderr (TL.toLazyText b) | |
| 65 |
|
|
| 56 | then do | |
| 57 | stderr "\x1b[93m" | |
| 58 | stderr (TL.toLazyText b) | |
| 59 | stderr "\x1b[39m\n" | |
| 66 | 60 | else TL.hPutStrLn Sys.stderr (TL.toLazyText b) |
| 67 | ||
| 68 | 61 | |
| 69 | 62 | -- | Write an error message to stderr and exit. If we are connected to |
| 70 | 63 | -- a TTY, this message will be in red. |
| 72 | 65 | cError msg = F.runFormat msg $ \b -> do |
| 73 | 66 | isTTY <- Posix.queryTerminal Posix.stdOutput |
| 74 | 67 | if isTTY |
| 75 | then do stderr "\x1b[91m" | |
| 76 | stderr (TL.toLazyText b) | |
| 77 |
|
|
| 68 | then do | |
| 69 | stderr "\x1b[91m" | |
| 70 | stderr (TL.toLazyText b) | |
| 71 | stderr "\x1b[39m\n" | |
| 78 | 72 | else TL.hPutStrLn Sys.stderr (TL.toLazyText b) |
| 79 | 73 | |
| 80 | 74 | -- | Write an error message to stderr and exit. If we are connected to |
| 81 | 75 | -- a TTY, this message will be in red. |
| 82 | 76 | cDie :: F.Format (IO r) a -> a |
| 83 |
cDie msg = F.runFormat msg $ \ |
|
| 77 | cDie msg = F.runFormat msg $ \b -> do | |
| 84 | 78 | isTTY <- Posix.queryTerminal Posix.stdOutput |
| 85 | 79 | if isTTY |
| 86 | then do stderr "\x1b[91m" | |
| 87 | stderr (TL.toLazyText b) | |
| 88 |
|
|
| 80 | then do | |
| 81 | stderr "\x1b[91m" | |
| 82 | stderr (TL.toLazyText b) | |
| 83 | stderr "\x1b[39m\n" | |
| 89 | 84 | else TL.hPutStrLn Sys.stderr (TL.toLazyText b) |
| 90 | 85 | Sys.exitFailure |
| 91 | ||
| 92 | 86 | |
| 93 | 87 | bsErrorLn :: BS.ByteString -> IO () |
| 94 | 88 | bsErrorLn bs = do |
| 95 | 89 | isTTY <- Posix.queryTerminal Posix.stdOutput |
| 96 | 90 | if isTTY |
| 97 | then do BS.hPutStr Sys.stderr "\x1b[91m" | |
| 98 | BS.hPutStr Sys.stderr bs | |
| 99 |
|
|
| 91 | then do | |
| 92 | BS.hPutStr Sys.stderr "\x1b[91m" | |
| 93 | BS.hPutStr Sys.stderr bs | |
| 94 | BS.hPutStr Sys.stderr "\x1b[39m\n" | |
| 100 | 95 | else BS.hPutStrLn Sys.stderr bs |
| 101 | 96 | |
| 102 | 97 | cOpenFile :: TL.Text -> FilePath -> IO TL.Text |
| 104 | 99 | exists <- Sys.doesFileExist path |
| 105 | 100 | if exists |
| 106 | 101 | then TL.readFile path |
| 107 | else cDie ("Unable to open " F.% F.text F.% | |
| 108 | " file at " F.% F.string) purpose path | |
| 102 | else | |
| 103 | cDie | |
| 104 | ( "Unable to open " F.% F.text | |
| 105 | F.% " file at " | |
| 106 | F.% F.string | |
| 107 | ) | |
| 108 | purpose | |
| 109 | path | |
| 1 | 1 | {-# LANGUAGE OverloadedStrings #-} |
| 2 | 2 | |
| 3 | 3 | module Bricoleur |
| 4 | ( main | |
| 5 | , Opt.getOpts | |
| 6 | ) where | |
| 7 | ||
| 8 | import qualified System.Exit as Sys | |
| 9 | ||
| 10 | import qualified Bricoleur.Config as Conf | |
| 11 | import qualified Bricoleur.Opts as Opt | |
| 4 | ( main, | |
| 5 | Opt.getOpts, | |
| 6 | ) | |
| 7 | where | |
| 12 | 8 | |
| 13 | 9 | import qualified Bricoleur.Commands.Splice as Cmd |
| 14 | 10 | import qualified Bricoleur.Commands.Test as Cmd |
| 11 | import qualified Bricoleur.Config as Conf | |
| 12 | import qualified Bricoleur.Opts as Opt | |
| 13 | import qualified System.Exit as Sys | |
| 15 | 14 | |
| 16 | 15 | -- | Run the main @bricoleur@ function with the provided options. |
| 17 | 16 | main :: Opt.Options -> IO () |
| 21 | 20 | Left err -> Sys.die err |
| 22 | 21 | Right x -> return x |
| 23 | 22 | case Opt.optCommand opts of |
| 24 |
Opt.Test |
|
| 23 | Opt.Test -> Cmd.runTest config opts | |
| 25 | 24 | Opt.Splice -> Cmd.runSplice config opts |