packages feed

ert-0.0.1.0: Main.hs

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