module B9.Content.YamlObject ( YamlObject (..)
) where
import Control.Applicative
import Control.Parallel.Strategies
import Data.Binary (Binary(..))
import Data.Data
import Data.Function
import Data.HashMap.Strict hiding (singleton)
import Data.Hashable
import Data.Maybe
import Data.Semigroup
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import Data.Vector ((++), singleton)
import Data.Yaml
import GHC.Generics (Generic)
import Prelude hiding ((++))
import Text.Printf
import B9.Content.AST
import B9.Content.StringTemplate
import Test.QuickCheck
data YamlObject =
YamlObject Data.Yaml.Value
deriving (Eq,Data,Typeable,Generic)
instance Hashable YamlObject
instance Binary YamlObject
instance NFData YamlObject
instance Binary Data.Yaml.Value where
put = put . encode
get = do
v <- get
return $ fromJust $ decode v
instance Read YamlObject where
readsPrec _ = readsYamlObject
where
readsYamlObject :: ReadS YamlObject
readsYamlObject s =
[ (yamlFromString y, r2) | ("YamlObject", r1) <- lex s,
(y,r2) <- reads r1]
where
yamlFromString :: String -> YamlObject
yamlFromString =
either error id . decodeSyntax "HERE-DOC" . E.encodeUtf8 . T.pack
instance Show YamlObject where
show (YamlObject o) =
"YamlObject " <> show (T.unpack $ E.decodeUtf8 $ encode o)
instance Semigroup YamlObject where
(YamlObject v1) <> (YamlObject v2) = YamlObject (combine v1 v2)
where
combine :: Data.Yaml.Value
-> Data.Yaml.Value
-> Data.Yaml.Value
combine (Object o1) (Object o2) =
Object (unionWith combine o1 o2)
combine (Array a1) (Array a2) =
Array (a1 ++ a2)
combine (Array a1) t2 =
Array (a1 ++ singleton t2)
combine t1 (Array a2) =
Array (singleton t1 ++ a2)
combine (String s1) (String s2) = String (s1 <> s2)
combine t1 t2 =
array [t1,t2]
instance ConcatableSyntax YamlObject where
decodeSyntax src str =
case decodeEither str of
Left e ->
Left (printf "YamlObject parse error in file '%s':\n%s\n"
src
e)
Right o ->
return (YamlObject o)
encodeSyntax (YamlObject o) =
E.encodeUtf8 (T.pack "#cloud-config\n") <> encode o
instance ASTish YamlObject where
fromAST ast =
case ast of
ASTObj pairs -> do
ys <- mapM fromASTPair pairs
return (YamlObject (object ys))
ASTArr asts -> do
ys <- mapM fromAST asts
let ys' =
(\(YamlObject o) ->
o) <$>
ys
return (YamlObject (array ys'))
ASTMerge [] ->
error "ASTMerge MUST NOT be used with an empty list!"
ASTMerge asts -> do
ys <- mapM fromAST asts
return (foldl1 (<>) ys)
ASTEmbed c ->
YamlObject . toJSON . T.unpack . E.decodeUtf8 <$> render c
ASTString str -> return (YamlObject (toJSON str))
ASTParse src@(Source _ srcPath) -> do
c <- readTemplateFile src
case decodeSyntax srcPath c of
Right s -> return s
Left e ->
error
(printf
"could not parse yaml source file: '%s'\n%s\n"
srcPath
e)
AST a -> pure a
where
fromASTPair (key,value) = do
(YamlObject o) <- fromAST value
let key' = T.pack key
return $ key' .= o
instance Arbitrary YamlObject where