configurator-ng-0.0.0.0: Data/Configurator/Parser.hs
{-# LANGUAGE CPP, OverloadedStrings, ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns, ViewPatterns, TupleSections #-}
-- |
-- Module: Data.Configurator.Parser
-- Copyright: (c) 2015 Leon P Smith
-- License: BSD3
-- Maintainer: Leon P Smith <leon@melding-monads.com>
-- Stability: experimental
-- Portability: portable
--
-- A set of combinators for high-level configuration parsing.
module Data.Configurator.Parser
( ConfigParser
, ConfigParserA
, ConfigParserM
, ConfigError (..)
, ConfigErrorLocation (..)
, ConversionError (..)
, ConversionErrorWhy (..)
, Config
, ConfigTransform
, unsafeBind
, runParser
, runParserA
, runParserM
, parserA
, parserM
, subassocs
, subassocs'
, subgroups
, localConfig
, union
, subconfig
, superconfig
, recover
, key
, keyWith
) where
import Prelude hiding (null)
import Data.DList (DList)
import qualified Data.DList as DL
#if !(MIN_VERSION_base(4,8,0))
import Data.Monoid(Monoid(..))
#endif
import Data.Monoid((<>))
import Data.Configurator.Config
( Config )
import Data.Configurator.Types.Internal hiding (Group)
import Data.Configurator.FromValue
( FromMaybeValue(fromMaybeValue)
, MaybeParser
, runMaybeParser
)
import qualified Data.Configurator.Config as C
import qualified Data.Configurator.Config.Internal as CI
import Data.Configurator.Parser.Implementation
runParser :: ConfigParser m => m a -> Config -> (Maybe a, [ConfigError])
runParser m conf = let (ma, errs) = unConfigParser_ m conf
in (ma, toErrors errs)
{- | Returns all the value bindings from the current configuration context
-- that is contained within the given subgroup, in lexicographic order.
-- For example, given the following context:
@
x = 1
foo {
x = 2
bar {
y = on
}
}
foo = \"Hello\"
@
Then the following arguments to 'subassocs' would return the following lists:
@
subassocs "" ==> [("foo",String \"Hello\"),("x",Number 1)]
subassocs "foo" ==> [("foo.x",Number 2)]
subassocs "foo.bar" ==> [("foo.bar.x",Bool True)]
@
All other arguments to subassocs would return [] in the given context.
-}
subassocs :: ConfigParser m => Name -> m [(Name, Value)]
subassocs t = configParser_ (\c -> (Just (C.subassocs t c), mempty))
{- | Returns all the value bindings from the current configuration context
-- that is contained within the given subgroup and all of it's subgroups
-- in lexicographic order. For example, given the following context:
@
x = 1
foo {
x = 2
bar {
y = on
}
}
foo = \"Hello\"
@
Then the following arguments to 'subassocs\'' would return the following lists:
@
subassocs\' "" ==> [ ("foo" , String \"Hello\")
, ("foo.bar.y" , Bool True )
, ("foo.x" , Number 2 )
, ("x" , Number 1 )
]
subassocs\' "foo" ==> [ ("foo.bar.y" , Bool True )
, ("foo.x" , Number 2 )
]
subassocs\' "foo.bar" ==> [ ("foo.bar.y" , Bool True )
]
@
All other arguments to @subassocs\'@ would return @[]@ in the given context.
-}
subassocs' :: ConfigParser m => Name -> m [(Name, Value)]
subassocs' t = configParser_ (\c -> (Just (C.subassocs' t c), mempty))
{- | Returns all the non-empty value groupings that is directly under
-- the argument grouping in the current configuration context.
-- For example, given the following context:
@
foo { }
bar {
a {
x = 1
}
b {
c {
y = 2
}
}
}
default
a {
x = 3
}
}
@
Then the following arguments to 'subgroups' would return the following lists:
@
subgroups "" ==> [ "bar", "default" ]
subgroups "bar" ==> [ "bar.a", "bar.b" ]
subgroups "bar.b" ==> [ "bar.b.c" ]
subgroups "default" ==> [ "default.a" ]
@
All other arguments to @subgroups@ would return @[]@ in the given context.
-}
subgroups :: ConfigParser m => Name -> m [Name]
subgroups t = configParser_ (\c -> (Just (C.subgroups t c), mempty))
-- | Modifies the 'Config' that a subparser is operating on.
-- This is perfectly analogous to 'Control.Monad.Reader.local'.
localConfig :: ConfigParser m => ConfigTransform -> m a -> m a
localConfig f m = configParser_ (\r -> unConfigParser_ m (interpConfigTransform f r))
-- | Exactly the same as 'runParser', except less polymorphic
runParserA :: ConfigParserA a -> Config -> (Maybe a, [ConfigError])
runParserA = runParser
-- | Exactly the same as 'runParser', except less polymorphic
runParserM :: ConfigParserM a -> Config -> (Maybe a, [ConfigError])
runParserM = runParser
-- | Lift a 'ConfigParserM' action into a generic 'ConfigParser'
-- action. Note that this does not change the semantics of the
-- argument, it just allows a 'ConfigParserM' computation to be
-- embedded in another 'ConfigParser' computation of either variant.
parserM :: ConfigParser m => ConfigParserM a -> m a
parserM (ConfigParserM m) = configParser_ m
-- | Lift a 'ConfigParserA' action into a generic 'ConfigParser'
-- action. Note that this does not change the semantics of the
-- argument, it just allows a 'ConfigParserA' computation to be
-- embedded in another 'ConfigParser' computation of either variant.
parserA :: ConfigParser m => ConfigParserA a -> m a
parserA (ConfigParserA m) = configParser_ m
-- | Given the expression @'recover' action@, the @action@ will be
-- run, and if it returns no value, @recover action@ will return
-- 'Nothing'. If @action@ returns the value @a@, then
-- @recover action@ will return the value @'Just' a@. Any errors
-- or warnings are passed through as-is.
recover :: ConfigParser m => m a -> m (Maybe a)
recover m = configParser_ $ \r -> let (ma, errs) = unConfigParser_ m r
in (Just ma, errs)
-- Look up a given value in the current configuration context, and convert
-- the value using the 'fromMaybeValue' method.
key :: (ConfigParser m, FromMaybeValue a) => Name -> m a
key name = keyWith name fromMaybeValue
-- Look up a given value in the current configuration context, and convert
-- the value using the 'MaybeParser' argument.
keyWith :: (ConfigParser m) => Name -> MaybeParser a -> m a
keyWith name parser =
configParser_ $ \(CI.Config c) ->
case CI.lookupWithName name c of
Nothing ->
convert (KeyMissing (DL.toList (getLookupPlan name c))) Nothing
Just (name', v) ->
convert (Key "" name') (Just v)
where
convert loc mv =
case runMaybeParser parser mv of
(Nothing, errs) ->
(Nothing, singleError (ConfigError loc (Just errs)))
(Just a, []) ->
(Just a, mempty)
(Just a, errs@(_:_)) ->
(Just a, singleError (ConfigError loc (Just errs)))
getLookupPlan :: Name -> CI.ConfigPlan a -> DList Name
getLookupPlan = CI.foldPlan DL.empty (<>) (\k _ -> DL.singleton k)