kdl-hs-1.1.1: KDL language parser and API
Safe HaskellNone
LanguageGHC2021

KDL.Decoder.Monad

Description

See KDL for more information.

Synopsis

Monad-specific definitions

fail :: forall a o. Text -> Decoder o a Source #

Same as fail, except a more ergonomic signature for use in do-notation.

Re-exports from KDL.Decoder.Arrow

data DecodeError Source #

Instances

Instances details
Show DecodeError Source # 
Instance details

Defined in KDL.Decoder.Internal.Error

Eq DecodeError Source # 
Instance details

Defined in KDL.Decoder.Internal.Error

bool :: DecodeArrow Value a Bool Source #

Decode a KDL bool value.

some :: Alternative f => f a -> f [a] #

One or more.

many :: Alternative f => f a -> f [a] #

Zero or more.

null :: DecodeArrow Value a () Source #

Decode a KDL null value.

any :: DecodeArrow Value a Value Source #

Decode any value, without any possibility of failure.

string :: DecodeArrow Value a Text Source #

Decode a KDL string value.

option :: Alternative f => a -> f a -> f a Source #

Return the given default value if the given action fails.

option a f === f <|> pure a

optional :: Alternative f => f a -> f (Maybe a) #

One or none.

It is useful for modelling any computation that is allowed to fail.

Examples

Expand

Using the Alternative instance of Control.Monad.Except, the following functions:

>>> import Control.Monad.Except
>>> canFail = throwError "it failed" :: Except String Int
>>> final = return 42                :: Except String Int

Can be combined by allowing the first function to fail:

>>> runExcept $ canFail *> final
Left "it failed"
>>> runExcept $ optional canFail *> final
Right 42

type Decoder o a = DecodeArrow o () a Source #

node :: DecodeNode a => Text -> NodeListDecoder a Source #

Decode a node with the given name using a DecodeNode instance.

Ensures that the node has been fully decoded (e.g. error if the user specified extra args, misspelled a prop name, or provided extraneous children nodes). To allow extra values, use the following functions to parse and ignore them:

KDL.many $ KDL.arg @Value
KDL.remainingProps @Value
KDL.children $ KDL.remainingNodes @Node

Example

Expand
instance KDL.DecodeNode Person where
  nodeDecoder = proc () -> do
    name <- KDL.arg -< ()
    returnA -< Person{..}

let
  config =
    """
    person "Alice"
    person "Bob"
    """
  decoder = KDL.document $ proc () -> do
    many $ KDL.node "person" -< ()
KDL.decodeWith decoder config == Right ["Alice", "Bob"]

decodeWith :: DocumentDecoder a -> Text -> Either DecodeError a Source #

Decode the given KDL configuration with the given decoder.

oneOf :: Alternative f => [f a] -> f a Source #

Return the first result that succeeds.

oneOf [a, b, c] === a <|> b <|> c

decodeFileWith :: DocumentDecoder a -> FilePath -> IO (Either DecodeError a) Source #

Read KDL configuration from the given file path and decode it with the given decoder.

decodeDocWith :: DocumentDecoder a -> Document -> Either DecodeError a Source #

Decode an already-parsed Document with the given decoder.

liftDecodeM :: (a -> DecodeM b) -> DecodeArrow o a b Source #

withDecoder :: DecodeArrow o a b -> (b -> DecodeM c) -> DecodeArrow o a c Source #

Run actions within a DecodeArrow. Useful for adding post-processing logic.

Example

Expand
decoder = KDL.withDecoder KDL.number $ \x -> do
  when (x > 100)
    KDL.failM $ "argument is too large: " <> (Text.pack . show) x
  pure $ MyVal x

debug :: Show o => DecodeArrow o a () Source #

Debug the current state of the object being decoded.

Example

Expand
decoder = proc () -> do
  KDL.debug -< ()    -- Node{entries = [Entry{}, Entry{}]}
  x <- KDL.arg -< ()
  KDL.debug -< ()    -- Node{entries = [Entry{}]}
  y <- KDL.arg -< ()
  KDL.debug -< ()    -- Node{entries = []}
  returnA -< (x, y)

data DecodeM a Source #

The monad that returns either a DecodeError or a result of type a.

The odd structure here is because of our backtracking semantics. We want to collect all errors that may appear (even if a value is successfully parsed) so that if we get a failure later on, we can return the deepest error, even if it was in a successful branch.

Take this motivating example: a node takes an arbitrary number of string args. If you pass some strings then a number, it'll successfully parse up to the number and return success, only for the node to fail later with "unexpected argument: 123". But the true error was "unexpected number, expected string".

Instances

Instances details
Alternative DecodeM Source # 
Instance details

Defined in KDL.Decoder.Internal.DecodeM

Methods

empty :: DecodeM a #

(<|>) :: DecodeM a -> DecodeM a -> DecodeM a #

some :: DecodeM a -> DecodeM [a] #

many :: DecodeM a -> DecodeM [a] #

Applicative DecodeM Source # 
Instance details

Defined in KDL.Decoder.Internal.DecodeM

Methods

pure :: a -> DecodeM a #

(<*>) :: DecodeM (a -> b) -> DecodeM a -> DecodeM b #

liftA2 :: (a -> b -> c) -> DecodeM a -> DecodeM b -> DecodeM c #

(*>) :: DecodeM a -> DecodeM b -> DecodeM b #

(<*) :: DecodeM a -> DecodeM b -> DecodeM a #

Functor DecodeM Source # 
Instance details

Defined in KDL.Decoder.Internal.DecodeM

Methods

fmap :: (a -> b) -> DecodeM a -> DecodeM b #

(<$) :: a -> DecodeM b -> DecodeM a #

Monad DecodeM Source # 
Instance details

Defined in KDL.Decoder.Internal.DecodeM

Methods

(>>=) :: DecodeM a -> (a -> DecodeM b) -> DecodeM b #

(>>) :: DecodeM a -> DecodeM b -> DecodeM b #

return :: a -> DecodeM a #

runDecodeM :: DecodeM a -> Either DecodeError a Source #

Run a DecodeM action and return the result or the deepest error found.

decodeThrow :: DecodeErrorKind -> DecodeM a Source #

Throw an error.

document :: NodeListDecoder a -> DocumentDecoder a Source #

Finalize a NodeListDecoder as a DocumentDecoder to use with decodeWith.

Ensures that all nodes have been decoded (e.g. error if the user specified unrecognized nodes, or misspelled a node name). To allow unrecognized nodes, use remainingNodes @Node and ignore the result.

documentSchema :: DocumentDecoder a -> SchemaOf NodeList Source #

Get the schema of a DocumentDecoder.

The schema is statically determined without running the decoder.

remainingNodes :: DecodeNode a => NodeListDecoder (Map Text [a]) Source #

Decode all remaining nodes.

Example

Expand
instance KDL.DecodeNode MyArg where
  nodeDecoder = proc () -> do
    name <- KDL.arg -< ()
    returnA -< MyArg{..}

let
  config =
    """
    build "pkg1"
    build "pkg2"
    lint "pkg1"
    """
  decoder = KDL.document $ proc () -> do
    KDL.remainingNodes -< ()
KDL.decodeWith decoder config == (Right . Map.fromList) [("build", [MyArg "pkg1", MyArg "pkg2"]), ("lint", [MyArg "pkg1"])]

argAt :: DecodeValue a => Text -> NodeListDecoder a Source #

A helper to decode the first argument of the first node with the given name. A utility for nodes that are acting like a key-value store.

KDL.argAt "my-node" === KDL.node "my-node" KDL.arg

Example

Expand
let
  config =
    """
    verbose #true
    """
  decoder = KDL.document $ proc () -> do
    KDL.argAt "verbose" -< ()
KDL.decodeWith decoder config == Right True

argsAt :: DecodeValue a => Text -> NodeListDecoder [a] Source #

A helper to decode all the arguments of the first node with the given name. A utility for nodes that are acting like a key-value store with a list of values.

KDL.argsAt "my-node" === KDL.node "my-node" $ KDL.many KDL.arg

This is different from many (argAt "foo"), as that would find multiple nodes named "foo" and get the first arg from each.

Example

Expand
let
  config =
    """
    email "aexample.com" "bexample.com"
    """
  decoder = KDL.document $ proc () -> do
    KDL.argsAt "email" -< ()
KDL.decodeWith decoder config == Right ["aexample.com", "bexample.com"]

dashChildrenAt :: DecodeValue a => Text -> NodeListDecoder [a] Source #

A helper for decoding child values in a list following the KDL convention of being named "-".

KDL.dashChildrenAt "my-node" === KDL.nodeWith "my-node" $ KDL.children $ KDL.many $ KDL.argAt "-"

Example

Expand
let
  config =
    """
    attendees {
      - "Alice"
      - "Bob"
    }
    """
  decoder = KDL.document $ proc () -> do
    KDL.dashChildrenAt "attendees" -< ()
KDL.decodeWith decoder config == Right ["Alice", "Bob"]

dashNodesAt :: DecodeNode a => Text -> NodeListDecoder [a] Source #

A helper for decoding child nodes in a list following the KDL convention of being named "-".

KDL.dashNodesAt "my-node" === KDL.nodeWith "my-node" $ KDL.children $ KDL.many $ KDL.node "-"

Example

Expand
instance KDL.DecodeNode Attendee where
  nodeDecoder = proc () -> do
    name <- KDL.arg -< ()
    returnA -< Attendee{..}

let
  config =
    """
    attendees {
      - "Alice"
      - "Bob"
    }
    """
  decoder = KDL.document $ proc () -> do
    KDL.dashNodesAt "attendees" -< ()
KDL.decodeWith decoder config == Right [Attendee "Alice", Attendee "Bob"]

nodeWith :: forall a b. Typeable b => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a b Source #

Same as node, except explicitly specify the NodeDecoder instead of using DecodeNode.

Example

Expand
let
  config =
    """
    person "Alice"
    person "Bob"
    """
  decoder = KDL.document $ proc () -> do
    many . KDL.nodeWith "person" $ KDL.arg -< ()
KDL.decodeWith decoder config == Right ["Alice", "Bob"]

remainingNodesWith :: forall a b. Typeable b => NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b]) Source #

Same as remainingNodes, except explicitly specify the NodeDecoder instead of using DecodeNode

Example

Expand
let
  config =
    """
    build "pkg1"
    build "pkg2"
    lint "pkg1"
    """
  decoder = KDL.document $ proc () -> do
    KDL.remainingNodesWith $ KDL.arg -< ()
KDL.decodeWith decoder config == (Right . Map.fromList) [("build", ["pkg1", "pkg2"]), ("lint", ["pkg1"])]

argAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a b Source #

Same as argAt, except explicitly specify the ValueDecoder instead of using DecodeValue

Example

Expand
let
  config =
    """
    verbose #true
    """
  decoder = KDL.document $ proc () -> do
    KDL.argAtWith "verbose" KDL.bool -< ()
KDL.decodeWith decoder config == Right True

argsAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b] Source #

Same as argsAt, except explicitly specify the ValueDecoder instead of using DecodeValue

Example

Expand
let
  config =
    """
    email "aexample.com" "bexample.com"
    """
  decoder = KDL.document $ proc () -> do
    KDL.argsAtWith "email" KDL.string -< ()
KDL.decodeWith decoder config == Right ["aexample.com", "bexample.com"]

dashChildrenAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b] Source #

Same as dashChildrenAt, except explicitly specify the ValueDecoder instead of using DecodeValue

Example

Expand
let
  config =
    """
    attendees {
      - "Alice"
      - "Bob"
    }
    """
  decoder = KDL.document $ proc () -> do
    KDL.dashChildrenAtWith "attendees" $ KDL.string -< ()
KDL.decodeWith decoder config == Right ["Alice", "Bob"]

dashNodesAtWith :: forall a b. Typeable b => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a [b] Source #

Same as dashChildrenAt, except explicitly specify the NodeDecoder instead of using DecodeNode

Example

Expand
let
  config =
    """
    attendees {
      - "Alice"
      - "Bob"
    }
    """
  decoder = KDL.document $ proc () -> do
    KDL.dashNodesAtWith "attendees" KDL.arg -< ()
KDL.decodeWith decoder config == Right [Alice, Bob]

nodeWith' :: forall a b. Typeable b => Text -> [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a b Source #

Same as nodeWith, except allow specifying type annotations.

remainingNodesWith' :: forall a b. Typeable b => [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b]) Source #

Same as remainingNodesWith, except allow specifying type annotations.

argAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a b Source #

Same as argAtWith, except allow specifying type annotations.

argsAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b] Source #

Same as argsAtWith, except allow specifying type annotations.

dashChildrenAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b] Source #

Same as dashChildrenAtWith, except allow specifying type annotations.

class Typeable a => DecodeNode a where Source #

The type class for specifying how a type should be decoded from a KDL node.

Minimal complete definition

nodeDecoder

Methods

validNodeTypeAnns :: Proxy a -> [Text] Source #

Allowed type annotations for a node of this type.

If specified, nodes with an explicit type annotation MUST match one of the annotations in this list. Nodes with no type annotations are not checked. Defaults to [], which means type annotations are ignored.

Example

Expand
instance DecodeNode Person where
  validNodeTypeAnns _ = ["person"]

nodeDecoder :: NodeDecoder a Source #

Decode a Node to a value of type a

Instances

Instances details
DecodeNode Node Source # 
Instance details

Defined in KDL.Decoder.Arrow

arg :: DecodeValue a => NodeDecoder a Source #

Decode an argument in the node.

Example

Expand
let
  config =
    """
    person "Alice" 1 2 3
    """
  decoder = KDL.document $ proc () -> do
    KDL.nodeWith "person" $ decodePerson -< ()
  decodePerson = proc () -> do
    name <- KDL.arg -< ()
    vals <- KDL.many KDL.arg -< ()
    returnA -< (name, vals)
KDL.decodeWith decoder config == Right ("Alice", [1, 2, 3])

prop :: DecodeValue a => Text -> NodeDecoder a Source #

Decode the property with the given name in the node.

If the property appears multiple times, the last value is returned, as defined in the spec.

Example

Expand
let
  config =
    """
    my-node a=1 b=2 a=3
    """
  decoder = KDL.document $ proc () -> do
    KDL.nodeWith "my-node" $ KDL.prop @Int "a" -< ()
KDL.decodeWith decoder config == Right 3

remainingProps :: DecodeValue a => NodeDecoder (Map Text a) Source #

Decode all remaining props.

Example

Expand
let
  config =
    """
    my-node a=1 b=2 a=3
    """
  decoder = KDL.document $ proc () -> do
    KDL.nodeWith "my-node" $ KDL.remainingProps @Int -< ()
KDL.decodeWith decoder config == (Right . Map.fromList) [("a", 3), ("b", 2)]

children :: NodeListDecodeArrow a b -> NodeDecodeArrow a b Source #

Decode the children of the node.

Example

Expand
let
  config =
    """
    person "Alice" {
      email "alice@example.com"
    }
    """
  decoder = KDL.document $ proc () -> do
    KDL.nodeWith "person" decodePerson -< ()
  decodePerson = proc () -> do
    name <- KDL.arg -< ()
    email <- KDL.children $ KDL.argAt "email" -< ()
    returnA -< Person{..}
KDL.decodeWith decoder config == Right Person{name = "Alice", email = "alice@example.com"}

argWith :: forall a b. Typeable b => ValueDecodeArrow a b -> NodeDecodeArrow a b Source #

Same as arg, except explicitly specify the ValueDecoder instead of using DecodeValue

Example

Expand
let
  config =
    """
    person "Alice" 1 2 3
    """
  decoder = KDL.document $ proc () -> do
    KDL.nodeWith "person" $ decodePerson -< ()
  decodePerson = proc () -> do
    name <- KDL.argWith $ Text.toUpper <$> KDL.string -< ()
    vals <- KDL.many $ KDL.argWith $ show <$> KDL.valueDecoder @Int -< ()
    returnA -< (name, vals)
KDL.decodeWith decoder config == Right ("ALICE", ["1", "2", "3"])

propWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeDecodeArrow a b Source #

Same as prop, except explicitly specify the ValueDecoder instead of using DecodeValue

Example

Expand
let
  config =
    """
    my-node a=1 b=2 a=3
    """
  decoder = KDL.document $ proc () -> do
    KDL.nodeWith "my-node" $ KDL.propWith "a" $ show <$> KDL.number -< ()
KDL.decodeWith decoder config == Right "3.0"

remainingPropsWith :: forall a b. Typeable b => ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b) Source #

Same as remainingProps, except explicitly specify the ValueDecoder instead of using DecodeValue

Example

Expand
let
  config =
    """
    my-node a=1 b=2 a=3
    """
  decoder = KDL.document $ proc () -> do
    KDL.nodeWith "my-node" $ KDL.remainingPropsWith $ show <$> KDL.number -< ()
KDL.decodeWith decoder config == (Right . Map.fromList) [("a", "3.0"), ("b", "2.0")]

argWith' :: forall a b. Typeable b => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b Source #

Same as argWith, except allow specifying type annotations.

propWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b Source #

Same as propWith, except allow specifying type annotations.

remainingPropsWith' :: forall a b. Typeable b => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b) Source #

Same as remainingPropsWith, except allow specifying type annotations.

class Typeable a => DecodeValue a where Source #

The type class for specifying how a type should be decoded from a KDL value.

Minimal complete definition

valueDecoder

Methods

validValueTypeAnns :: Proxy a -> [Text] Source #

Allowed type annotations for a value of this type.

If specified, values with an explicit type annotation MUST match one of the annotations in this list. Nodes with no type annotations are not checked. Defaults to [], which means type annotations are ignored.

Example

Expand
instance DecodeValue Age where
  validValueTypeAnns _ = ["age"]

valueDecoder :: ValueDecoder a Source #

Decode a Value to a value of type a

Helpers that may be useful:

Instances

Instances details
DecodeValue Int16 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Int32 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Int64 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Int8 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Rational Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Word16 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Word32 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Word64 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Word8 Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Value Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue ValueData Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Scientific Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Text Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue String Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Integer Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Natural Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Bool Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Double Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Float Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Int Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue Word Source # 
Instance details

Defined in KDL.Decoder.Arrow

DecodeValue a => DecodeValue (Maybe a) Source # 
Instance details

Defined in KDL.Decoder.Arrow

(DecodeValue a, DecodeValue b) => DecodeValue (Either a b) Source # 
Instance details

Defined in KDL.Decoder.Arrow

number :: DecodeArrow Value a Scientific Source #

Decode a KDL number value.