ginger-0.8.0.0: cli/GingerCLI.hs
-- | An example Ginger CLI application.
--
-- Takes two optional arguments; the first one is a template file, the second
-- one a file containing some context data in JSON format.
{-#LANGUAGE OverloadedStrings #-}
{-#LANGUAGE ScopedTypeVariables #-}
module Main where
import Text.Ginger
import Text.Ginger.Html
import Data.Text as Text
import qualified Data.Aeson as JSON
import qualified Data.Yaml as YAML
import Data.Maybe
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Control.Applicative
import System.Environment ( getArgs )
import System.IO
import System.IO.Error
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Control.Monad.Trans.Class ( lift )
import Control.Monad.Trans.Maybe
import Control.Monad
import Data.Default ( def )
import System.Process as Process
import Text.Printf (printf)
loadFile fn = openFile fn ReadMode >>= hGetContents
loadFileMay fn =
tryIOError (loadFile fn) >>= \e ->
case e of
Right contents -> return (Just contents)
Left err -> do
print err
return Nothing
decodeFile :: (JSON.FromJSON v) => FilePath -> IO (Maybe v)
decodeFile fn = YAML.decode <$> (openFile fn ReadMode >>= BS.hGetContents)
printF :: GVal (Run p IO Html)
printF = fromFunction $ go
where
go :: [(Maybe Text, GVal (Run p IO Html))] -> Run p IO Html (GVal (Run p IO Html))
go args = forM_ args printArg >> return def
printArg (Nothing, v) = liftRun . putStrLn . Text.unpack . asText $ v
printArg (Just x, _) = return ()
systemF :: GVal (Run p IO Html)
systemF = fromFunction $ go . fmap snd
where
go :: [GVal (Run p IO Html)] -> Run p IO Html (GVal (Run p IO Html))
go [cmd,args,pipe] = do
let gArgs = fmap gAsStr . fromMaybe [] . asList $ args
strToGVal <$> liftRun (Process.readProcess (gAsStr cmd) gArgs (gAsStr pipe))
go [] = pure def
go [cmd] = go [cmd,def]
go [cmd,args] = go [cmd,args,def]
go xs = go $ Prelude.take 3 xs
gAsStr :: GVal m -> String
gAsStr = Text.unpack . asText
strToGVal :: String -> GVal m
strToGVal = toGVal . Text.pack
main = do
args <- getArgs
let (srcFn, scopeFn) = case args of
[] -> (Nothing, Nothing)
a:[] -> (Just a, Nothing)
a:b:[] -> (Just a, Just b)
scope <- case scopeFn of
Nothing -> return Nothing
Just fn -> (decodeFile fn :: IO (Maybe (HashMap Text JSON.Value)))
let scopeLookup key = toGVal (scope >>= HashMap.lookup key)
resolve = loadFileMay
let contextLookup :: Text -> Run p IO Html (GVal (Run p IO Html))
contextLookup key =
case key of
"print" -> return printF
"system" -> return systemF
_ -> return $ scopeLookup key
(tpl, src) <- case srcFn of
Just fn -> (,) <$> parseGingerFile resolve fn <*> return Nothing
Nothing -> getContents >>= \s -> (,) <$> parseGinger resolve Nothing s <*> return (Just s)
-- TODO: do some sort of arg parsing thing so that we can turn
-- template dumping on or off.
-- print tpl
case tpl of
Left err -> do
tplSource <-
case src of
Just s ->
return (Just s)
Nothing -> do
let s = sourceName <$> peSourcePosition err
case s of
Nothing -> return Nothing
Just sn -> Just <$> loadFile sn
printParserError tplSource err
Right t -> do
let context =
makeContextHtmlExM
contextLookup
(putStr . Text.unpack . htmlSource)
(hPutStrLn stderr . show)
runGingerT context t >>= either (hPutStrLn stderr . show) showOutput
where
showOutput value
| isNull value = return ()
| otherwise = putStrLn . show $ value
printParserError :: Maybe String -> ParserError -> IO ()
printParserError srcMay = putStrLn . formatParserError srcMay
displayParserError :: String -> ParserError -> IO ()
displayParserError src pe = do
case peSourcePosition pe of
Just pos -> do
let ln = Prelude.take 1 . Prelude.drop (sourceLine pos - 1) . Prelude.lines $ src
case ln of
[] -> return ()
x:_ -> do
putStrLn x
putStrLn $ Prelude.replicate (sourceColumn pos - 1) ' ' ++ "^"
_ -> return ()