yst-0.2.2: Yst/Yaml.hs
{-
Copyright (C) 2009 John MacFarlane <jgm@berkeley.edu>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-}
module Yst.Yaml (readYamlFile, nodeToYamlNode)
where
import Yst.Types
import Yst.Util
import Data.Yaml.Syck hiding (unpackBuf, packBuf)
import qualified Data.Yaml.Syck (unpackBuf, packBuf)
import Data.Time
import System.Locale (defaultTimeLocale)
import Codec.Binary.UTF8.String (encodeString, decodeString)
import qualified Data.ByteString.Char8 as B (ByteString, readFile)
-- Note: Syck isn't unicode aware, so we use parseYamlBytes and do our
-- own encoding and decoding.
type Buf = B.ByteString
unpackBuf :: Buf -> String
unpackBuf = decodeString . Data.Yaml.Syck.unpackBuf
packBuf :: String -> Buf
packBuf = Data.Yaml.Syck.packBuf . encodeString
readYamlFile :: FilePath -> IO Node
readYamlFile f = catch (B.readFile f >>= parseYamlBytes >>= return . yamlNodeToNode)
(\e -> errorExit 11 ("Error parsing " ++ f ++ ": " ++ show e) >> return NNil)
yamlNodeToNode :: YamlNode -> Node
yamlNodeToNode n =
case n_elem n of
EStr s -> case parseAsDate (unpackBuf s) of
Nothing -> NString (unpackBuf s)
Just d -> NDate d
EMap xs | all (\(k,_) -> isStrNode k) xs -> NMap pairs
where pairs = foldr addPair [] xs
addPair (k,v) acc = if isStrNode k && strFrom k == "<<" -- hash merge, unsupported in Syck!
then case n_elem v of -- so we do it ourselves
EMap ys -> foldr addPair acc ys
_ -> error "Tried hash merge on non-hash"
else case lookup kstr acc of
Just _ -> acc -- overridden
Nothing -> (kstr, yamlNodeToNode v) : acc
where kstr = strFrom k
EMap _ -> error "Map keys must all be strings."
ESeq xs -> NList $ map yamlNodeToNode xs
ENil -> NNil
nodeToYamlNode :: Node -> YamlNode
nodeToYamlNode n =
case n of
NString s -> mkNode (EStr $ packBuf s)
NDate s -> mkNode (EStr $ packBuf $ formatTime defaultTimeLocale "%x" s)
NMap xs -> mkNode (EMap $ map (\(k,v) -> (nodeToYamlNode (NString k), nodeToYamlNode v)) xs)
NList xs -> mkNode (ESeq $ map nodeToYamlNode xs)
NNil -> mkNode ENil
isStrNode :: YamlNode -> Bool
isStrNode x = case n_elem x of
EStr _ -> True
_ -> False
strFrom :: YamlNode -> String
strFrom x = case n_elem x of
EStr z -> unpackBuf z
_ -> error "expected EStr node"