libconfig-0.2.0.0: src/Language/Libconfig/Decode.hs
{-# OPTIONS_GHC -Wall #-}
{-|
Module : Language.Libconfig.Decode
Copyright : (c) Matthew Peddie 2014
License : BSD3
Maintainer : mpeddie@gmail.com
Stability : experimental
Portability : GHC
Converting libconfig native data into friendly Haskell structures from
"Language.Libconfig.Types".
-}
module Language.Libconfig.Decode (
-- * Decoding libconfig native data
decode
, decodeFrom
) where
import Control.Applicative
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Maybe
import qualified Data.Text as T (pack)
import Language.Libconfig.Types
import Language.Libconfig.Bindings (ConfigType(..))
import qualified Language.Libconfig.Bindings as C
-- TODO(MP): Properly convert integral types depending on the setting
-- format.
toScalar :: C.Setting -> MaybeT IO Scalar
toScalar s = MaybeT $ C.configSettingType s >>= go
where
go :: ConfigType -> IO (Maybe Scalar)
go IntType = Just . Integer . fromIntegral <$> C.configSettingGetInt s
go Int64Type = Just . Integer64 . fromIntegral <$> C.configSettingGetInt64 s
go FloatType = Just . Float <$> C.configSettingGetFloat s
go BoolType = Just . Boolean <$> C.configSettingGetBool s
go StringType = Just . String . T.pack <$> C.configSettingGetString s
go _ = return Nothing
toList :: C.Setting -> MaybeT IO List
toList s = liftIO (C.configSettingType s) >>= go
where
go :: ConfigType -> MaybeT IO List
go ListType = do
l <- liftIO $ C.configSettingLength s
mapM (\i -> MaybeT (C.configSettingGetElem s i) >>= toValue) [0 .. l - 1]
go _ = MaybeT $ return Nothing
toArray :: C.Setting -> MaybeT IO Array
toArray s = liftIO (C.configSettingType s) >>= go
where
go :: ConfigType -> MaybeT IO Array
go ArrayType = do
l <- liftIO $ C.configSettingLength s
mapM (\i -> MaybeT (C.configSettingGetElem s i) >>= toScalar) [0 .. l - 1]
go _ = MaybeT $ return Nothing
toGroup :: C.Setting -> MaybeT IO Group
toGroup s = liftIO (C.configSettingType s) >>= go
where
go :: ConfigType -> MaybeT IO Group
go GroupType = do
l <- liftIO $ C.configSettingLength s
mapM (\i -> MaybeT (C.configSettingGetElem s i) >>= decodeSetting) [0 .. l - 1]
go _ = MaybeT $ return Nothing
toValue :: C.Setting -> MaybeT IO Value
toValue s = liftIO (C.configSettingType s) >>= go
where
go :: ConfigType -> MaybeT IO Value
go NoneType = MaybeT $ return Nothing
go ListType = List <$> toList s
go ArrayType = Array <$> toArray s
go GroupType = Group <$> toGroup s
go _ = Scalar <$> toScalar s
decodeSetting :: C.Setting -> MaybeT IO Setting
decodeSetting s = liftIO (C.configSettingType s) >>= go
where
go NoneType = MaybeT $ return Nothing
go _ =
(:=) <$> fmap T.pack (MaybeT $ C.configSettingName s) <*> toValue s
-- | Convert a native 'C.Configuration' into a top-level 'Group' of
-- 'Setting's.
--
-- >>> Just conf <- C.configNew "test/test.conf"
-- >>> decode conf
-- Just ["version" := Scalar (String "1.0"),"application" := Group ["window" := Group ["title" := Scalar (String "My Application"),"size" := Group ["w" := Scalar (Integer 640),"h" := Scalar (Integer 480)],"pos" := Group ["x" := Scalar (Integer 350),"y" := Scalar (Integer 250)]],"list" := List [List [Scalar (String "abc"),Scalar (Integer 123),Scalar (Boolean True)],Scalar (Float 1.234),List []],"books" := List [Group ["title" := Scalar (String "Treasure Island"),"author" := Scalar (String "Robert Louis Stevenson"),"price" := Scalar (Float 29.95),"qty" := Scalar (Integer 5)],Group ["title" := Scalar (String "Snow Crash"),"author" := Scalar (String "Neal Stephenson"),"price" := Scalar (Float 9.99),"qty" := Scalar (Integer 8)]],"misc" := Group ["pi" := Scalar (Float 3.141592654),"bigint" := Scalar (Integer64 9223372036854775807),"columns" := Array [String "Last Name",String "First Name",String "MI"],"bitmask" := Scalar (Integer 8131)]]]
decode :: C.Configuration -> IO (Maybe Group)
decode c = do
res <- runMaybeT $ MaybeT (C.configRootSetting c) >>= toGroup
C.touchConfiguration c
return res
-- | Load the libconfig configuration file at the given path and try
-- to convert it to a top-level 'Group' of 'Setting's.
--
-- >>> decodeFrom "test/test.conf"
-- Just ["version" := Scalar (String "1.0"),"application" := Group ["window" := Group ["title" := Scalar (String "My Application"),"size" := Group ["w" := Scalar (Integer 640),"h" := Scalar (Integer 480)],"pos" := Group ["x" := Scalar (Integer 350),"y" := Scalar (Integer 250)]],"list" := List [List [Scalar (String "abc"),Scalar (Integer 123),Scalar (Boolean True)],Scalar (Float 1.234),List []],"books" := List [Group ["title" := Scalar (String "Treasure Island"),"author" := Scalar (String "Robert Louis Stevenson"),"price" := Scalar (Float 29.95),"qty" := Scalar (Integer 5)],Group ["title" := Scalar (String "Snow Crash"),"author" := Scalar (String "Neal Stephenson"),"price" := Scalar (Float 9.99),"qty" := Scalar (Integer 8)]],"misc" := Group ["pi" := Scalar (Float 3.141592654),"bigint" := Scalar (Integer64 9223372036854775807),"columns" := Array [String "Last Name",String "First Name",String "MI"],"bitmask" := Scalar (Integer 8131)]]]
decodeFrom :: String -> IO (Maybe Group)
decodeFrom filename = do
c <- C.configNew filename
case c of
Just conf -> decode conf
Nothing -> return Nothing