module Sos.Template
( RawTemplate
, Template
, parseTemplate
, instantiateTemplate
) where
import Sos.Exception
import Sos.Job (ShellCommand)
import Sos.Utils
import Control.Applicative
import Control.Monad.Catch (MonadThrow, throwM)
import Data.ByteString (ByteString)
import Text.ParserCombinators.ReadP
import qualified Data.Text.Encoding as Text
import qualified Data.Text.Lazy as LText
import qualified Data.Text.Lazy.Builder as LText
type RawTemplate = ByteString
type Template = [Either Int ByteString]
parseTemplate :: MonadThrow m => RawTemplate -> m Template
parseTemplate raw_template =
case readP_to_S parser (unpackBS raw_template) of
[(template, "")] -> pure template
_ -> throwM (SosCommandParseException raw_template)
where
parser :: ReadP Template
parser = some (capturePart <|||> textPart) <* eof
where
capturePart :: ReadP Int
capturePart = read <$> (char '\\' *> munch1 digit)
where
digit :: Char -> Bool
digit c = c >= '0' && c <= '9'
textPart :: ReadP ByteString
textPart = packBS <$> munch1 (/= '\\')
instantiateTemplate
:: forall m. MonadThrow m => [ByteString] -> Template -> m ShellCommand
instantiateTemplate vars0 template0 = go 0 vars0 template0
where
go :: Int -> [ByteString] -> Template -> m ShellCommand
go _ [] template =
case flattenTemplate template of
Left n ->
let err = "uninstantiated template variable: \\" ++ show n
in throwM (SosCommandApplyException template0 vars0 err)
Right x -> pure x
go n (t:ts) template = go (n+1) ts (map f template)
where
f :: Either Int ByteString -> Either Int ByteString
f (Left n')
| n == n' = Right t
| otherwise = Left n'
f x = x
flattenTemplate :: Template -> Either Int ShellCommand
flattenTemplate = go mempty
where
go :: LText.Builder -> Template -> Either Int ShellCommand
go !acc [] = Right (LText.unpack (LText.toLazyText acc))
go !acc (x:xs) =
case x of
Right s ->
let acc' = acc <> LText.fromText (Text.decodeUtf8 s)
in go acc' xs
Left n -> Left n