import Paths_ert(version) import Data.Version(showVersion) import System.Console.GetOpt(getOpt, usageInfo, ArgOrder(Permute), ArgDescr(..), OptDescr(..)) import System.Environment(getArgs, getProgName) import System.IO (hPutStrLn, stdin, stdout, stderr) import System.Exit(exitWith, ExitCode(ExitSuccess, ExitFailure)) import Data.ByteString (ByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BSC import qualified Data.EasyTpl as ET import qualified Data.Attoparsec.ByteString as AP import qualified Data.Aeson as J #ifdef WITH_YAML import qualified Data.Yaml as Y #endif data Action = ShowUsage | ShowVersion | ProcessTemplate deriving (Show, Eq) data DataFile = NullData | JsonFile FilePath #ifdef WITH_YAML | YamlFile FilePath #endif | NoneData deriving (Show, Eq) debugLog :: (Show s) => String -> s -> IO () #ifdef WITH_DEBUG debugLog key val = hPutStrLn stderr $ "\n" ++ key ++ ">> " ++ show val #else debugLog _ _ = return () #endif data Options = Options { optTemplate :: FilePath , optOutput :: FilePath , optDataFile :: DataFile , optAction :: Action } deriving (Show, Eq) defaults :: Options defaults = Options { optTemplate = "" , optOutput = "" , optDataFile = NoneData , optAction = ShowUsage } options :: [OptDescr (Options -> Options)] options = [ Option ['h', '?'] ["help"] (NoArg (\opts -> opts { optAction = ShowUsage })) "This usage hint." , Option ['V'] ["version"] (NoArg (\opts -> opts { optAction = ShowVersion })) "Version information." , Option ['i', 't'] ["input", "template"] (OptArg (\val opts -> opts { optTemplate = maybe "" id val , optAction = ProcessTemplate }) "FILE") ("Read input template from FILE (default: " ++ (optTemplate defaults) ++ ", empty means stdin).") , Option ['o', 'r'] ["output", "render-to"] (OptArg (\val opts -> opts { optOutput = maybe "" id val , optAction = ProcessTemplate }) "FILE") ("Output render result to FILE (default: " ++ (optOutput defaults) ++ ", empty means stdout).") , Option ['s'] ["standalone"] (NoArg (\opts -> opts { optDataFile = NullData , optAction = ProcessTemplate })) "Render template without data." , Option ['j'] ["json", "json-data"] (OptArg (\val opts -> opts { optDataFile = JsonFile $ maybe "" id val , optAction = ProcessTemplate }) "FILE") ("Render template with JSON data from FILE (default: compile template only, empty means stdin).") #ifdef WITH_YAML , Option ['y'] ["yaml", "yaml-data"] (OptArg (\val opts -> opts { optDataFile = YamlFile $ maybe "" id val , optAction = ProcessTemplate }) "FILE") ("Render template with YAML data from FILE (default: compile template only, empty means stdin).") #endif ] parseArgs :: [String] -> String -> IO Options parseArgs argv program = case getOpt Permute options argv of (args, fs, []) -> do doAction' $ foldl (flip id) defaults $ concat [ args , map (\val opts -> opts { optTemplate = val , optAction = ProcessTemplate }) fs ] (_, _, errs) -> do hPutStrLn stderr (concat errs ++ usageInfo usage options) exitWith $ ExitFailure 1 where -- usage :: String usage = "Usage: " ++ program ++ " [options]" -- verst :: String verst = program ++ " " ++ showVersion version -- doAction :: Options -> IO () doAction' opts = do debugLog "CommandOptions" opts case optAction opts of ShowUsage -> do hPutStrLn stderr (usageInfo usage options) exitWith ExitSuccess ShowVersion -> do hPutStrLn stderr verst exitWith ExitSuccess _ -> return opts doAction :: Options -> IO () doAction Options { optTemplate = template , optOutput = output , optDataFile = NoneData } = --BS.readFile template >>= AP.parseTest ET.parseTemplate inputFile template >>= return . AP.parseOnly ET.parseTemplate >>= outputFile output . BSC.pack . (++ "\n") . show doAction Options { optTemplate = template , optOutput = output , optDataFile = input } = do envData <- case input of NullData -> return $ ET.defaultData _ -> inputFile (dataFile input) >>= either error return . dataParse input debugLog "InputData" envData inputFile template >>= either error return . ET.compile >>= outputFile output . (flip ET.render envData) where dataFile :: DataFile -> FilePath dataFile (JsonFile file) = file #ifdef WITH_YAML dataFile (YamlFile file) = file #endif dataFile _ = "" dataParse :: DataFile -> BS.ByteString -> Either String ET.Value dataParse (JsonFile _) = AP.parseOnly J.json' #ifdef WITH_YAML dataParse (YamlFile _) = Y.decodeEither #endif dataParse _ = \_ -> Left "Unknown data type" inputFile :: FilePath -> IO ByteString inputFile "" = BS.hGetContents stdin inputFile path = BS.readFile path outputFile :: FilePath -> ByteString -> IO () outputFile "" = BS.hPutStr stdout outputFile path = BS.writeFile path main :: IO () main = getArgs >>= parseArgs' >>= doAction where parseArgs' argv = getProgName >>= parseArgs argv