packages feed

goldplate-0.1.1: src/Text/Splice.hs

module Text.Splice
    ( SpliceEnv
    , MissingEnvVar (..)
    , splice
    ) where

import           Control.Exception (Exception)
import qualified Data.List         as List
import           Data.Typeable     (Typeable)

-- | Environment for splicing in things.
type SpliceEnv = [(String, String)]

data MissingEnvVar = MissingEnvVar String
    deriving (Typeable)

instance Show MissingEnvVar where
    show (MissingEnvVar k) = "Missing environment variable: " ++ k

instance Exception MissingEnvVar

-- | Splice in a string with "Hello ${FOO}" syntax.
splice :: SpliceEnv -> String -> Either MissingEnvVar String
splice env = go
  where
    go str = case break (== '$') str of
        -- Splice
        (xs, '$' : '{' : ys) -> case break (== '}') ys of
            (key, '}' : zs) -> case lookup key env of
                Nothing  -> Left $ MissingEnvVar key
                Just val -> fmap ((xs ++ val) ++) (go zs)
            (_, _) -> fmap ((xs ++ "${") ++) (go ys)
        -- Escape
        (xs, '$' : '$' : ys) ->
            let (dollars, zs) = break (== '{') ys in
            if all (== '$') dollars && "{" `List.isPrefixOf` zs
                then (xs ++) . (dollars ++) . ("${" ++) <$> go (drop 1 zs)
                else (xs ++) . ("$$" ++) <$> go ys
        (xs, []) -> Right xs
        (xs, (y : ys)) -> (xs ++) . (y :) <$> (go ys)