configuration-tools-0.6.1: Tools for specifying and parsing configurations
CopyrightCopyright © 2015 PivotCloud Inc.
LicenseMIT
MaintainerLars Kuhtz <lkuhtz@pivotmail.com>
Stabilityexperimental
Safe HaskellNone
LanguageHaskell2010

Configuration.Utils.Maybe

Description

This module provides tools for defining Maybe configuration types.

Synopsis

Simple Maybe Values

Optional configuration values are supposed to be encoded by wrapping the respective type with Maybe.

For simple values the standard FromJSON instance from the aeson package can be used along with the ..: operator.

data LogConfig = LogConfig
   { _logLevel ∷ !Int
   , _logFile ∷ !(Maybe String)
   }

$(makeLenses ''LogConfig)

defaultLogConfig ∷ LogConfig
defaultLogConfig = LogConfig
    { _logLevel = 1
    , _logFile = Nothing
    }

instance FromJSON (LogConfig → LogConfig) where
    parseJSON = withObject "LogConfig" $ \o → id
        <$< logLevel ..: "LogLevel" % o
        <*< logFile ..: "LogConfig" % o

instance ToJSON LogConfig where
    toJSON config = object
        [ "LogLevel" .= _logLevel config
        , "LogConfig" .= _logFile config
        ]

When defining command line option parsers with .:: and %:: all options are optional. When an option is not present on the command line the default value is used. For Maybe values it is therefore enough to wrap the parsed value into Just.

pLogConfig ∷ MParser LogConfig
pLogConfig = id
    <$< logLevel .:: option auto
        % long "log-level"
        % metavar "INTEGER"
        % help "log level"
    <*< logFile .:: fmap Just % strOption
        % long "log-file"
        % metavar "FILENAME"
        % help "log file name"

Record Maybe Values

For Maybe types that wrap product (record) types the following orphan FromJSON instance is provided:

instance (FromJSON (a → a), FromJSON a) ⇒ FromJSON (Maybe a → Maybe a)
    parseJSON Null = pure (const Nothing)
    parseJSON v = f <$> parseJSON v <*> parseJSON v
      where
        f g _ Nothing = Just g
        f _ g (Just x) = Just (g x)

(Using an orphan instance is generally problematic but convenient in this case. It's unlikely that an instance for this type is needed elsewhere. If this is an issue for you, please let me know. In that case we can define a new type for optional configuration values.)

The semantics are as follows:

  • If the parsed configuration value is Null the result is Nothing.
  • If the parsed configuration value is not Null then the result is an update function that

    • updates the given default value if this value is Just x or
    • is a constant function that returns the value that is parsed from the configuration using the FromJSON instance for the configuration type.

Note, that this instance requires an FromJSON instance for the configuration type itself as well as a FromJSON instance for the update function of the configuration type. The former can be defined by means of the latter as follows:

instance FromJSON MyType where
    parseJSON v = parseJSON v <*> pure defaultMyType

This instance will cause the usage of defaultMyType as default value if the default value that is given to the configuration parser is Nothing and the parsed configuration is not Null.

maybeOption Source #

Arguments

:: a

default value that is used if base configuration is Nothing

-> Bool

whether to enable this parser or not (usually is a boolean option parser)

-> (a -> a)

update function (usually given as applicative 'MParser a')

-> Maybe a

the base value that is updated (usually the result of parsing the configuration file)

-> Maybe a 

Command line parser for record Maybe values

Example:

data Setting = Setting
    { _setA ∷ !Int
    , _setB ∷ !String
    }
    deriving (Show, Read, Eq, Ord, Typeable)

$(makeLenses ''Setting)

defaultSetting ∷ Setting
defaultSetting = Setting
    { _setA = 0
    , _setB = 1
    }

instance ToJSON Setting where
    toJSON setting = object
       [ "a" .= _setA setting
       , "b" .= _setB setting
       ]

instance FromJSON (Setting → Setting) where
    parseJSON = withObject "Setting" $ \o → id
        <$< setA ..: "a" % o
        <*< setB ..: "b" % o

instance FromJSON Setting where
   parseJSON v = parseJSON v <*> pure defaultSetting

pSetting ∷ MParser Setting
pSetting = id
    <$< setA .:: option auto
        % short 'a'
        <> metavar "INT"
        <> help "set a"
    <*< setB .:: option auto
        % short 'b'
        <> metavar "INT"
        <> help "set b"

-- | Use 'Setting' as 'Maybe' in a configuration:
--
data Config = Config
    { _maybeSetting ∷ !(Maybe Setting)
    }
    deriving (Show, Read, Eq, Ord, Typeable)

$(makeLenses ''Config)

defaultConfig ∷ Config
defaultConfig = Config
    { _maybeSetting = defaultSetting
    }

instance ToJSON Config where
    toJSON config = object
        [ "setting" .= maybeSetting
        ]

instance FromJSON (Config → Config) where
    parseJSON = withObject "Config" $ \o → id
        <$< maybeSetting %.: "setting" % o

pConfig ∷ MParser Config
pConfig = id
    <$< maybeSetting %:: (maybeOption defaultSetting
        <$> pEnableSetting
        <*> pSetting)
  where
    pEnableSetting = boolOption
        % long "setting-enable"
        <> value False
        <> help "Enable configuration flags for setting"

Orphan instances

(FromJSON (a -> a), FromJSON a) => FromJSON (Maybe a -> Maybe a) Source # 
Instance details

Methods

parseJSON :: Value -> Parser (Maybe a -> Maybe a) #

parseJSONList :: Value -> Parser [Maybe a -> Maybe a] #