packages feed

kdl-hs 0.1.0 → 0.2.0

raw patch · 40 files changed

+5391/−5922 lines, 40 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.KDL.Decoder.Arrow: DecodeState :: !o -> DecodeHistory o -> DecodeState o
- Data.KDL.Decoder.Arrow: Decoder :: SchemaOf o -> (a -> DecodeStateM o b) -> Decoder o a b
- Data.KDL.Decoder.Arrow: UnsafeDocumentDecoder :: NodeListDecoder () a -> DocumentDecoder a
- Data.KDL.Decoder.Arrow: [history] :: DecodeState o -> DecodeHistory o
- Data.KDL.Decoder.Arrow: [object] :: DecodeState o -> !o
- Data.KDL.Decoder.Arrow: [run] :: Decoder o a b -> a -> DecodeStateM o b
- Data.KDL.Decoder.Arrow: [schema] :: Decoder o a b -> SchemaOf o
- Data.KDL.Decoder.Arrow: any :: ValueDecoder a Value
- Data.KDL.Decoder.Arrow: arg :: DecodeValue a => NodeDecoder () a
- Data.KDL.Decoder.Arrow: argAt :: DecodeValue a => Text -> NodeListDecoder () a
- Data.KDL.Decoder.Arrow: argAtWith :: forall a b. Typeable b => Text -> ValueDecoder a b -> NodeListDecoder a b
- Data.KDL.Decoder.Arrow: argAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecoder a b -> NodeListDecoder a b
- Data.KDL.Decoder.Arrow: argWith :: forall a b. Typeable b => ValueDecoder a b -> NodeDecoder a b
- Data.KDL.Decoder.Arrow: argWith' :: forall a b. Typeable b => [Text] -> ValueDecoder a b -> NodeDecoder a b
- Data.KDL.Decoder.Arrow: argsAt :: DecodeValue a => Text -> NodeListDecoder () [a]
- Data.KDL.Decoder.Arrow: argsAtWith :: forall a b. Typeable b => Text -> ValueDecoder a b -> NodeListDecoder a [b]
- Data.KDL.Decoder.Arrow: argsAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecoder a b -> NodeListDecoder a [b]
- Data.KDL.Decoder.Arrow: bool :: ValueDecoder a Bool
- Data.KDL.Decoder.Arrow: children :: NodeListDecoder a b -> NodeDecoder a b
- Data.KDL.Decoder.Arrow: class Typeable a => DecodeNode a
- Data.KDL.Decoder.Arrow: class Typeable a => DecodeValue a
- Data.KDL.Decoder.Arrow: class HasDecodeHistory (o :: k) where {
- Data.KDL.Decoder.Arrow: dashChildrenAt :: DecodeValue a => Text -> NodeListDecoder () [a]
- Data.KDL.Decoder.Arrow: dashChildrenAtWith :: forall a b. Typeable b => Text -> ValueDecoder a b -> NodeListDecoder a [b]
- Data.KDL.Decoder.Arrow: dashChildrenAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecoder a b -> NodeListDecoder a [b]
- Data.KDL.Decoder.Arrow: dashNodesAt :: DecodeNode a => Text -> NodeListDecoder () [a]
- Data.KDL.Decoder.Arrow: dashNodesAtWith :: forall a b. Typeable b => Text -> NodeDecoder a b -> NodeListDecoder a [b]
- Data.KDL.Decoder.Arrow: data DecodeHistory (o :: k);
- Data.KDL.Decoder.Arrow: data DecodeState o
- Data.KDL.Decoder.Arrow: data Decoder o a b
- Data.KDL.Decoder.Arrow: debug :: Show o => Decoder o a ()
- Data.KDL.Decoder.Arrow: decodeDocWith :: DocumentDecoder a -> Document -> Either DecodeError a
- Data.KDL.Decoder.Arrow: decodeFileWith :: DocumentDecoder a -> FilePath -> IO (Either DecodeError a)
- Data.KDL.Decoder.Arrow: decodeWith :: DocumentDecoder a -> Text -> Either DecodeError a
- Data.KDL.Decoder.Arrow: document :: NodeListDecoder () a -> DocumentDecoder a
- Data.KDL.Decoder.Arrow: documentSchema :: DocumentDecoder a -> SchemaOf NodeList
- Data.KDL.Decoder.Arrow: emptyDecodeHistory :: HasDecodeHistory o => DecodeHistory o
- Data.KDL.Decoder.Arrow: fail :: forall b o. Decoder o Text b
- Data.KDL.Decoder.Arrow: instance (Data.KDL.Decoder.Arrow.DecodeValue a, Data.KDL.Decoder.Arrow.DecodeValue b) => Data.KDL.Decoder.Arrow.DecodeValue (Data.Either.Either a b)
- Data.KDL.Decoder.Arrow: instance Control.Arrow.Arrow (Data.KDL.Decoder.Arrow.Decoder o)
- Data.KDL.Decoder.Arrow: instance Control.Arrow.ArrowChoice (Data.KDL.Decoder.Arrow.Decoder o)
- Data.KDL.Decoder.Arrow: instance Control.Category.Category (Data.KDL.Decoder.Arrow.Decoder o)
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeNode Data.KDL.Types.Node
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue Data.KDL.Types.Value
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue Data.KDL.Types.ValueData
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue Data.Scientific.Scientific
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue Data.Text.Internal.Text
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Base.String
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Int.Int16
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Int.Int32
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Int.Int64
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Int.Int8
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Num.Integer.Integer
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Num.Natural.Natural
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Real.Rational
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Types.Bool
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Types.Double
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Types.Float
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Types.Int
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Types.Word
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Word.Word16
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Word.Word32
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Word.Word64
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue GHC.Word.Word8
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.DecodeValue a => Data.KDL.Decoder.Arrow.DecodeValue (GHC.Maybe.Maybe a)
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.HasDecodeHistory Data.KDL.Types.Node
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.HasDecodeHistory Data.KDL.Types.NodeList
- Data.KDL.Decoder.Arrow: instance Data.KDL.Decoder.Arrow.HasDecodeHistory Data.KDL.Types.Value
- Data.KDL.Decoder.Arrow: instance GHC.Base.Alternative (Data.KDL.Decoder.Arrow.Decoder o a)
- Data.KDL.Decoder.Arrow: instance GHC.Base.Applicative (Data.KDL.Decoder.Arrow.Decoder o a)
- Data.KDL.Decoder.Arrow: instance GHC.Base.Functor (Data.KDL.Decoder.Arrow.Decoder o a)
- Data.KDL.Decoder.Arrow: many :: Alternative f => f a -> f [a]
- Data.KDL.Decoder.Arrow: newtype DocumentDecoder a
- Data.KDL.Decoder.Arrow: node :: DecodeNode a => Text -> NodeListDecoder () a
- Data.KDL.Decoder.Arrow: nodeDecoder :: DecodeNode a => NodeDecoder () a
- Data.KDL.Decoder.Arrow: nodeWith :: forall a b. Typeable b => Text -> NodeDecoder a b -> NodeListDecoder a b
- Data.KDL.Decoder.Arrow: nodeWith' :: forall a b. Typeable b => Text -> [Text] -> NodeDecoder a b -> NodeListDecoder a b
- Data.KDL.Decoder.Arrow: null :: ValueDecoder a ()
- Data.KDL.Decoder.Arrow: number :: ValueDecoder a Scientific
- Data.KDL.Decoder.Arrow: oneOf :: Alternative f => [f a] -> f a
- Data.KDL.Decoder.Arrow: option :: Alternative f => a -> f a -> f a
- Data.KDL.Decoder.Arrow: optional :: Alternative f => f a -> f (Maybe a)
- Data.KDL.Decoder.Arrow: prop :: DecodeValue a => Text -> NodeDecoder () a
- Data.KDL.Decoder.Arrow: propWith :: forall a b. Typeable b => Text -> ValueDecoder a b -> NodeDecoder a b
- Data.KDL.Decoder.Arrow: propWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecoder a b -> NodeDecoder a b
- Data.KDL.Decoder.Arrow: remainingNodes :: DecodeNode a => NodeListDecoder () (Map Text [a])
- Data.KDL.Decoder.Arrow: remainingNodesWith :: forall a b. Typeable b => NodeDecoder a b -> NodeListDecoder a (Map Text [b])
- Data.KDL.Decoder.Arrow: remainingNodesWith' :: forall a b. Typeable b => [Text] -> NodeDecoder a b -> NodeListDecoder a (Map Text [b])
- Data.KDL.Decoder.Arrow: remainingProps :: DecodeValue a => NodeDecoder () (Map Text a)
- Data.KDL.Decoder.Arrow: remainingPropsWith :: forall a b. Typeable b => ValueDecoder a b -> NodeDecoder a (Map Text b)
- Data.KDL.Decoder.Arrow: remainingPropsWith' :: forall a b. Typeable b => [Text] -> ValueDecoder a b -> NodeDecoder a (Map Text b)
- Data.KDL.Decoder.Arrow: runDecodeStateM :: o -> DecodeHistory o -> DecodeStateM o a -> DecodeM a
- Data.KDL.Decoder.Arrow: some :: Alternative f => f a -> f [a]
- Data.KDL.Decoder.Arrow: text :: ValueDecoder a Text
- Data.KDL.Decoder.Arrow: type DecodeStateM o a = StateT DecodeState o DecodeM a
- Data.KDL.Decoder.Arrow: type NodeDecoder = Decoder Node
- Data.KDL.Decoder.Arrow: type NodeListDecoder = Decoder NodeList
- Data.KDL.Decoder.Arrow: type ValueDecoder = Decoder Value
- Data.KDL.Decoder.Arrow: validNodeTypeAnns :: DecodeNode a => Proxy a -> [Text]
- Data.KDL.Decoder.Arrow: validValueTypeAnns :: DecodeValue a => Proxy a -> [Text]
- Data.KDL.Decoder.Arrow: valueDecoder :: DecodeValue a => ValueDecoder () a
- Data.KDL.Decoder.Arrow: withDecoder :: Decoder o a b -> (b -> DecodeM c) -> Decoder o a c
- Data.KDL.Decoder.Arrow: }
- Data.KDL.Decoder.DecodeM: DecodeM :: (forall r. () => (DecodeError -> r) -> (DecodeError -> r) -> (a -> r) -> r) -> DecodeM a
- Data.KDL.Decoder.DecodeM: addContext :: ContextItem -> DecodeM a -> DecodeM a
- Data.KDL.Decoder.DecodeM: data DecodeM a
- Data.KDL.Decoder.DecodeM: decodeThrow :: BaseDecodeError -> DecodeM a
- Data.KDL.Decoder.DecodeM: failM :: Text -> DecodeM a
- Data.KDL.Decoder.DecodeM: instance GHC.Base.Alternative Data.KDL.Decoder.DecodeM.DecodeM
- Data.KDL.Decoder.DecodeM: instance GHC.Base.Applicative Data.KDL.Decoder.DecodeM.DecodeM
- Data.KDL.Decoder.DecodeM: instance GHC.Base.Functor Data.KDL.Decoder.DecodeM.DecodeM
- Data.KDL.Decoder.DecodeM: instance GHC.Base.Monad Data.KDL.Decoder.DecodeM.DecodeM
- Data.KDL.Decoder.DecodeM: makeFatal :: DecodeM a -> DecodeM a
- Data.KDL.Decoder.DecodeM: makeNonFatal :: DecodeM a -> DecodeM a
- Data.KDL.Decoder.DecodeM: runDecodeM :: DecodeM a -> Either DecodeError a
- Data.KDL.Decoder.Error: ContextArg :: Int -> ContextItem
- Data.KDL.Decoder.Error: ContextNode :: Identifier -> Int -> ContextItem
- Data.KDL.Decoder.Error: ContextProp :: Identifier -> ContextItem
- Data.KDL.Decoder.Error: DecodeError :: [(Context, BaseDecodeError)] -> DecodeError
- Data.KDL.Decoder.Error: DecodeError_Custom :: Text -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_ExpectedArg :: Int -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_ExpectedNode :: Text -> Int -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_ExpectedProp :: Text -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_MismatchedAnn :: Identifier -> [Text] -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_ParseError :: Text -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_UnexpectedArg :: Int -> Value -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_UnexpectedNode :: Identifier -> Int -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_UnexpectedProp :: Identifier -> Value -> BaseDecodeError
- Data.KDL.Decoder.Error: DecodeError_ValueDecodeFail :: Text -> Value -> BaseDecodeError
- Data.KDL.Decoder.Error: [expectedType] :: BaseDecodeError -> Text
- Data.KDL.Decoder.Error: [givenAnn] :: BaseDecodeError -> Identifier
- Data.KDL.Decoder.Error: [identifier] :: BaseDecodeError -> Identifier
- Data.KDL.Decoder.Error: [index] :: ContextItem -> Int
- Data.KDL.Decoder.Error: [name] :: ContextItem -> Identifier
- Data.KDL.Decoder.Error: [validAnns] :: BaseDecodeError -> [Text]
- Data.KDL.Decoder.Error: [value] :: BaseDecodeError -> Value
- Data.KDL.Decoder.Error: data BaseDecodeError
- Data.KDL.Decoder.Error: data ContextItem
- Data.KDL.Decoder.Error: data DecodeError
- Data.KDL.Decoder.Error: instance GHC.Base.Monoid Data.KDL.Decoder.Error.DecodeError
- Data.KDL.Decoder.Error: instance GHC.Base.Semigroup Data.KDL.Decoder.Error.DecodeError
- Data.KDL.Decoder.Error: instance GHC.Classes.Eq Data.KDL.Decoder.Error.BaseDecodeError
- Data.KDL.Decoder.Error: instance GHC.Classes.Eq Data.KDL.Decoder.Error.ContextItem
- Data.KDL.Decoder.Error: instance GHC.Classes.Eq Data.KDL.Decoder.Error.DecodeError
- Data.KDL.Decoder.Error: instance GHC.Classes.Ord Data.KDL.Decoder.Error.ContextItem
- Data.KDL.Decoder.Error: instance GHC.Show.Show Data.KDL.Decoder.Error.BaseDecodeError
- Data.KDL.Decoder.Error: instance GHC.Show.Show Data.KDL.Decoder.Error.ContextItem
- Data.KDL.Decoder.Error: instance GHC.Show.Show Data.KDL.Decoder.Error.DecodeError
- Data.KDL.Decoder.Error: renderDecodeError :: DecodeError -> Text
- Data.KDL.Decoder.Error: type Context = [ContextItem]
- Data.KDL.Decoder.Monad: any :: ValueDecoder Value
- Data.KDL.Decoder.Monad: arg :: DecodeValue a => NodeDecoder a
- Data.KDL.Decoder.Monad: argAt :: DecodeValue a => Text -> NodeListDecoder a
- Data.KDL.Decoder.Monad: argAtWith :: Typeable a => Text -> ValueDecoder a -> NodeListDecoder a
- Data.KDL.Decoder.Monad: argAtWith' :: Typeable a => Text -> [Text] -> ValueDecoder a -> NodeListDecoder a
- Data.KDL.Decoder.Monad: argWith :: Typeable a => ValueDecoder a -> NodeDecoder a
- Data.KDL.Decoder.Monad: argWith' :: Typeable a => [Text] -> ValueDecoder a -> NodeDecoder a
- Data.KDL.Decoder.Monad: argsAt :: DecodeValue a => Text -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: argsAtWith :: Typeable a => Text -> ValueDecoder a -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: argsAtWith' :: Typeable a => Text -> [Text] -> ValueDecoder a -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: bool :: ValueDecoder Bool
- Data.KDL.Decoder.Monad: children :: NodeListDecoder a -> NodeDecoder a
- Data.KDL.Decoder.Monad: class Typeable a => DecodeNode a
- Data.KDL.Decoder.Monad: class Typeable a => DecodeValue a
- Data.KDL.Decoder.Monad: dashChildrenAt :: DecodeValue a => Text -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: dashChildrenAtWith :: Typeable a => Text -> ValueDecoder a -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: dashChildrenAtWith' :: Typeable a => Text -> [Text] -> ValueDecoder a -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: dashNodesAt :: DecodeNode a => Text -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: dashNodesAtWith :: Typeable a => Text -> NodeDecoder a -> NodeListDecoder [a]
- Data.KDL.Decoder.Monad: data Decoder o a
- Data.KDL.Decoder.Monad: data DocumentDecoder a
- Data.KDL.Decoder.Monad: debug :: Show o => Decoder o ()
- Data.KDL.Decoder.Monad: decodeDocWith :: DocumentDecoder a -> Document -> Either DecodeError a
- Data.KDL.Decoder.Monad: decodeFileWith :: DocumentDecoder a -> FilePath -> IO (Either DecodeError a)
- Data.KDL.Decoder.Monad: decodeWith :: DocumentDecoder a -> Text -> Either DecodeError a
- Data.KDL.Decoder.Monad: document :: NodeListDecoder a -> DocumentDecoder a
- Data.KDL.Decoder.Monad: fail :: forall a o. Text -> Decoder o a
- Data.KDL.Decoder.Monad: instance GHC.Base.Alternative (Data.KDL.Decoder.Monad.Decoder o)
- Data.KDL.Decoder.Monad: instance GHC.Base.Applicative (Data.KDL.Decoder.Monad.Decoder o)
- Data.KDL.Decoder.Monad: instance GHC.Base.Functor (Data.KDL.Decoder.Monad.Decoder o)
- Data.KDL.Decoder.Monad: instance GHC.Base.Monad (Data.KDL.Decoder.Monad.Decoder o)
- Data.KDL.Decoder.Monad: many :: Alternative f => f a -> f [a]
- Data.KDL.Decoder.Monad: noSchema :: Decoder o a -> Decoder o () a
- Data.KDL.Decoder.Monad: node :: DecodeNode a => Text -> NodeListDecoder a
- Data.KDL.Decoder.Monad: nodeDecoder :: DecodeNode a => NodeDecoder () a
- Data.KDL.Decoder.Monad: nodeWith :: Typeable a => Text -> NodeDecoder a -> NodeListDecoder a
- Data.KDL.Decoder.Monad: nodeWith' :: Typeable a => Text -> [Text] -> NodeDecoder a -> NodeListDecoder a
- Data.KDL.Decoder.Monad: null :: ValueDecoder ()
- Data.KDL.Decoder.Monad: number :: ValueDecoder Scientific
- Data.KDL.Decoder.Monad: oneOf :: Alternative f => [f a] -> f a
- Data.KDL.Decoder.Monad: option :: Alternative f => a -> f a -> f a
- Data.KDL.Decoder.Monad: optional :: Alternative f => f a -> f (Maybe a)
- Data.KDL.Decoder.Monad: prop :: DecodeValue a => Text -> NodeDecoder a
- Data.KDL.Decoder.Monad: propWith :: Typeable a => Text -> ValueDecoder a -> NodeDecoder a
- Data.KDL.Decoder.Monad: propWith' :: Typeable a => Text -> [Text] -> ValueDecoder a -> NodeDecoder a
- Data.KDL.Decoder.Monad: remainingNodes :: DecodeNode a => NodeListDecoder (Map Text [a])
- Data.KDL.Decoder.Monad: remainingNodesWith :: Typeable a => NodeDecoder a -> NodeListDecoder (Map Text [a])
- Data.KDL.Decoder.Monad: remainingNodesWith' :: Typeable a => [Text] -> NodeDecoder a -> NodeListDecoder (Map Text [a])
- Data.KDL.Decoder.Monad: remainingProps :: DecodeValue a => NodeDecoder (Map Text a)
- Data.KDL.Decoder.Monad: remainingPropsWith :: Typeable a => ValueDecoder a -> NodeDecoder (Map Text a)
- Data.KDL.Decoder.Monad: remainingPropsWith' :: Typeable a => [Text] -> ValueDecoder a -> NodeDecoder (Map Text a)
- Data.KDL.Decoder.Monad: some :: Alternative f => f a -> f [a]
- Data.KDL.Decoder.Monad: text :: ValueDecoder Text
- Data.KDL.Decoder.Monad: type NodeDecoder a = Decoder Node a
- Data.KDL.Decoder.Monad: type NodeListDecoder = Decoder NodeList
- Data.KDL.Decoder.Monad: type ValueDecoder a = Decoder Value a
- Data.KDL.Decoder.Monad: validNodeTypeAnns :: DecodeNode a => Proxy a -> [Text]
- Data.KDL.Decoder.Monad: validValueTypeAnns :: DecodeValue a => Proxy a -> [Text]
- Data.KDL.Decoder.Monad: valueDecoder :: DecodeValue a => ValueDecoder () a
- Data.KDL.Decoder.Monad: withDecoder :: Decoder o a -> (a -> DecodeM b) -> Decoder o b
- Data.KDL.Decoder.Schema: SchemaAnd :: [Schema a] -> Schema a
- Data.KDL.Decoder.Schema: SchemaOne :: a -> Schema a
- Data.KDL.Decoder.Schema: SchemaOr :: [Schema a] -> Schema a
- Data.KDL.Decoder.Schema: SchemaSome :: Schema a -> Schema a
- Data.KDL.Decoder.Schema: SchemaUnknown :: Schema a
- Data.KDL.Decoder.Schema: TypedNodeSchema :: TypeRep -> [Text] -> SchemaOf Node -> TypedNodeSchema
- Data.KDL.Decoder.Schema: TypedValueSchema :: TypeRep -> [Text] -> SchemaOf Value -> TypedValueSchema
- Data.KDL.Decoder.Schema: [dataSchema] :: TypedValueSchema -> SchemaOf Value
- Data.KDL.Decoder.Schema: [nodeSchema] :: TypedNodeSchema -> SchemaOf Node
- Data.KDL.Decoder.Schema: [typeHint] :: TypedValueSchema -> TypeRep
- Data.KDL.Decoder.Schema: [validTypeAnns] :: TypedValueSchema -> [Text]
- Data.KDL.Decoder.Schema: data Schema a
- Data.KDL.Decoder.Schema: data TypedNodeSchema
- Data.KDL.Decoder.Schema: data TypedValueSchema
- Data.KDL.Decoder.Schema: data family SchemaItem a
- Data.KDL.Decoder.Schema: instance GHC.Classes.Eq (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.Node)
- Data.KDL.Decoder.Schema: instance GHC.Classes.Eq (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.NodeList)
- Data.KDL.Decoder.Schema: instance GHC.Classes.Eq (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.Value)
- Data.KDL.Decoder.Schema: instance GHC.Classes.Eq Data.KDL.Decoder.Schema.TypedNodeSchema
- Data.KDL.Decoder.Schema: instance GHC.Classes.Eq Data.KDL.Decoder.Schema.TypedValueSchema
- Data.KDL.Decoder.Schema: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.KDL.Decoder.Schema.Schema a)
- Data.KDL.Decoder.Schema: instance GHC.Classes.Ord (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.Value)
- Data.KDL.Decoder.Schema: instance GHC.Enum.Bounded (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.Value)
- Data.KDL.Decoder.Schema: instance GHC.Enum.Enum (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.Value)
- Data.KDL.Decoder.Schema: instance GHC.Show.Show (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.Node)
- Data.KDL.Decoder.Schema: instance GHC.Show.Show (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.NodeList)
- Data.KDL.Decoder.Schema: instance GHC.Show.Show (Data.KDL.Decoder.Schema.SchemaItem Data.KDL.Types.Value)
- Data.KDL.Decoder.Schema: instance GHC.Show.Show Data.KDL.Decoder.Schema.TypedNodeSchema
- Data.KDL.Decoder.Schema: instance GHC.Show.Show Data.KDL.Decoder.Schema.TypedValueSchema
- Data.KDL.Decoder.Schema: instance GHC.Show.Show a => GHC.Show.Show (Data.KDL.Decoder.Schema.Schema a)
- Data.KDL.Decoder.Schema: schemaAlt :: Schema a -> Schema a -> Schema a
- Data.KDL.Decoder.Schema: schemaJoin :: Schema a -> Schema a -> Schema a
- Data.KDL.Decoder.Schema: type SchemaOf o = Schema SchemaItem o
- Data.KDL.Parser: parse :: Text -> Either Text Document
- Data.KDL.Parser: parseFile :: FilePath -> IO (Either Text Document)
- Data.KDL.Render: render :: Document -> Text
- Data.KDL.Render: renderAnn :: Ann -> Text
- Data.KDL.Render: renderIdentifier :: Identifier -> Text
- Data.KDL.Render: renderValue :: Value -> Text
- Data.KDL.Render: renderValueData :: ValueData -> Text
- Data.KDL.Types: Ann :: Identifier -> Maybe AnnFormat -> Ann
- Data.KDL.Types: AnnFormat :: Text -> Text -> Text -> Text -> AnnFormat
- Data.KDL.Types: Bool :: Bool -> ValueData
- Data.KDL.Types: Entry :: Maybe Identifier -> Value -> Maybe EntryFormat -> Entry
- Data.KDL.Types: EntryFormat :: Text -> Text -> Text -> Text -> EntryFormat
- Data.KDL.Types: Identifier :: Text -> Maybe IdentifierFormat -> Identifier
- Data.KDL.Types: IdentifierFormat :: Text -> IdentifierFormat
- Data.KDL.Types: Node :: Maybe Ann -> Identifier -> [Entry] -> Maybe NodeList -> Maybe NodeFormat -> Node
- Data.KDL.Types: NodeFormat :: Text -> Text -> Text -> Text -> Text -> NodeFormat
- Data.KDL.Types: NodeList :: [Node] -> Maybe NodeListFormat -> NodeList
- Data.KDL.Types: NodeListFormat :: Text -> Text -> NodeListFormat
- Data.KDL.Types: Null :: ValueData
- Data.KDL.Types: Number :: Scientific -> ValueData
- Data.KDL.Types: Text :: Text -> ValueData
- Data.KDL.Types: Value :: Maybe Ann -> ValueData -> Maybe ValueFormat -> Value
- Data.KDL.Types: ValueFormat :: Text -> ValueFormat
- Data.KDL.Types: [after_eq] :: EntryFormat -> Text
- Data.KDL.Types: [after_id] :: AnnFormat -> Text
- Data.KDL.Types: [after_key] :: EntryFormat -> Text
- Data.KDL.Types: [ann] :: Value -> Maybe Ann
- Data.KDL.Types: [before_children] :: NodeFormat -> Text
- Data.KDL.Types: [before_id] :: AnnFormat -> Text
- Data.KDL.Types: [before_terminator] :: NodeFormat -> Text
- Data.KDL.Types: [children] :: Node -> Maybe NodeList
- Data.KDL.Types: [data_] :: Value -> ValueData
- Data.KDL.Types: [entries] :: Node -> [Entry]
- Data.KDL.Types: [format] :: Identifier -> Maybe IdentifierFormat
- Data.KDL.Types: [identifier] :: Ann -> Identifier
- Data.KDL.Types: [leading] :: AnnFormat -> Text
- Data.KDL.Types: [name] :: Entry -> Maybe Identifier
- Data.KDL.Types: [nodes] :: NodeList -> [Node]
- Data.KDL.Types: [repr] :: IdentifierFormat -> Text
- Data.KDL.Types: [terminator] :: NodeFormat -> Text
- Data.KDL.Types: [trailing] :: AnnFormat -> Text
- Data.KDL.Types: [value] :: Identifier -> Text
- Data.KDL.Types: annFormat :: Ann -> Maybe AnnFormat
- Data.KDL.Types: annIdentifier :: Ann -> Identifier
- Data.KDL.Types: data Ann
- Data.KDL.Types: data AnnFormat
- Data.KDL.Types: data Entry
- Data.KDL.Types: data EntryFormat
- Data.KDL.Types: data Identifier
- Data.KDL.Types: data IdentifierFormat
- Data.KDL.Types: data Node
- Data.KDL.Types: data NodeFormat
- Data.KDL.Types: data NodeList
- Data.KDL.Types: data NodeListFormat
- Data.KDL.Types: data Value
- Data.KDL.Types: data ValueData
- Data.KDL.Types: data ValueFormat
- Data.KDL.Types: docNodes :: Document -> [Node]
- Data.KDL.Types: entryFormat :: Entry -> Maybe EntryFormat
- Data.KDL.Types: entryName :: Entry -> Maybe Identifier
- Data.KDL.Types: entryValue :: Entry -> Value
- Data.KDL.Types: filterNodes :: Text -> NodeList -> [Node]
- Data.KDL.Types: fromIdentifier :: Identifier -> Text
- Data.KDL.Types: fromNodeList :: NodeList -> [Node]
- Data.KDL.Types: getArg :: Node -> Maybe Value
- Data.KDL.Types: getArgAt :: Text -> NodeList -> Maybe Value
- Data.KDL.Types: getArgs :: Node -> [Value]
- Data.KDL.Types: getArgsAt :: Text -> NodeList -> [Value]
- Data.KDL.Types: getDashChildrenAt :: Text -> NodeList -> [Value]
- Data.KDL.Types: getDashNodesAt :: Text -> NodeList -> [Node]
- Data.KDL.Types: getProp :: Text -> Node -> Maybe Value
- Data.KDL.Types: getProps :: Node -> Map Text Value
- Data.KDL.Types: identifierFormat :: Identifier -> Maybe IdentifierFormat
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.Ann
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.AnnFormat
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.Entry
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.EntryFormat
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.Identifier
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.IdentifierFormat
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.Node
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.NodeFormat
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.NodeList
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.NodeListFormat
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.Value
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.ValueData
- Data.KDL.Types: instance GHC.Classes.Eq Data.KDL.Types.ValueFormat
- Data.KDL.Types: instance GHC.Classes.Ord Data.KDL.Types.Identifier
- Data.KDL.Types: instance GHC.Classes.Ord Data.KDL.Types.IdentifierFormat
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.Ann
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.AnnFormat
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.Entry
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.EntryFormat
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.Identifier
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.IdentifierFormat
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.Node
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.NodeFormat
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.NodeList
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.NodeListFormat
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.Value
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.ValueData
- Data.KDL.Types: instance GHC.Show.Show Data.KDL.Types.ValueFormat
- Data.KDL.Types: lookupNode :: Text -> NodeList -> Maybe Node
- Data.KDL.Types: nodeAnn :: Node -> Maybe Ann
- Data.KDL.Types: nodeChildren :: Node -> Maybe NodeList
- Data.KDL.Types: nodeEntries :: Node -> [Entry]
- Data.KDL.Types: nodeFormat :: Node -> Maybe NodeFormat
- Data.KDL.Types: nodeListFormat :: NodeList -> Maybe NodeListFormat
- Data.KDL.Types: nodeName :: Node -> Identifier
- Data.KDL.Types: toIdentifier :: Text -> Identifier
- Data.KDL.Types: type Document = NodeList
- Data.KDL.Types: valueAnn :: Value -> Maybe Ann
- Data.KDL.Types: valueData :: Value -> ValueData
- Data.KDL.Types: valueFormat :: Value -> Maybe ValueFormat
+ KDL.Decoder.Arrow: UnsafeDocumentDecoder :: NodeListDecoder a -> DocumentDecoder a
+ KDL.Decoder.Arrow: any :: DecodeArrow Value a Value
+ KDL.Decoder.Arrow: arg :: DecodeValue a => NodeDecoder a
+ KDL.Decoder.Arrow: argAt :: DecodeValue a => Text -> NodeListDecoder a
+ KDL.Decoder.Arrow: argAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Arrow: argAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Arrow: argWith :: forall a b. Typeable b => ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Arrow: argWith' :: forall a b. Typeable b => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Arrow: argsAt :: DecodeValue a => Text -> NodeListDecoder [a]
+ KDL.Decoder.Arrow: argsAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Arrow: argsAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Arrow: bool :: DecodeArrow Value a Bool
+ KDL.Decoder.Arrow: children :: NodeListDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Arrow: class Typeable a => DecodeNode a
+ KDL.Decoder.Arrow: class Typeable a => DecodeValue a
+ KDL.Decoder.Arrow: dashChildrenAt :: DecodeValue a => Text -> NodeListDecoder [a]
+ KDL.Decoder.Arrow: dashChildrenAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Arrow: dashChildrenAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Arrow: dashNodesAt :: DecodeNode a => Text -> NodeListDecoder [a]
+ KDL.Decoder.Arrow: dashNodesAtWith :: forall a b. Typeable b => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Arrow: debug :: Show o => DecodeArrow o a ()
+ KDL.Decoder.Arrow: decodeDocWith :: DocumentDecoder a -> Document -> Either DecodeError a
+ KDL.Decoder.Arrow: decodeFileWith :: DocumentDecoder a -> FilePath -> IO (Either DecodeError a)
+ KDL.Decoder.Arrow: decodeWith :: DocumentDecoder a -> Text -> Either DecodeError a
+ KDL.Decoder.Arrow: document :: NodeListDecoder a -> DocumentDecoder a
+ KDL.Decoder.Arrow: documentSchema :: DocumentDecoder a -> SchemaOf NodeList
+ KDL.Decoder.Arrow: fail :: forall b o. DecodeArrow o Text b
+ KDL.Decoder.Arrow: instance (KDL.Decoder.Arrow.DecodeValue a, KDL.Decoder.Arrow.DecodeValue b) => KDL.Decoder.Arrow.DecodeValue (Data.Either.Either a b)
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeNode KDL.Types.Node
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue Data.Scientific.Scientific
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue Data.Text.Internal.Text
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Base.String
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Int.Int16
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Int.Int32
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Int.Int64
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Int.Int8
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Num.Integer.Integer
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Num.Natural.Natural
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Real.Rational
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Types.Bool
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Types.Double
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Types.Float
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Types.Int
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Types.Word
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Word.Word16
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Word.Word32
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Word.Word64
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue GHC.Word.Word8
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue KDL.Types.Value
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue KDL.Types.ValueData
+ KDL.Decoder.Arrow: instance KDL.Decoder.Arrow.DecodeValue a => KDL.Decoder.Arrow.DecodeValue (GHC.Maybe.Maybe a)
+ KDL.Decoder.Arrow: many :: Alternative f => f a -> f [a]
+ KDL.Decoder.Arrow: newtype DocumentDecoder a
+ KDL.Decoder.Arrow: node :: DecodeNode a => Text -> NodeListDecoder a
+ KDL.Decoder.Arrow: nodeDecoder :: DecodeNode a => NodeDecoder a
+ KDL.Decoder.Arrow: nodeWith :: forall a b. Typeable b => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Arrow: nodeWith' :: forall a b. Typeable b => Text -> [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Arrow: null :: DecodeArrow Value a ()
+ KDL.Decoder.Arrow: number :: DecodeArrow Value a Scientific
+ KDL.Decoder.Arrow: oneOf :: Alternative f => [f a] -> f a
+ KDL.Decoder.Arrow: option :: Alternative f => a -> f a -> f a
+ KDL.Decoder.Arrow: optional :: Alternative f => f a -> f (Maybe a)
+ KDL.Decoder.Arrow: prop :: DecodeValue a => Text -> NodeDecoder a
+ KDL.Decoder.Arrow: propWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Arrow: propWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Arrow: remainingNodes :: DecodeNode a => NodeListDecoder (Map Text [a])
+ KDL.Decoder.Arrow: remainingNodesWith :: forall a b. Typeable b => NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b])
+ KDL.Decoder.Arrow: remainingNodesWith' :: forall a b. Typeable b => [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b])
+ KDL.Decoder.Arrow: remainingProps :: DecodeValue a => NodeDecoder (Map Text a)
+ KDL.Decoder.Arrow: remainingPropsWith :: forall a b. Typeable b => ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b)
+ KDL.Decoder.Arrow: remainingPropsWith' :: forall a b. Typeable b => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b)
+ KDL.Decoder.Arrow: some :: Alternative f => f a -> f [a]
+ KDL.Decoder.Arrow: text :: DecodeArrow Value a Text
+ KDL.Decoder.Arrow: type Decoder o a = DecodeArrow o () a
+ KDL.Decoder.Arrow: type NodeDecodeArrow a b = DecodeArrow Node a b
+ KDL.Decoder.Arrow: type NodeDecoder a = NodeDecodeArrow () a
+ KDL.Decoder.Arrow: type NodeListDecodeArrow a b = DecodeArrow NodeList a b
+ KDL.Decoder.Arrow: type NodeListDecoder a = NodeListDecodeArrow () a
+ KDL.Decoder.Arrow: type ValueDecodeArrow a b = DecodeArrow Value a b
+ KDL.Decoder.Arrow: type ValueDecoder a = ValueDecodeArrow () a
+ KDL.Decoder.Arrow: validNodeTypeAnns :: DecodeNode a => Proxy a -> [Text]
+ KDL.Decoder.Arrow: validValueTypeAnns :: DecodeValue a => Proxy a -> [Text]
+ KDL.Decoder.Arrow: valueDecoder :: DecodeValue a => ValueDecoder a
+ KDL.Decoder.Arrow: withDecoder :: DecodeArrow o a b -> (b -> DecodeM c) -> DecodeArrow o a c
+ KDL.Decoder.Internal.DecodeM: DecodeM :: (forall r. () => (DecodeError -> r) -> (DecodeError -> r) -> (a -> r) -> r) -> DecodeM a
+ KDL.Decoder.Internal.DecodeM: addContext :: ContextItem -> DecodeM a -> DecodeM a
+ KDL.Decoder.Internal.DecodeM: data DecodeM a
+ KDL.Decoder.Internal.DecodeM: decodeThrow :: BaseDecodeError -> DecodeM a
+ KDL.Decoder.Internal.DecodeM: failM :: Text -> DecodeM a
+ KDL.Decoder.Internal.DecodeM: instance GHC.Base.Alternative KDL.Decoder.Internal.DecodeM.DecodeM
+ KDL.Decoder.Internal.DecodeM: instance GHC.Base.Applicative KDL.Decoder.Internal.DecodeM.DecodeM
+ KDL.Decoder.Internal.DecodeM: instance GHC.Base.Functor KDL.Decoder.Internal.DecodeM.DecodeM
+ KDL.Decoder.Internal.DecodeM: instance GHC.Base.Monad KDL.Decoder.Internal.DecodeM.DecodeM
+ KDL.Decoder.Internal.DecodeM: makeFatal :: DecodeM a -> DecodeM a
+ KDL.Decoder.Internal.DecodeM: makeNonFatal :: DecodeM a -> DecodeM a
+ KDL.Decoder.Internal.DecodeM: runDecodeM :: DecodeM a -> Either DecodeError a
+ KDL.Decoder.Internal.Decoder: DecodeArrow :: SchemaOf o -> (a -> DecodeStateM o b) -> DecodeArrow o a b
+ KDL.Decoder.Internal.Decoder: DecodeState :: !o -> DecodeHistory o -> DecodeState o
+ KDL.Decoder.Internal.Decoder: [history] :: DecodeState o -> DecodeHistory o
+ KDL.Decoder.Internal.Decoder: [object] :: DecodeState o -> !o
+ KDL.Decoder.Internal.Decoder: [run] :: DecodeArrow o a b -> a -> DecodeStateM o b
+ KDL.Decoder.Internal.Decoder: [schema] :: DecodeArrow o a b -> SchemaOf o
+ KDL.Decoder.Internal.Decoder: class HasDecodeHistory (o :: k) where {
+ KDL.Decoder.Internal.Decoder: data DecodeArrow o a b
+ KDL.Decoder.Internal.Decoder: data DecodeHistory (o :: k);
+ KDL.Decoder.Internal.Decoder: data DecodeState o
+ KDL.Decoder.Internal.Decoder: data family DecodeHistory (o :: k)
+ KDL.Decoder.Internal.Decoder: debug :: Show o => DecodeArrow o a ()
+ KDL.Decoder.Internal.Decoder: emptyDecodeHistory :: HasDecodeHistory o => DecodeHistory o
+ KDL.Decoder.Internal.Decoder: fail :: forall b o. DecodeArrow o Text b
+ KDL.Decoder.Internal.Decoder: instance Control.Arrow.Arrow (KDL.Decoder.Internal.Decoder.DecodeArrow o)
+ KDL.Decoder.Internal.Decoder: instance Control.Arrow.ArrowChoice (KDL.Decoder.Internal.Decoder.DecodeArrow o)
+ KDL.Decoder.Internal.Decoder: instance Control.Category.Category (KDL.Decoder.Internal.Decoder.DecodeArrow o)
+ KDL.Decoder.Internal.Decoder: instance GHC.Base.Alternative (KDL.Decoder.Internal.Decoder.DecodeArrow o a)
+ KDL.Decoder.Internal.Decoder: instance GHC.Base.Applicative (KDL.Decoder.Internal.Decoder.DecodeArrow o a)
+ KDL.Decoder.Internal.Decoder: instance GHC.Base.Functor (KDL.Decoder.Internal.Decoder.DecodeArrow o a)
+ KDL.Decoder.Internal.Decoder: instance GHC.Base.Monad (KDL.Decoder.Internal.Decoder.DecodeArrow o a)
+ KDL.Decoder.Internal.Decoder: instance KDL.Decoder.Internal.Decoder.HasDecodeHistory KDL.Types.Node
+ KDL.Decoder.Internal.Decoder: instance KDL.Decoder.Internal.Decoder.HasDecodeHistory KDL.Types.NodeList
+ KDL.Decoder.Internal.Decoder: instance KDL.Decoder.Internal.Decoder.HasDecodeHistory KDL.Types.Value
+ KDL.Decoder.Internal.Decoder: liftDecodeM :: (a -> DecodeM b) -> DecodeArrow o a b
+ KDL.Decoder.Internal.Decoder: runDecodeStateM :: o -> DecodeHistory o -> DecodeStateM o a -> DecodeM a
+ KDL.Decoder.Internal.Decoder: type DecodeStateM o a = StateT DecodeState o DecodeM a
+ KDL.Decoder.Internal.Decoder: type Decoder o a = DecodeArrow o () a
+ KDL.Decoder.Internal.Decoder: withDecoder :: DecodeArrow o a b -> (b -> DecodeM c) -> DecodeArrow o a c
+ KDL.Decoder.Internal.Decoder: }
+ KDL.Decoder.Internal.Error: ContextArg :: Int -> ContextItem
+ KDL.Decoder.Internal.Error: ContextNode :: Identifier -> Int -> ContextItem
+ KDL.Decoder.Internal.Error: ContextProp :: Identifier -> ContextItem
+ KDL.Decoder.Internal.Error: DecodeError :: [(Context, BaseDecodeError)] -> DecodeError
+ KDL.Decoder.Internal.Error: DecodeError_Custom :: Text -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_ExpectedArg :: Int -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_ExpectedNode :: Text -> Int -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_ExpectedProp :: Text -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_MismatchedAnn :: Identifier -> [Text] -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_ParseError :: Text -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_UnexpectedArg :: Int -> Value -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_UnexpectedNode :: Identifier -> Int -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_UnexpectedProp :: Identifier -> Value -> BaseDecodeError
+ KDL.Decoder.Internal.Error: DecodeError_ValueDecodeFail :: Text -> Value -> BaseDecodeError
+ KDL.Decoder.Internal.Error: [expectedType] :: BaseDecodeError -> Text
+ KDL.Decoder.Internal.Error: [givenAnn] :: BaseDecodeError -> Identifier
+ KDL.Decoder.Internal.Error: [identifier] :: BaseDecodeError -> Identifier
+ KDL.Decoder.Internal.Error: [index] :: ContextItem -> Int
+ KDL.Decoder.Internal.Error: [name] :: ContextItem -> Identifier
+ KDL.Decoder.Internal.Error: [validAnns] :: BaseDecodeError -> [Text]
+ KDL.Decoder.Internal.Error: [value] :: BaseDecodeError -> Value
+ KDL.Decoder.Internal.Error: data BaseDecodeError
+ KDL.Decoder.Internal.Error: data ContextItem
+ KDL.Decoder.Internal.Error: data DecodeError
+ KDL.Decoder.Internal.Error: instance GHC.Base.Monoid KDL.Decoder.Internal.Error.DecodeError
+ KDL.Decoder.Internal.Error: instance GHC.Base.Semigroup KDL.Decoder.Internal.Error.DecodeError
+ KDL.Decoder.Internal.Error: instance GHC.Classes.Eq KDL.Decoder.Internal.Error.BaseDecodeError
+ KDL.Decoder.Internal.Error: instance GHC.Classes.Eq KDL.Decoder.Internal.Error.ContextItem
+ KDL.Decoder.Internal.Error: instance GHC.Classes.Eq KDL.Decoder.Internal.Error.DecodeError
+ KDL.Decoder.Internal.Error: instance GHC.Classes.Ord KDL.Decoder.Internal.Error.ContextItem
+ KDL.Decoder.Internal.Error: instance GHC.Show.Show KDL.Decoder.Internal.Error.BaseDecodeError
+ KDL.Decoder.Internal.Error: instance GHC.Show.Show KDL.Decoder.Internal.Error.ContextItem
+ KDL.Decoder.Internal.Error: instance GHC.Show.Show KDL.Decoder.Internal.Error.DecodeError
+ KDL.Decoder.Internal.Error: renderDecodeError :: DecodeError -> Text
+ KDL.Decoder.Internal.Error: type Context = [ContextItem]
+ KDL.Decoder.Monad: ContextArg :: Int -> ContextItem
+ KDL.Decoder.Monad: ContextNode :: Identifier -> Int -> ContextItem
+ KDL.Decoder.Monad: ContextProp :: Identifier -> ContextItem
+ KDL.Decoder.Monad: DecodeError :: [(Context, BaseDecodeError)] -> DecodeError
+ KDL.Decoder.Monad: DecodeError_Custom :: Text -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_ExpectedArg :: Int -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_ExpectedNode :: Text -> Int -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_ExpectedProp :: Text -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_MismatchedAnn :: Identifier -> [Text] -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_ParseError :: Text -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_UnexpectedArg :: Int -> Value -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_UnexpectedNode :: Identifier -> Int -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_UnexpectedProp :: Identifier -> Value -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeError_ValueDecodeFail :: Text -> Value -> BaseDecodeError
+ KDL.Decoder.Monad: DecodeM :: (forall r. () => (DecodeError -> r) -> (DecodeError -> r) -> (a -> r) -> r) -> DecodeM a
+ KDL.Decoder.Monad: UnsafeDocumentDecoder :: NodeListDecoder a -> DocumentDecoder a
+ KDL.Decoder.Monad: [expectedType] :: BaseDecodeError -> Text
+ KDL.Decoder.Monad: [givenAnn] :: BaseDecodeError -> Identifier
+ KDL.Decoder.Monad: [identifier] :: BaseDecodeError -> Identifier
+ KDL.Decoder.Monad: [index] :: BaseDecodeError -> Int
+ KDL.Decoder.Monad: [name] :: BaseDecodeError -> Text
+ KDL.Decoder.Monad: [validAnns] :: BaseDecodeError -> [Text]
+ KDL.Decoder.Monad: [value] :: BaseDecodeError -> Value
+ KDL.Decoder.Monad: addContext :: ContextItem -> DecodeM a -> DecodeM a
+ KDL.Decoder.Monad: any :: DecodeArrow Value a Value
+ KDL.Decoder.Monad: arg :: DecodeValue a => NodeDecoder a
+ KDL.Decoder.Monad: argAt :: DecodeValue a => Text -> NodeListDecoder a
+ KDL.Decoder.Monad: argAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Monad: argAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Monad: argWith :: forall a b. Typeable b => ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Monad: argWith' :: forall a b. Typeable b => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Monad: argsAt :: DecodeValue a => Text -> NodeListDecoder [a]
+ KDL.Decoder.Monad: argsAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Monad: argsAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Monad: bool :: DecodeArrow Value a Bool
+ KDL.Decoder.Monad: children :: NodeListDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Monad: class Typeable a => DecodeNode a
+ KDL.Decoder.Monad: class Typeable a => DecodeValue a
+ KDL.Decoder.Monad: dashChildrenAt :: DecodeValue a => Text -> NodeListDecoder [a]
+ KDL.Decoder.Monad: dashChildrenAtWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Monad: dashChildrenAtWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Monad: dashNodesAt :: DecodeNode a => Text -> NodeListDecoder [a]
+ KDL.Decoder.Monad: dashNodesAtWith :: forall a b. Typeable b => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a [b]
+ KDL.Decoder.Monad: data BaseDecodeError
+ KDL.Decoder.Monad: data ContextItem
+ KDL.Decoder.Monad: data DecodeError
+ KDL.Decoder.Monad: data DecodeM a
+ KDL.Decoder.Monad: debug :: Show o => DecodeArrow o a ()
+ KDL.Decoder.Monad: decodeDocWith :: DocumentDecoder a -> Document -> Either DecodeError a
+ KDL.Decoder.Monad: decodeFileWith :: DocumentDecoder a -> FilePath -> IO (Either DecodeError a)
+ KDL.Decoder.Monad: decodeThrow :: BaseDecodeError -> DecodeM a
+ KDL.Decoder.Monad: decodeWith :: DocumentDecoder a -> Text -> Either DecodeError a
+ KDL.Decoder.Monad: document :: NodeListDecoder a -> DocumentDecoder a
+ KDL.Decoder.Monad: documentSchema :: DocumentDecoder a -> SchemaOf NodeList
+ KDL.Decoder.Monad: fail :: forall a o. Text -> Decoder o a
+ KDL.Decoder.Monad: failM :: Text -> DecodeM a
+ KDL.Decoder.Monad: makeFatal :: DecodeM a -> DecodeM a
+ KDL.Decoder.Monad: makeNonFatal :: DecodeM a -> DecodeM a
+ KDL.Decoder.Monad: many :: Alternative f => f a -> f [a]
+ KDL.Decoder.Monad: newtype DocumentDecoder a
+ KDL.Decoder.Monad: node :: DecodeNode a => Text -> NodeListDecoder a
+ KDL.Decoder.Monad: nodeDecoder :: DecodeNode a => NodeDecoder a
+ KDL.Decoder.Monad: nodeWith :: forall a b. Typeable b => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Monad: nodeWith' :: forall a b. Typeable b => Text -> [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a b
+ KDL.Decoder.Monad: null :: DecodeArrow Value a ()
+ KDL.Decoder.Monad: number :: DecodeArrow Value a Scientific
+ KDL.Decoder.Monad: oneOf :: Alternative f => [f a] -> f a
+ KDL.Decoder.Monad: option :: Alternative f => a -> f a -> f a
+ KDL.Decoder.Monad: optional :: Alternative f => f a -> f (Maybe a)
+ KDL.Decoder.Monad: prop :: DecodeValue a => Text -> NodeDecoder a
+ KDL.Decoder.Monad: propWith :: forall a b. Typeable b => Text -> ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Monad: propWith' :: forall a b. Typeable b => Text -> [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b
+ KDL.Decoder.Monad: remainingNodes :: DecodeNode a => NodeListDecoder (Map Text [a])
+ KDL.Decoder.Monad: remainingNodesWith :: forall a b. Typeable b => NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b])
+ KDL.Decoder.Monad: remainingNodesWith' :: forall a b. Typeable b => [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b])
+ KDL.Decoder.Monad: remainingProps :: DecodeValue a => NodeDecoder (Map Text a)
+ KDL.Decoder.Monad: remainingPropsWith :: forall a b. Typeable b => ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b)
+ KDL.Decoder.Monad: remainingPropsWith' :: forall a b. Typeable b => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b)
+ KDL.Decoder.Monad: renderDecodeError :: DecodeError -> Text
+ KDL.Decoder.Monad: runDecodeM :: DecodeM a -> Either DecodeError a
+ KDL.Decoder.Monad: some :: Alternative f => f a -> f [a]
+ KDL.Decoder.Monad: text :: DecodeArrow Value a Text
+ KDL.Decoder.Monad: type Context = [ContextItem]
+ KDL.Decoder.Monad: type Decoder o a = DecodeArrow o () a
+ KDL.Decoder.Monad: type NodeDecodeArrow a b = DecodeArrow Node a b
+ KDL.Decoder.Monad: type NodeDecoder a = NodeDecodeArrow () a
+ KDL.Decoder.Monad: type NodeListDecodeArrow a b = DecodeArrow NodeList a b
+ KDL.Decoder.Monad: type NodeListDecoder a = NodeListDecodeArrow () a
+ KDL.Decoder.Monad: type ValueDecodeArrow a b = DecodeArrow Value a b
+ KDL.Decoder.Monad: type ValueDecoder a = ValueDecodeArrow () a
+ KDL.Decoder.Monad: validNodeTypeAnns :: DecodeNode a => Proxy a -> [Text]
+ KDL.Decoder.Monad: validValueTypeAnns :: DecodeValue a => Proxy a -> [Text]
+ KDL.Decoder.Monad: valueDecoder :: DecodeValue a => ValueDecoder a
+ KDL.Decoder.Monad: withDecoder :: DecodeArrow o a b -> (b -> DecodeM c) -> DecodeArrow o a c
+ KDL.Decoder.Schema: SchemaAnd :: [Schema a] -> Schema a
+ KDL.Decoder.Schema: SchemaOne :: a -> Schema a
+ KDL.Decoder.Schema: SchemaOr :: [Schema a] -> Schema a
+ KDL.Decoder.Schema: SchemaSome :: Schema a -> Schema a
+ KDL.Decoder.Schema: SchemaUnknown :: Schema a
+ KDL.Decoder.Schema: TypedNodeSchema :: TypeRep -> [Text] -> SchemaOf Node -> TypedNodeSchema
+ KDL.Decoder.Schema: TypedValueSchema :: TypeRep -> [Text] -> SchemaOf Value -> TypedValueSchema
+ KDL.Decoder.Schema: [dataSchema] :: TypedValueSchema -> SchemaOf Value
+ KDL.Decoder.Schema: [nodeSchema] :: TypedNodeSchema -> SchemaOf Node
+ KDL.Decoder.Schema: [typeHint] :: TypedValueSchema -> TypeRep
+ KDL.Decoder.Schema: [validTypeAnns] :: TypedValueSchema -> [Text]
+ KDL.Decoder.Schema: data Schema a
+ KDL.Decoder.Schema: data TypedNodeSchema
+ KDL.Decoder.Schema: data TypedValueSchema
+ KDL.Decoder.Schema: data family SchemaItem a
+ KDL.Decoder.Schema: instance GHC.Classes.Eq (KDL.Decoder.Schema.SchemaItem KDL.Types.Node)
+ KDL.Decoder.Schema: instance GHC.Classes.Eq (KDL.Decoder.Schema.SchemaItem KDL.Types.NodeList)
+ KDL.Decoder.Schema: instance GHC.Classes.Eq (KDL.Decoder.Schema.SchemaItem KDL.Types.Value)
+ KDL.Decoder.Schema: instance GHC.Classes.Eq KDL.Decoder.Schema.TypedNodeSchema
+ KDL.Decoder.Schema: instance GHC.Classes.Eq KDL.Decoder.Schema.TypedValueSchema
+ KDL.Decoder.Schema: instance GHC.Classes.Eq a => GHC.Classes.Eq (KDL.Decoder.Schema.Schema a)
+ KDL.Decoder.Schema: instance GHC.Classes.Ord (KDL.Decoder.Schema.SchemaItem KDL.Types.Value)
+ KDL.Decoder.Schema: instance GHC.Enum.Bounded (KDL.Decoder.Schema.SchemaItem KDL.Types.Value)
+ KDL.Decoder.Schema: instance GHC.Enum.Enum (KDL.Decoder.Schema.SchemaItem KDL.Types.Value)
+ KDL.Decoder.Schema: instance GHC.Show.Show (KDL.Decoder.Schema.SchemaItem KDL.Types.Node)
+ KDL.Decoder.Schema: instance GHC.Show.Show (KDL.Decoder.Schema.SchemaItem KDL.Types.NodeList)
+ KDL.Decoder.Schema: instance GHC.Show.Show (KDL.Decoder.Schema.SchemaItem KDL.Types.Value)
+ KDL.Decoder.Schema: instance GHC.Show.Show KDL.Decoder.Schema.TypedNodeSchema
+ KDL.Decoder.Schema: instance GHC.Show.Show KDL.Decoder.Schema.TypedValueSchema
+ KDL.Decoder.Schema: instance GHC.Show.Show a => GHC.Show.Show (KDL.Decoder.Schema.Schema a)
+ KDL.Decoder.Schema: schemaAlt :: Schema a -> Schema a -> Schema a
+ KDL.Decoder.Schema: schemaJoin :: Schema a -> Schema a -> Schema a
+ KDL.Decoder.Schema: type SchemaOf o = Schema SchemaItem o
+ KDL.Parser: parse :: Text -> Either Text Document
+ KDL.Parser: parseFile :: FilePath -> IO (Either Text Document)
+ KDL.Render: render :: Document -> Text
+ KDL.Render: renderAnn :: Ann -> Text
+ KDL.Render: renderIdentifier :: Identifier -> Text
+ KDL.Render: renderValue :: Value -> Text
+ KDL.Render: renderValueData :: ValueData -> Text
+ KDL.Types: Ann :: Identifier -> Maybe AnnFormat -> Ann
+ KDL.Types: AnnFormat :: Text -> Text -> Text -> Text -> AnnFormat
+ KDL.Types: Bool :: Bool -> ValueData
+ KDL.Types: Entry :: Maybe Identifier -> Value -> Maybe EntryFormat -> Entry
+ KDL.Types: EntryFormat :: Text -> Text -> Text -> Text -> EntryFormat
+ KDL.Types: Identifier :: Text -> Maybe IdentifierFormat -> Identifier
+ KDL.Types: IdentifierFormat :: Text -> IdentifierFormat
+ KDL.Types: Node :: Maybe Ann -> Identifier -> [Entry] -> Maybe NodeList -> Maybe NodeFormat -> Node
+ KDL.Types: NodeFormat :: Text -> Text -> Text -> Text -> Text -> NodeFormat
+ KDL.Types: NodeList :: [Node] -> Maybe NodeListFormat -> NodeList
+ KDL.Types: NodeListFormat :: Text -> Text -> NodeListFormat
+ KDL.Types: Null :: ValueData
+ KDL.Types: Number :: Scientific -> ValueData
+ KDL.Types: Text :: Text -> ValueData
+ KDL.Types: Value :: Maybe Ann -> ValueData -> Maybe ValueFormat -> Value
+ KDL.Types: ValueFormat :: Text -> ValueFormat
+ KDL.Types: [after_eq] :: EntryFormat -> Text
+ KDL.Types: [after_id] :: AnnFormat -> Text
+ KDL.Types: [after_key] :: EntryFormat -> Text
+ KDL.Types: [ann] :: Value -> Maybe Ann
+ KDL.Types: [before_children] :: NodeFormat -> Text
+ KDL.Types: [before_id] :: AnnFormat -> Text
+ KDL.Types: [before_terminator] :: NodeFormat -> Text
+ KDL.Types: [children] :: Node -> Maybe NodeList
+ KDL.Types: [data_] :: Value -> ValueData
+ KDL.Types: [entries] :: Node -> [Entry]
+ KDL.Types: [format] :: Identifier -> Maybe IdentifierFormat
+ KDL.Types: [identifier] :: Ann -> Identifier
+ KDL.Types: [leading] :: AnnFormat -> Text
+ KDL.Types: [name] :: Entry -> Maybe Identifier
+ KDL.Types: [nodes] :: NodeList -> [Node]
+ KDL.Types: [repr] :: IdentifierFormat -> Text
+ KDL.Types: [terminator] :: NodeFormat -> Text
+ KDL.Types: [trailing] :: AnnFormat -> Text
+ KDL.Types: [value] :: Identifier -> Text
+ KDL.Types: annFormat :: Ann -> Maybe AnnFormat
+ KDL.Types: annIdentifier :: Ann -> Identifier
+ KDL.Types: data Ann
+ KDL.Types: data AnnFormat
+ KDL.Types: data Entry
+ KDL.Types: data EntryFormat
+ KDL.Types: data Identifier
+ KDL.Types: data IdentifierFormat
+ KDL.Types: data Node
+ KDL.Types: data NodeFormat
+ KDL.Types: data NodeList
+ KDL.Types: data NodeListFormat
+ KDL.Types: data Value
+ KDL.Types: data ValueData
+ KDL.Types: data ValueFormat
+ KDL.Types: docNodes :: Document -> [Node]
+ KDL.Types: entryFormat :: Entry -> Maybe EntryFormat
+ KDL.Types: entryName :: Entry -> Maybe Identifier
+ KDL.Types: entryValue :: Entry -> Value
+ KDL.Types: filterNodes :: Text -> NodeList -> [Node]
+ KDL.Types: fromIdentifier :: Identifier -> Text
+ KDL.Types: fromNodeList :: NodeList -> [Node]
+ KDL.Types: getArg :: Node -> Maybe Value
+ KDL.Types: getArgAt :: Text -> NodeList -> Maybe Value
+ KDL.Types: getArgs :: Node -> [Value]
+ KDL.Types: getArgsAt :: Text -> NodeList -> [Value]
+ KDL.Types: getDashChildrenAt :: Text -> NodeList -> [Value]
+ KDL.Types: getDashNodesAt :: Text -> NodeList -> [Node]
+ KDL.Types: getProp :: Text -> Node -> Maybe Value
+ KDL.Types: getProps :: Node -> Map Text Value
+ KDL.Types: identifierFormat :: Identifier -> Maybe IdentifierFormat
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.Ann
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.AnnFormat
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.Entry
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.EntryFormat
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.Identifier
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.IdentifierFormat
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.Node
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.NodeFormat
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.NodeList
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.NodeListFormat
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.Value
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.ValueData
+ KDL.Types: instance GHC.Classes.Eq KDL.Types.ValueFormat
+ KDL.Types: instance GHC.Classes.Ord KDL.Types.Identifier
+ KDL.Types: instance GHC.Classes.Ord KDL.Types.IdentifierFormat
+ KDL.Types: instance GHC.Show.Show KDL.Types.Ann
+ KDL.Types: instance GHC.Show.Show KDL.Types.AnnFormat
+ KDL.Types: instance GHC.Show.Show KDL.Types.Entry
+ KDL.Types: instance GHC.Show.Show KDL.Types.EntryFormat
+ KDL.Types: instance GHC.Show.Show KDL.Types.Identifier
+ KDL.Types: instance GHC.Show.Show KDL.Types.IdentifierFormat
+ KDL.Types: instance GHC.Show.Show KDL.Types.Node
+ KDL.Types: instance GHC.Show.Show KDL.Types.NodeFormat
+ KDL.Types: instance GHC.Show.Show KDL.Types.NodeList
+ KDL.Types: instance GHC.Show.Show KDL.Types.NodeListFormat
+ KDL.Types: instance GHC.Show.Show KDL.Types.Value
+ KDL.Types: instance GHC.Show.Show KDL.Types.ValueData
+ KDL.Types: instance GHC.Show.Show KDL.Types.ValueFormat
+ KDL.Types: lookupNode :: Text -> NodeList -> Maybe Node
+ KDL.Types: nodeAnn :: Node -> Maybe Ann
+ KDL.Types: nodeChildren :: Node -> Maybe NodeList
+ KDL.Types: nodeEntries :: Node -> [Entry]
+ KDL.Types: nodeFormat :: Node -> Maybe NodeFormat
+ KDL.Types: nodeListFormat :: NodeList -> Maybe NodeListFormat
+ KDL.Types: nodeName :: Node -> Identifier
+ KDL.Types: toIdentifier :: Text -> Identifier
+ KDL.Types: type Document = NodeList
+ KDL.Types: valueAnn :: Value -> Maybe Ann
+ KDL.Types: valueData :: Value -> ValueData
+ KDL.Types: valueFormat :: Value -> Maybe ValueFormat

Files

CHANGELOG.md view
@@ -1,3 +1,11 @@+## v0.2.0++* Remove monad `Decoder` newtype, just give the `Monad` instance to the canonical `Decoder` type and use the same `Decoder` type everywhere+* Reorganize module structure+* Schema improvements+* Rename `Decoder` => `DecodeArrow`, set `Decoder o a` as alias for `DecodeArrow o () a`+* Use v2 syntax for `#true`/`#false`/`#null`+ ## v0.1.0  Initial release, with:
README.md view
@@ -24,14 +24,14 @@ Parse it with `kdl-hs`:  ```hs-import Data.KDL qualified as KDL+import KDL qualified  main :: IO () main = do   config <- KDL.decodeFileWith decoder "config.kdl"   print config -decoder :: KDL.Decoder Config+decoder :: KDL.DocumentDecoder Config decoder = KDL.document $ do   KDL.node "package" 
kdl-hs.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0  name: kdl-hs-version: 0.1.0+version: 0.2.0 synopsis: KDL language parser and API description: KDL language parser and API. homepage: https://github.com/brandonchinn178/kdl-hs#readme@@ -23,21 +23,24 @@ library   hs-source-dirs: src   exposed-modules:-    Data.KDL-    Data.KDL.Decoder.Arrow-    Data.KDL.Decoder.DecodeM-    Data.KDL.Decoder.Error-    Data.KDL.Decoder.Monad-    Data.KDL.Decoder.Schema-    Data.KDL.Parser-    Data.KDL.Render-    Data.KDL.Types+    KDL+    KDL.Arrow+    KDL.Decoder+    KDL.Decoder.Arrow+    KDL.Decoder.Internal.DecodeM+    KDL.Decoder.Internal.Decoder+    KDL.Decoder.Internal.Error+    KDL.Decoder.Monad+    KDL.Decoder.Schema+    KDL.Parser+    KDL.Render+    KDL.Types   other-modules:-    Data.KDL.Parser.Hustle-    Data.KDL.Parser.Hustle.Formatter-    Data.KDL.Parser.Hustle.Internal-    Data.KDL.Parser.Hustle.Parser-    Data.KDL.Parser.Hustle.Types+    KDL.Parser.Hustle+    KDL.Parser.Hustle.Formatter+    KDL.Parser.Hustle.Internal+    KDL.Parser.Hustle.Parser+    KDL.Parser.Hustle.Types   build-depends:       base < 5     , containers@@ -56,9 +59,9 @@   hs-source-dirs: test   main-is: Main.hs   other-modules:-    Data.KDL.ParserSpec-    Data.KDL.Decoder.ArrowSpec-    Data.KDL.Decoder.MonadSpec+    KDL.ParserSpec+    KDL.Decoder.ArrowSpec+    KDL.Decoder.MonadSpec   build-depends:       base     , containers
− src/Data/KDL.hs
@@ -1,70 +0,0 @@-{-|-This module is intended to be imported qualified as:--> import Data.KDL qualified as KDL--= Quickstart--Given a file @config.kdl@:--@-package {-  name my-pkg-  version "1.2.3"--  dependencies {-    aeson ">= 2.2.3.0" optional=#true-    text ">= 2"-  }-}-@--Parse it with @kdl-hs@:--@-import Data.KDL qualified as KDL--main :: IO ()-main = do-  config <- KDL.decodeFileWith decoder "config.kdl"-  print config--decoder :: KDL.Decoder Config-decoder = KDL.document $ do-  KDL.node "package"--data Config = Config-  { name :: Text-  , version :: Text-  , dependencies :: Map Text Dep-  }-  deriving (Show)--data Dep = Dep-  { version :: Text-  , optional :: Bool-  }-  deriving (Show)--instance KDL.DecodeNode Config where-  nodeDecoder = KDL.noSchema $ do-    name <- KDL.argAt "name"-    version <- KDL.argAt "version"-    dependencies <- KDL.nodeWith "dependencies" . KDL.children $ KDL.remainingNodes-    pure Config{..}--instance KDL.DecodeNode Dep where-  nodeDecoder = KDL.noSchema $ do-    version <- KDL.arg-    optional <- KDL.option False $ KDL.prop "optional"-    pure Dep{..}-@--}-module Data.KDL (-  module X,-) where--import Data.KDL.Decoder.Monad as X-import Data.KDL.Parser as X-import Data.KDL.Render as X-import Data.KDL.Types as X
− src/Data/KDL/Decoder/Arrow.hs
@@ -1,1278 +0,0 @@-{-# LANGUAGE Arrows #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedRecordDot #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}--{-|-This module defines the Arrow interface for decoding a KDL document. Intended to-be imported qualified as:--> import Data.KDL.Decoder.Arrow qualified as KDL--For most use-cases, the Monad interface exported by "Data.KDL" is sufficient. You-may wish to use the Arrow interface if you would like to statically analyze a-decoder's schema, e.g. to generate documentation.--= Quickstart--Given a file @config.kdl@:--@-package {-  name my-pkg-  version "1.2.3"--  dependencies {-    aeson ">= 2.2.3.0" optional=#true-    text ">= 2"-  }-}-@--Parse it with:--@-{\-# LANGUAGE Arrows #-\}--import Data.KDL.Decoder.Arrow qualified as KDL--main :: IO ()-main = do-  config <- KDL.decodeFileWith decoder "config.kdl"-  print config--decoder :: KDL.Decoder Config-decoder = KDL.document $ proc () -> do-  KDL.node "package" -< ()--data Config = Config-  { name :: Text-  , version :: Text-  , dependencies :: Map Text Dep-  }-  deriving (Show)--data Dep = Dep-  { version :: Text-  , optional :: Bool-  }-  deriving (Show)--instance KDL.DecodeNode Config where-  nodeDecoder = proc () -> do-    name <- KDL.argAt "name" -< ()-    version <- KDL.argAt "version" -< ()-    dependencies <- KDL.nodeWith "dependencies" . KDL.children $ KDL.remainingNodes -< ()-    returnA -< Config{..}--instance KDL.DecodeNode Dep where-  nodeDecoder = proc () -> do-    version <- KDL.arg -< ()-    optional <- KDL.option False $ KDL.prop "optional" -< ()-    returnA -< Dep{..}-@--}-module Data.KDL.Decoder.Arrow (-  decodeWith,-  decodeFileWith,-  decodeDocWith,--  -- * Decoder-  Decoder (..),-  module Data.KDL.Decoder.DecodeM,-  fail,-  withDecoder,-  debug,--  -- * Document-  DocumentDecoder (..),-  document,-  documentSchema,--  -- * NodeList-  NodeListDecoder,-  node,-  remainingNodes,-  argAt,-  argsAt,-  dashChildrenAt,-  dashNodesAt,--  -- ** Explicitly specify decoders-  nodeWith,-  remainingNodesWith,-  argAtWith,-  argsAtWith,-  dashChildrenAtWith,-  dashNodesAtWith,--  -- ** Explicitly specify decoders and type annotations-  nodeWith',-  remainingNodesWith',-  argAtWith',-  argsAtWith',-  dashChildrenAtWith',--  -- * Node-  NodeDecoder,-  DecodeNode (..),-  arg,-  prop,-  remainingProps,-  children,--  -- ** Explicitly specify decoders-  argWith,-  propWith,-  remainingPropsWith,--  -- ** Explicitly specify decoders and type annotations-  argWith',-  propWith',-  remainingPropsWith',--  -- * Value-  ValueDecoder,-  DecodeValue (..),-  any,-  text,-  number,-  bool,-  null,--  -- * Combinators-  oneOf,-  many,-  optional,-  option,-  some,--  -- * Internal API-  DecodeStateM,-  runDecodeStateM,-  HasDecodeHistory (..),-  DecodeState (..),-) where--import Control.Applicative (-  Alternative (..),-  optional,- )-import Control.Arrow (Arrow (..), ArrowChoice (..), (>>>))-import Control.Category (Category)-import Control.Category qualified-import Control.Monad (unless, when, (>=>))-import Control.Monad.Trans.Class qualified as Trans-import Control.Monad.Trans.State.Strict (StateT)-import Control.Monad.Trans.State.Strict qualified as StateT-import Data.Bits (finiteBitSize)-import Data.Int (Int64)-import Data.KDL.Decoder.DecodeM-import Data.KDL.Decoder.Schema (-  Schema (..),-  SchemaItem (..),-  SchemaOf,-  TypedNodeSchema (..),-  TypedValueSchema (..),-  schemaAlt,-  schemaJoin,- )-import Data.KDL.Parser (parse, parseFile)-import Data.KDL.Types (-  Ann (..),-  Document,-  Entry (..),-  Identifier (..),-  Node (..),-  NodeList (..),-  Value (..),-  ValueData (..),- )-import Data.List (partition)-import Data.List.NonEmpty qualified as NonEmpty-import Data.Map.Strict (Map)-import Data.Map.Strict qualified as Map-import Data.Maybe (fromMaybe, isNothing)-import Data.Proxy (Proxy (..))-import Data.Scientific (Scientific)-import Data.Scientific qualified as Scientific-import Data.Set (Set)-import Data.Set qualified as Set-import Data.Text (Text)-import Data.Text qualified as Text-import Data.Typeable (Typeable, typeRep)-import Data.Word (Word16, Word32, Word64, Word8)-import Debug.Trace (traceM)-import GHC.Int (Int16, Int32, Int8)-import Numeric.Natural (Natural)-import Prelude hiding (any, fail, null)-import Prelude qualified---- | Decode the given KDL configuration with the given decoder.-decodeWith :: DocumentDecoder a -> Text -> Either DecodeError a-decodeWith decoder = decodeFromParseResult decoder . parse---- | Read KDL configuration from the given file path and decode it with the given decoder.-decodeFileWith :: DocumentDecoder a -> FilePath -> IO (Either DecodeError a)-decodeFileWith decoder = fmap (decodeFromParseResult decoder) . parseFile---- | Decode an already-parsed 'Document' with the given decoder.-decodeDocWith :: DocumentDecoder a -> Document -> Either DecodeError a-decodeDocWith (UnsafeDocumentDecoder decoder) doc =-  runDecodeM . runDecodeStateM doc emptyDecodeHistory $-    decoder.run ()--decodeFromParseResult :: DocumentDecoder a -> Either Text Document -> Either DecodeError a-decodeFromParseResult decoder = \case-  Left e -> runDecodeM . decodeThrow $ DecodeError_ParseError e-  Right doc -> decodeDocWith decoder doc--{----- Decoder -----}--class HasDecodeHistory o where-  data DecodeHistory o-  emptyDecodeHistory :: DecodeHistory o---- | The state to track when decoding an object of type @o@.------ At each decode step, some value within @o@ is consumed and--- the action is recorded in the history.-data DecodeState o = DecodeState-  { object :: !o-  , history :: DecodeHistory o-  -- ^ Not strict, since this only matters for reporting errors-  }--type DecodeStateM o a = StateT (DecodeState o) DecodeM a---- | @Decoder o a b@ represents an arrow with input @a@ and output @b@, within--- the context of decoding a KDL object of type @o@. It also knows the expected--- schema of @o@.------ We're using arrows here so that we can:------   1. Get the schema without running the decoder, and also---   2. Use previously decoded values to inform decoding other values------ Using monads alone would lose (1), but applicatives can't do (2).-data Decoder o a b = Decoder-  { schema :: SchemaOf o-  , run :: a -> DecodeStateM o b-  }--instance Category (Decoder o) where-  id = liftDecodeM pure-  Decoder sch2 bc . Decoder sch1 ab = Decoder (sch1 `schemaJoin` sch2) $ ab >=> bc-instance Arrow (Decoder o) where-  arr f = liftDecodeM (pure . f)-  Decoder sch1 bc *** Decoder sch2 bc' =-    Decoder (sch1 `schemaJoin` sch2) $ \(b, b') -> (,) <$> bc b <*> bc' b'-instance ArrowChoice (Decoder o) where-  Decoder sch1 bc +++ Decoder sch2 bc' =-    Decoder (sch1 `schemaAlt` sch2) $ either (fmap Left . bc) (fmap Right . bc')--instance Functor (Decoder o a) where-  fmap f (Decoder schema run) = Decoder schema $ (fmap f . run)-instance Applicative (Decoder o a) where-  pure = arr . const-  Decoder sch1 kf <*> Decoder sch2 kx =-    Decoder (sch1 `schemaJoin` sch2) $ \a -> kf a <*> kx a-instance Alternative (Decoder o a) where-  -- Can't use StateT's Alternative instance: https://hub.darcs.net/ross/transformers/issue/78-  empty = Decoder (SchemaOr []) $ \_ -> Trans.lift empty-  Decoder sch1 run1 <|> Decoder sch2 run2 =-    Decoder (sch1 `schemaAlt` sch2) $ \a -> StateT.StateT $ \s -> do-      StateT.runStateT (run1 a) s <|> StateT.runStateT (run2 a) s-  some (Decoder sch run) =-    Decoder (SchemaSome sch) $ \a ->-      StateT.StateT $-        let go s0 = do-              (x, s1) <- StateT.runStateT (run a) s0-              (xs, s2) <- go s1 <|> pure ([], s1)-              pure (x : xs, s2)-         in go-  many (Decoder sch run) = some (Decoder sch run) <|> pure []--liftDecodeM :: (a -> DecodeM b) -> Decoder o a b-liftDecodeM f = Decoder (SchemaAnd []) (Trans.lift . f)---- | Run actions within a t'Decoder'. Useful for adding post-processing logic.------ === __Example__------ @--- decoder = KDL.withDecoder KDL.number $ \\x -> do---   when (x > 100)---     KDL.failM $ "argument is too large: " <> (Text.pack . show) x---   pure $ MyVal x--- @-withDecoder :: forall o a b c. Decoder o a b -> (b -> DecodeM c) -> Decoder o a c-withDecoder decoder f = decoder >>> liftDecodeM f--runDecodeStateM :: o -> DecodeHistory o -> DecodeStateM o a -> DecodeM a-runDecodeStateM o hist m =-  StateT.evalStateT m $-    DecodeState-      { object = o-      , history = hist-      }---- | Unconditionally fail the decoder.------ === __Example__------ @--- decoder = proc () -> do---   x <- KDL.arg -< ()---   if x > 100---     then KDL.fail -\< "argument is too large: " <> (Text.pack . show) x---     else returnA -< ()---   returnA -< x--- @-fail :: forall b o. Decoder o Text b-fail = liftDecodeM failM---- | Debug the current state of the object being decoded.------ === __Example__------ @--- 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)--- @-debug :: forall o a. (Show o) => Decoder o a ()-debug =-  Decoder (SchemaAnd []) $ \_ -> do-    o <- StateT.gets (.object)-    traceM $ "[kdl-hs] DEBUG: " ++ show o--{----- Decoding Document -----}--newtype DocumentDecoder a = UnsafeDocumentDecoder (NodeListDecoder () a)---- | 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.-document :: NodeListDecoder () a -> DocumentDecoder a-document decoder =-  UnsafeDocumentDecoder-    decoder-      { run = \() -> do-          a <- decoder.run ()-          validateNodeList-          pure a-      }---- | Get the schema of a 'DocumentDecoder'.------ The schema is statically determined without running the decoder.-documentSchema :: DocumentDecoder a -> SchemaOf NodeList-documentSchema (UnsafeDocumentDecoder decoder) = decoder.schema--{----- Decoding NodeList -----}--type NodeListDecoder = Decoder NodeList-instance HasDecodeHistory NodeList where-  data DecodeHistory NodeList = DecodeHistory_NodeList-    { nodesSeen :: Map Text Int-    }-  emptyDecodeHistory = DecodeHistory_NodeList{nodesSeen = Map.empty}--getNodeIndex :: Text -> DecodeState NodeList -> Int-getNodeIndex name = Map.findWithDefault 0 name . (.history.nodesSeen)--validateNodeList :: StateT (DecodeState NodeList) DecodeM ()-validateNodeList = do-  nodeList <- StateT.gets (.object)-  case nodeList.nodes of-    [] -> pure ()-    node_ : _ -> do-      let identifier = node_.name-      index <- StateT.gets (getNodeIndex identifier.value)-      Trans.lift . decodeThrow $-        DecodeError_UnexpectedNode-          { identifier = identifier-          , index = index-          }---- | 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__------ @--- 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"]--- @-node :: (DecodeNode a) => Text -> NodeListDecoder () a-node name = withDecodeNode $ nodeWith' name---- | Same as 'node', except explicitly specify the 'NodeDecoder' instead of using 'DecodeNode'.------ === __Example__------ @--- let---   config =---     """---     person \"Alice"---     person \"Bob"---     """---   decoder = KDL.document $ proc () -> do---     many . KDL.nodeWith "person" $ KDL.arg -< ()--- KDL.decodeWith decoder config == Right [\"Alice", \"Bob"]--- @-nodeWith :: forall a b. (Typeable b) => Text -> NodeDecoder a b -> NodeListDecoder a b-nodeWith name = nodeWith' name []---- | Same as 'nodeWith', except allow specifying type annotations.-nodeWith' :: forall a b. (Typeable b) => Text -> [Text] -> NodeDecoder a b -> NodeListDecoder a b-nodeWith' name =-  withTypedNodeDecoder $ \schema decodeNode ->-    Decoder (SchemaOne $ NodeNamed name schema) $ \a -> do-      decodeFirstNodeWhere ((== name) . (.name.value)) (decodeNode a) >>= \case-        Just (_, b) -> pure b-        Nothing -> do-          index <- StateT.gets (getNodeIndex name)-          Trans.lift $ decodeThrow DecodeError_ExpectedNode{name = name, index = index}--decodeFirstNodeWhere ::-  (Node -> Bool) ->-  (Node -> DecodeM a) ->-  StateT (DecodeState NodeList) DecodeM (Maybe (Node, a))-decodeFirstNodeWhere matcher decodeNode = do-  nodes <- StateT.gets (.object.nodes)-  case extractFirst matcher nodes of-    Nothing -> do-      pure Nothing-    Just (node_, nodes') -> do-      let name = node_.name-      index <- StateT.gets (getNodeIndex name.value)-      StateT.modify $ \s -> s{object = s.object{nodes = nodes'}}-      b <--        Trans.lift . makeFatal . addContext ContextNode{name = name, index = index} $-          decodeNode node_-      StateT.modify $ \s -> s{history = s.history{nodesSeen = inc name.value s.history.nodesSeen}}-      pure $ Just (node_, b)- where-  inc k = Map.insertWith (+) k 1---- | Decode all remaining nodes.------ === __Example__------ @--- 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"])]--- @-remainingNodes :: (DecodeNode a) => NodeListDecoder () (Map Text [a])-remainingNodes = withDecodeNode remainingNodesWith'---- | Same as 'remainingNodes', except explicitly specify the 'NodeDecoder' instead of using 'DecodeNode'------ === __Example__------ @--- 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"])]--- @-remainingNodesWith :: forall a b. (Typeable b) => NodeDecoder a b -> NodeListDecoder a (Map Text [b])--- TODO: Detect duplicate `remainingNodes` calls and fail to build a decoder-remainingNodesWith = remainingNodesWith' []---- | Same as 'remainingNodesWith', except allow specifying type annotations.-remainingNodesWith' :: forall a b. (Typeable b) => [Text] -> NodeDecoder a b -> NodeListDecoder a (Map Text [b])--- TODO: Detect duplicate `remainingNodes` calls and fail to build a decoder-remainingNodesWith' =-  withTypedNodeDecoder $ \schema decodeNode ->-    Decoder (SchemaOne $ RemainingNodes schema) $ \a -> do-      Map.fromListWith (<>) <$> go (decodeNode a)- where-  go decodeNode = do-    decodeFirstNodeWhere (const True) decodeNode >>= \case-      Nothing -> pure []-      Just (node_, b) -> do-        nodeMap <- go decodeNode-        pure $ (node_.name.value, [b]) : nodeMap---- | 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__------ @--- let---   config =---     """---     verbose #true---     """---   decoder = KDL.document $ proc () -> do---     KDL.argAt "verbose" -< ()--- KDL.decodeWith decoder config == Right True--- @-argAt :: (DecodeValue a) => Text -> NodeListDecoder () a-argAt name = withDecodeValue $ argAtWith' name---- | Same as 'argAt', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- let---   config =---     """---     verbose #true---     """---   decoder = KDL.document $ proc () -> do---     KDL.argAtWith "verbose" KDL.bool -< ()--- KDL.decodeWith decoder config == Right True--- @-argAtWith :: forall a b. (Typeable b) => Text -> ValueDecoder a b -> NodeListDecoder a b-argAtWith name = argAtWith' name []---- | Same as 'argAtWith', except allow specifying type annotations.-argAtWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecoder a b -> NodeListDecoder a b-argAtWith' name typeAnns decoder = nodeWith name $ argWith' typeAnns decoder---- | 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__------ @--- let---   config =---     """---     email "a@example.com" "b@example.com"---     """---   decoder = KDL.document $ proc () -> do---     KDL.argsAt "email" -< ()--- KDL.decodeWith decoder config == Right ["a@example.com", "b@example.com"]--- @-argsAt :: (DecodeValue a) => Text -> NodeListDecoder () [a]-argsAt name = withDecodeValue $ argsAtWith' name---- | Same as 'argsAt', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- let---   config =---     """---     email "a@example.com" "b@example.com"---     """---   decoder = KDL.document $ proc () -> do---     KDL.argsAtWith "email" KDL.text -< ()--- KDL.decodeWith decoder config == Right ["a@example.com", "b@example.com"]--- @-argsAtWith :: forall a b. (Typeable b) => Text -> ValueDecoder a b -> NodeListDecoder a [b]-argsAtWith name = argsAtWith' name []---- | Same as 'argsAtWith', except allow specifying type annotations.-argsAtWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecoder a b -> NodeListDecoder a [b]-argsAtWith' name typeAnns decoder = option [] $ nodeWith name $ many $ argWith' typeAnns decoder---- | 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__------ @--- let---   config =---     """---     attendees {---       - \"Alice"---       - \"Bob"---     }---     """---   decoder = KDL.document $ proc () -> do---     KDL.dashChildrenAt "attendees" -< ()--- KDL.decodeWith decoder config == Right [\"Alice", \"Bob"]--- @-dashChildrenAt :: (DecodeValue a) => Text -> NodeListDecoder () [a]-dashChildrenAt name = withDecodeValue $ dashChildrenAtWith' name---- | Same as 'dashChildrenAt', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- let---   config =---     """---     attendees {---       - \"Alice"---       - \"Bob"---     }---     """---   decoder = KDL.document $ proc () -> do---     KDL.dashChildrenAtWith "attendees" $ KDL.text -< ()--- KDL.decodeWith decoder config == Right [\"Alice", \"Bob"]--- @-dashChildrenAtWith :: forall a b. (Typeable b) => Text -> ValueDecoder a b -> NodeListDecoder a [b]-dashChildrenAtWith name = dashChildrenAtWith' name []---- | Same as 'dashChildrenAtWith', except allow specifying type annotations.-dashChildrenAtWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecoder a b -> NodeListDecoder a [b]-dashChildrenAtWith' name typeAnns decoder = dashNodesAtWith name $ argWith' typeAnns decoder---- | 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__------ @--- 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"]--- @-dashNodesAt :: (DecodeNode a) => Text -> NodeListDecoder () [a]-dashNodesAt name = withDecodeNode $ \_ decoder -> dashNodesAtWith name decoder---- | Same as 'dashChildrenAt', except explicitly specify the 'NodeDecoder' instead of using 'DecodeNode'------ === __Example__------ @--- let---   config =---     """---     attendees {---       - \"Alice"---       - \"Bob"---     }---     """---   decoder = KDL.document $ proc () -> do---     KDL.dashNodesAtWith "attendees" KDL.arg -< ()--- KDL.decodeWith decoder config == Right ["Alice", "Bob"]--- @-dashNodesAtWith :: forall a b. (Typeable b) => Text -> NodeDecoder a b -> NodeListDecoder a [b]-dashNodesAtWith name decoder =-  option [] . nodeWith name $-    children $-      many (nodeWith "-" decoder)--{----- Decoding Ann -----}--validateAnn :: [Text] -> Maybe Ann -> DecodeM ()-validateAnn typeAnns mGivenAnn =-  case mGivenAnn of-    Nothing -> pure ()-    Just givenAnn -> do-      let isValidAnn = Prelude.null typeAnns || givenAnn.identifier.value `elem` typeAnns-      unless isValidAnn $ do-        decodeThrow DecodeError_MismatchedAnn{givenAnn = givenAnn.identifier, validAnns = typeAnns}--{----- Decoding Node -----}--withDecodeNode :: forall a r. (DecodeNode a) => ([Text] -> NodeDecoder () a -> r) -> r-withDecodeNode k = k (validNodeTypeAnns (Proxy @a)) nodeDecoder--withTypedNodeDecoder ::-  forall a b r.-  (Typeable b) =>-  (TypedNodeSchema -> (a -> Node -> DecodeM b) -> r) ->-  ([Text] -> NodeDecoder a b -> r)-withTypedNodeDecoder k typeAnns decoder = k schema decodeNode- where-  schema =-    TypedNodeSchema-      { typeHint = typeRep (Proxy @b)-      , validTypeAnns = typeAnns-      , nodeSchema = decoder.schema-      }-  decodeNode a node_ = do-    validateAnn typeAnns node_.ann-    runDecodeStateM node_ emptyDecodeHistory $ do-      -- TODO: add typeHint to Context-      decoder.run a <* validateNode--validateNode :: StateT (DecodeState Node) DecodeM ()-validateNode = do-  node_ <- StateT.gets (.object)-  case node_.entries of-    [] -> pure ()-    Entry{name = Nothing, value} : _ -> do-      index <- StateT.gets getArgIndex-      Trans.lift . decodeThrow $-        DecodeError_UnexpectedArg-          { index = index-          , value = value-          }-    Entry{name = Just identifier, value} : _ -> do-      Trans.lift . decodeThrow $-        DecodeError_UnexpectedProp-          { identifier = identifier-          , value = value-          }-  case node_.children of-    Nothing -> pure ()-    Just children_ -> do-      childrenHistory <- StateT.gets (.history.childrenHistory)-      Trans.lift . runDecodeStateM children_ childrenHistory $ validateNodeList--type NodeDecoder = Decoder Node-instance HasDecodeHistory Node where-  data DecodeHistory Node = DecodeHistory_Node-    { argsSeen :: Int-    , propsSeen :: Set Identifier-    , childrenHistory :: DecodeHistory NodeList-    }-  emptyDecodeHistory =-    DecodeHistory_Node-      { argsSeen = 0-      , propsSeen = Set.empty-      , childrenHistory = emptyDecodeHistory-      }--getArgIndex :: DecodeState Node -> Int-getArgIndex = (.history.argsSeen)---- | The type class for specifying how a type should be decoded from a KDL node.-class (Typeable a) => DecodeNode a where-  -- | 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__-  ---  -- @-  -- instance DecodeNode Person where-  --   validNodeTypeAnns _ = ["person"]-  -- @-  validNodeTypeAnns :: Proxy a -> [Text]-  validNodeTypeAnns _ = []--  -- | Decode a t'Node' to a value of type @a@-  nodeDecoder :: NodeDecoder () a--instance DecodeNode Node where-  nodeDecoder =-    Decoder SchemaUnknown $ \() -> do-      node_ <- StateT.gets (.object)-      StateT.modify $ \s -> s{object = emptyNode node_.name}-      pure node_-   where-    emptyNode name =-      Node-        { ann = Nothing-        , name = name-        , entries = []-        , children = Nothing-        , format = Nothing-        }---- | Decode an argument in the node.------ === __Example__------ @--- 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])--- @-arg :: (DecodeValue a) => NodeDecoder () a-arg = withDecodeValue argWith'---- | Same as 'arg', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- 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.text -< ()---     vals \<- KDL.many $ KDL.argWith $ show \<$> KDL.valueDecoder @Int -< ()---     returnA -< (name, vals)--- KDL.decodeWith decoder config == Right (\"ALICE", ["1", "2", "3"])--- @-argWith :: forall a b. (Typeable b) => ValueDecoder a b -> NodeDecoder a b-argWith = argWith' []---- | Same as 'argWith', except allow specifying type annotations.-argWith' :: forall a b. (Typeable b) => [Text] -> ValueDecoder a b -> NodeDecoder a b-argWith' =-  withTypedValueDecoder $ \schema decodeValue ->-    Decoder (SchemaOne $ NodeArg schema) $ \a -> do-      index <- StateT.gets getArgIndex--      entries <- StateT.gets (.object.entries)-      (entry, entries') <--        maybe (Trans.lift $ decodeThrow DecodeError_ExpectedArg{index = index}) pure $-          extractFirst (isNothing . (.name)) entries-      StateT.modify $ \s -> s{object = s.object{entries = entries'}}--      b <--        Trans.lift . makeFatal . addContext ContextArg{index = index} $-          decodeValue a entry.value-      StateT.modify $ \s -> s{history = s.history{argsSeen = s.history.argsSeen + 1}}-      pure b---- | 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__------ @--- 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--- @-prop :: (DecodeValue a) => Text -> NodeDecoder () a-prop name = withDecodeValue $ propWith' name---- | Same as 'prop', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- 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"--- @-propWith :: forall a b. (Typeable b) => Text -> ValueDecoder a b -> NodeDecoder a b-propWith name = propWith' name []---- | Same as 'propWith', except allow specifying type annotations.-propWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecoder a b -> NodeDecoder a b-propWith' name =-  withTypedValueDecoder $ \schema decodeValue ->-    Decoder (SchemaOne $ NodeProp name schema) $ \a -> do-      decodeOnePropWhere (== name) (decodeValue a)-        >>= maybe (Trans.lift $ decodeThrow DecodeError_ExpectedProp{name = name}) (pure . snd)--decodeOnePropWhere ::-  (Text -> Bool) ->-  (Value -> DecodeM a) ->-  StateT (DecodeState Node) DecodeM (Maybe (Identifier, a))-decodeOnePropWhere matcher decodeValue = do-  entries <- StateT.gets (.object.entries)-  case findProp entries of-    Nothing -> pure Nothing-    Just (name, prop_, entries') -> do-      StateT.modify $ \s -> s{object = s.object{entries = entries'}}-      b <--        Trans.lift . makeFatal . addContext ContextProp{name = name} $-          decodeValue prop_.value-      StateT.modify $ \s -> s{history = s.history{propsSeen = Set.insert name s.history.propsSeen}}-      pure $ Just (name, b)- where-  isPropWhere f entry = (f . (.value) <$> entry.name) == Just True-  findProp entries =-    case break (isPropWhere matcher) entries of-      (entries1, prop0@Entry{name = Just name} : remainingEntries) ->-        -- Collect remaining props with the same name, latter props override earlier props-        let (props, entries2) = partition (isPropWhere (== name.value)) remainingEntries-         in Just (name, NonEmpty.last $ prop0 NonEmpty.:| props, entries1 <> entries2)-      _ ->-        Nothing---- | Decode all remaining props.------ === __Example__------ @--- 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)]--- @-remainingProps :: (DecodeValue a) => NodeDecoder () (Map Text a)-remainingProps = withDecodeValue remainingPropsWith'---- | Same as 'remainingProps', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- 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")]--- @-remainingPropsWith :: forall a b. (Typeable b) => ValueDecoder a b -> NodeDecoder a (Map Text b)-remainingPropsWith = remainingPropsWith' []---- | Same as 'remainingPropsWith', except allow specifying type annotations.-remainingPropsWith' :: forall a b. (Typeable b) => [Text] -> ValueDecoder a b -> NodeDecoder a (Map Text b)-remainingPropsWith' =-  withTypedValueDecoder $ \schema decodeValue ->-    Decoder (SchemaOne $ NodeRemainingProps schema) $ \a -> do-      Map.fromList <$> go (decodeValue a)- where-  go decodeValue =-    decodeOnePropWhere (const True) decodeValue >>= \case-      Nothing -> pure []-      Just (name, b) -> do-        propMap <- go decodeValue-        pure $ (name.value, b) : propMap---- | Decode the children of the node.------ === __Example__------ @--- 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"}--- @-children :: forall a b. NodeListDecoder a b -> NodeDecoder a b-children decoder =-  Decoder (SchemaOne $ NodeChildren decoder.schema) $ \a -> do-    mChildren <- StateT.gets (.object.children)-    childrenHistory <- StateT.gets (.history.childrenHistory)-    (b, decodeState) <--      Trans.lift $-        StateT.runStateT (decoder.run a) $-          DecodeState-            { object = fromMaybe emptyNodeList mChildren-            , history = childrenHistory-            }-    StateT.modify $ \s ->-      s-        { object = s.object{children = decodeState.object <$ mChildren}-        , history = s.history{childrenHistory = decodeState.history}-        }-    pure b- where-  emptyNodeList = NodeList{nodes = [], format = Nothing}--{----- Decoding ValueData -----}--withDecodeValue :: forall a r. (DecodeValue a) => ([Text] -> ValueDecoder () a -> r) -> r-withDecodeValue k = k (validValueTypeAnns (Proxy @a)) valueDecoder--withTypedValueDecoder ::-  forall a b r.-  (Typeable b) =>-  (TypedValueSchema -> (a -> Value -> DecodeM b) -> r) ->-  ([Text] -> ValueDecoder a b -> r)-withTypedValueDecoder k typeAnns decoder = k schema decodeValue- where-  schema =-    TypedValueSchema-      { typeHint = typeRep (Proxy @b)-      , validTypeAnns = typeAnns-      , dataSchema = decoder.schema-      }-  decodeValue a value = do-    validateAnn typeAnns value.ann-    runDecodeStateM value emptyDecodeHistory $ do-      -- TODO: add typeHint to Context-      decoder.run a--type ValueDecoder = Decoder Value--instance HasDecodeHistory Value where-  data DecodeHistory Value = DecodeHistory_Value-  emptyDecodeHistory = DecodeHistory_Value---- | The type class for specifying how a type should be decoded from a KDL value.-class (Typeable a) => DecodeValue a where-  -- | 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__-  ---  -- @-  -- instance DecodeValue Age where-  --   validValueTypeAnns _ = ["age"]-  -- @-  validValueTypeAnns :: Proxy a -> [Text]-  validValueTypeAnns _ = []--  -- | Decode a t'Value' to a value of type @a@-  ---  -- Helpers that may be useful:-  ---  --   * 'oneOf'-  --   * 'withDecoder'-  --   * 'failM'-  valueDecoder :: ValueDecoder () a--instance DecodeValue Value where-  valueDecoder = any-instance DecodeValue ValueData where-  valueDecoder = (.data_) <$> any-instance DecodeValue Text where-  validValueTypeAnns _ = ["text"]-  valueDecoder = text-instance DecodeValue String where-  validValueTypeAnns _ = ["string"]-  valueDecoder = Text.unpack <$> text-instance DecodeValue Bool where-  validValueTypeAnns _ = ["bool", "boolean"]-  valueDecoder = bool-instance (DecodeValue a) => DecodeValue (Maybe a) where-  validValueTypeAnns _ = validValueTypeAnns (Proxy @a)-  valueDecoder = oneOf [Nothing <$ null, Just <$> valueDecoder]-instance (DecodeValue a, DecodeValue b) => DecodeValue (Either a b) where-  validValueTypeAnns _ = validValueTypeAnns (Proxy @a) <> validValueTypeAnns (Proxy @b)-  valueDecoder = oneOf [Left <$> valueDecoder, Right <$> valueDecoder]--decodeInt :: (Integral b, Bounded b) => ValueDecoder a b-decodeInt = withDecoder number $ \x -> do-  unless (Scientific.isInteger x) $ do-    failM $ "Expected integer, got: " <> (Text.pack . show) x-  maybe (failM $ "Number doesn't fit bounds: " <> (Text.pack . show) x) pure $-    Scientific.toBoundedInteger x-instance DecodeValue Integer where-  validValueTypeAnns _ =-    concat-      [ ["i8", "i16", "i32", "i64", "i128", "isize"]-      , ["u8", "u16", "u32", "u64", "u128", "usize"]-      ]-  valueDecoder = toInteger <$> decodeInt @Int64-instance DecodeValue Int where-  validValueTypeAnns _ =-    concat-      [ ["i8", "i16", "isize"]-      , if bits >= 32 then ["i32"] else []-      , if bits >= 64 then ["i64"] else []-      ]-   where-    bits = finiteBitSize (0 :: Int)-  valueDecoder = decodeInt-instance DecodeValue Int8 where-  validValueTypeAnns _ = ["i8"]-  valueDecoder = decodeInt-instance DecodeValue Int16 where-  validValueTypeAnns _ = ["i16"]-  valueDecoder = decodeInt-instance DecodeValue Int32 where-  validValueTypeAnns _ = ["i32"]-  valueDecoder = decodeInt-instance DecodeValue Int64 where-  validValueTypeAnns _ = ["i64"]-  valueDecoder = decodeInt-instance DecodeValue Word where-  validValueTypeAnns _ =-    concat-      [ ["u8", "u16", "usize"]-      , if bits >= 32 then ["u32"] else []-      , if bits >= 64 then ["u64"] else []-      ]-   where-    bits = finiteBitSize (0 :: Word)-  valueDecoder = decodeInt-instance DecodeValue Word8 where-  validValueTypeAnns _ = ["u8"]-  valueDecoder = decodeInt-instance DecodeValue Word16 where-  validValueTypeAnns _ = ["u16"]-  valueDecoder = decodeInt-instance DecodeValue Word32 where-  validValueTypeAnns _ = ["u32"]-  valueDecoder = decodeInt-instance DecodeValue Word64 where-  validValueTypeAnns _ = ["u64"]-  valueDecoder = decodeInt-instance DecodeValue Natural where-  validValueTypeAnns _ = ["u8", "u16", "u32", "u64", "usize"]-  valueDecoder = withDecoder (valueDecoder @Integer) $ \x -> do-    when (x < 0) $ do-      failM $ "Expected a non-negative number, got: " <> (Text.pack . show) x-    pure $ fromIntegral x--decodeRealFloat :: (RealFloat b) => ValueDecoder a b-decodeRealFloat = withDecoder number $ \x -> do-  either (\_ -> failM $ "Number is too small or too large: " <> (Text.pack . show) x) pure $-    Scientific.toBoundedRealFloat x-instance DecodeValue Scientific where-  validValueTypeAnns _ = ["f32", "f64", "decimal64", "decimal128"]-  valueDecoder = number-instance DecodeValue Float where-  validValueTypeAnns _ = ["f32"]-  valueDecoder = decodeRealFloat-instance DecodeValue Double where-  validValueTypeAnns _ = ["f64"]-  valueDecoder = decodeRealFloat-instance DecodeValue Rational where-  validValueTypeAnns _ = ["decimal64", "decimal128"]-  valueDecoder = withDecoder number $ \x -> do-    -- Use toBoundedRealFloat to guard against large values, but use-    -- toRational after checking to maintain precision-    case Scientific.toBoundedRealFloat @Double x of-      Right _ -> pure $ toRational x-      Left _ -> failM $ "Number is too small or too large: " <> (Text.pack . show) x--valueDataDecoderPrim :: SchemaOf Value -> (Value -> DecodeM b) -> ValueDecoder a b-valueDataDecoderPrim schema f = Decoder schema $ \_ -> Trans.lift . f =<< StateT.gets (.object)---- | Decode any value, without any possibility of failure.-any :: ValueDecoder a Value-any = valueDataDecoderPrim (SchemaOr $ map SchemaOne [minBound .. maxBound]) pure---- | Decode a KDL text value.-text :: ValueDecoder a Text-text = valueDataDecoderPrim (SchemaOne TextSchema) $ \case-  Value{data_ = Text s} -> pure s-  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "text", value = v}---- | Decode a KDL number value.-number :: ValueDecoder a Scientific-number = valueDataDecoderPrim (SchemaOne NumberSchema) $ \case-  Value{data_ = Number x} -> pure x-  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "number", value = v}---- | Decode a KDL bool value.-bool :: ValueDecoder a Bool-bool = valueDataDecoderPrim (SchemaOne BoolSchema) $ \case-  Value{data_ = Bool x} -> pure x-  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "bool", value = v}---- | Decode a KDL null value.-null :: ValueDecoder a ()-null = valueDataDecoderPrim (SchemaOne NullSchema) $ \case-  Value{data_ = Null} -> pure ()-  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "null", value = v}--{----- Utilities -----}---- | Return the first result that succeeds.------ > oneOf [a, b, c] === a <|> b <|> c <|> empty-oneOf :: (Alternative f) => [f a] -> f a-oneOf = foldr (<|>) empty---- | Return the given default value if the given action fails.------ > option a f === f <|> pure a-option :: (Alternative f) => a -> f a -> f a-option a f = f <|> pure a--extractFirst :: (a -> Bool) -> [a] -> Maybe (a, [a])-extractFirst f = go- where-  go = \case-    [] -> Nothing-    x : xs ->-      if f x-        then Just (x, xs)-        else fmap (x :) <$> go xs
− src/Data/KDL/Decoder/DecodeM.hs
@@ -1,94 +0,0 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE NoFieldSelectors #-}--module Data.KDL.Decoder.DecodeM (-  -- * Decoding errors-  module Data.KDL.Decoder.Error,--  -- * DecodeM monad-  DecodeM (..),-  runDecodeM,-  decodeThrow,-  failM,-  makeFatal,-  makeNonFatal,-  addContext,-) where--import Control.Applicative (Alternative (..))-import Data.KDL.Decoder.Error-import Data.Text (Text)---- | The monad that returns either a 'DecodeError' or a result of type @a@.------ To a first approximation, this monad is equivalent to the @Either DecodeError@--- monad, with the following changes:------   * Uses continuation-passing style for performance---   * Has two error channels, one for fatal errors and one for non-fatal errors (see 'makeFatal')---   * Collects as many errors as possible, within an Applicative context-data DecodeM a-  = DecodeM-      ( forall r.-        (DecodeError -> r) -> -- fatal error, not handled by <|>-        (DecodeError -> r) -> -- non-fatal error, handled by <|>-        (a -> r) ->-        r-      )--instance Functor DecodeM where-  fmap f (DecodeM k) = DecodeM $ \onFatal onFail onSuccess -> k onFatal onFail (onSuccess . f)-instance Applicative DecodeM where-  pure x = DecodeM $ \_ _ onSuccess -> onSuccess x-  DecodeM kf <*> DecodeM ka = DecodeM $ \onFatal onFail onSuccess ->-    -- Collect all errors-    kf-      (\e1 -> ka (\e2 -> onFatal $ e1 <> e2) (\e2 -> onFatal $ e1 <> e2) (\_ -> onFatal e1))-      (\e1 -> ka (\e2 -> onFatal $ e1 <> e2) (\e2 -> onFail $ e1 <> e2) (\_ -> onFail e1))-      (\f -> ka onFatal onFail (onSuccess . f))-instance Monad DecodeM where-  (>>) = (*>)-  DecodeM ka >>= k = DecodeM $ \onFatal onFail onSuccess ->-    ka onFatal onFail $ \a -> let DecodeM kb = k a in kb onFatal onFail onSuccess-instance Alternative DecodeM where-  empty = DecodeM $ \_ onFail _ -> onFail mempty-  DecodeM k1 <|> DecodeM k2 = DecodeM $ \onFatal onFail onSuccess ->-    k1-      onFatal-      (\e1 -> k2 onFatal (\e2 -> onFail $ e1 <> e2) onSuccess)-      onSuccess---- | Run a 'DecodeM' action and return the result or the error.-runDecodeM :: DecodeM a -> Either DecodeError a-runDecodeM (DecodeM f) = f Left Left Right---- | Throw an error.------ This error is non-fatal and can be handled by '<|>'. See 'makeFatal'--- for more information.-decodeThrow :: BaseDecodeError -> DecodeM a-decodeThrow e = DecodeM $ \_ onFail _ -> onFail $ DecodeError [([], e)]---- | Throw a 'DecodeError_Custom' error.-failM :: Text -> DecodeM a-failM = decodeThrow . DecodeError_Custom---- | Make all errors in the given action fatal errors.------ A la standard parsing libraries like megaparsec, errors should be--- considered fatal when decoding has started consuming something.-makeFatal :: DecodeM a -> DecodeM a-makeFatal (DecodeM f) = DecodeM $ \onFatal _ onSuccess -> f onFatal onFatal onSuccess---- | Make all errors non-fatal errors.-makeNonFatal :: DecodeM a -> DecodeM a-makeNonFatal (DecodeM f) = DecodeM $ \_ onFail onSuccess -> f onFail onFail onSuccess---- | Add context to all errors that occur in the given action.-addContext :: ContextItem -> DecodeM a -> DecodeM a-addContext ctxItem (DecodeM f) = DecodeM $ \onFatal onFail onSuccess -> f (onFatal . addCtx) (onFail . addCtx) onSuccess- where-  addCtx (DecodeError es) = DecodeError [(ctxItem : ctx, msg) | (ctx, msg) <- es]
− src/Data/KDL/Decoder/Error.hs
@@ -1,96 +0,0 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE NoFieldSelectors #-}--module Data.KDL.Decoder.Error (-  DecodeError (..),-  BaseDecodeError (..),-  Context,-  ContextItem (..),-  renderDecodeError,-) where--import Data.KDL.Render (-  renderIdentifier,-  renderValue,- )-import Data.KDL.Types (-  Identifier,-  Value,- )-import Data.Map qualified as Map-import Data.Text (Text)-import Data.Text qualified as Text--data DecodeError = DecodeError [(Context, BaseDecodeError)]-  deriving (Show, Eq)-instance Semigroup DecodeError where-  DecodeError e1 <> DecodeError e2 = DecodeError (e1 <> e2)-instance Monoid DecodeError where-  mempty = DecodeError []--type Context = [ContextItem]--data ContextItem-  = ContextNode-      { name :: Identifier-      , index :: Int-      }-  | ContextArg-      { index :: Int-      }-  | ContextProp-      { name :: Identifier-      }-  deriving (Show, Eq, Ord)--data BaseDecodeError-  = DecodeError_Custom Text-  | DecodeError_ParseError Text-  | DecodeError_ExpectedNode {name :: Text, index :: Int}-  | DecodeError_ExpectedArg {index :: Int}-  | DecodeError_ExpectedProp {name :: Text}-  | DecodeError_MismatchedAnn {givenAnn :: Identifier, validAnns :: [Text]}-  | DecodeError_ValueDecodeFail {expectedType :: Text, value :: Value}-  | DecodeError_UnexpectedNode {identifier :: Identifier, index :: Int}-  | DecodeError_UnexpectedArg {index :: Int, value :: Value}-  | DecodeError_UnexpectedProp {identifier :: Identifier, value :: Value}-  deriving (Show, Eq)--renderDecodeError :: DecodeError -> Text-renderDecodeError = Text.intercalate "\n" . map renderCtxErrors . groupCtxErrors- where-  -- Group errors with the same contexts together-  groupCtxErrors (DecodeError es) = Map.toAscList $ Map.fromListWith (<>) [(ctx, [e]) | (ctx, e) <- es]--  renderCtxErrors (ctx, errs) =-    Text.intercalate "\n" $ ("At: " <> renderCtxItems ctx) : renderErrors errs--  renderCtxItems items-    | null items = "<root>"-    | otherwise = Text.intercalate " > " . map renderCtxItem $ items-  renderCtxItem = \case-    ContextNode{..} -> renderIdentifier name <> " #" <> showT index-    ContextArg{..} -> "arg #" <> showT index-    ContextProp{..} -> "prop " <> renderIdentifier name--  renderErrors = map ("  " <>) . concatMap (Text.lines . renderError)-  renderError = \case-    DecodeError_Custom msg -> msg-    DecodeError_ParseError msg -> msg-    DecodeError_ExpectedNode{..}-      | index == 0 -> "Expected node: " <> name-      | otherwise -> "Expected another node: " <> name-    DecodeError_ExpectedArg{..} -> "Expected arg #" <> showT index-    DecodeError_ExpectedProp{..} -> "Expected prop: " <> name-    DecodeError_MismatchedAnn{..} -> "Expected annotation to be one of " <> showT validAnns <> ", got: " <> renderIdentifier givenAnn-    DecodeError_ValueDecodeFail{..} -> "Expected " <> expectedType <> ", got: " <> renderValue value-    DecodeError_UnexpectedNode{..} -> "Unexpected node: " <> renderIdentifier identifier <> " #" <> showT index-    DecodeError_UnexpectedArg{..} -> "Unexpected arg #" <> showT index <> ": " <> renderValue value-    DecodeError_UnexpectedProp{..} -> "Unexpected prop: " <> renderIdentifier identifier <> "=" <> renderValue value--  -- Replace with Text.show after requiring at least text-2.1.2-  showT :: (Show a) => a -> Text-  showT = Text.pack . show
− src/Data/KDL/Decoder/Monad.hs
@@ -1,687 +0,0 @@-{-# LANGUAGE LambdaCase #-}--{-|-This module defines the Monad interface for decoding a KDL document. Intended to-be imported from "Data.KDL" as:--> import Data.KDL qualified as KDL--For most use-cases, this Monad interface is sufficient. You may wish to use-"Data.KDL.Decoder.Arrow" if you would like to statically analyze a decoder's-schema, e.g. to generate documentation.--= Quickstart--See "Data.KDL"--}-module Data.KDL.Decoder.Monad (-  -- * Decoding entrypoint-  decodeWith,-  decodeFileWith,-  decodeDocWith,--  -- * Decoder-  Decoder,-  module Data.KDL.Decoder.DecodeM,-  fail,-  withDecoder,-  debug,--  -- * Decode type classes-  -- $decodeTypeClassesDoc-  noSchema,-  Arrow.DecodeNode (..),-  Arrow.DecodeValue (..),--  -- * Document-  DocumentDecoder,-  document,--  -- * NodeList-  NodeListDecoder,-  node,-  remainingNodes,-  argAt,-  argsAt,-  dashChildrenAt,-  dashNodesAt,--  -- ** Explicitly specify decoders-  nodeWith,-  remainingNodesWith,-  dashChildrenAtWith,-  dashNodesAtWith,-  argAtWith,-  argsAtWith,--  -- ** Explicitly specify decoders and type annotations-  nodeWith',-  remainingNodesWith',-  dashChildrenAtWith',-  argAtWith',-  argsAtWith',--  -- * Node-  NodeDecoder,-  arg,-  prop,-  remainingProps,-  children,--  -- ** Explicitly specify decoders-  argWith,-  propWith,-  remainingPropsWith,--  -- ** Explicitly specify decoders and type annotations-  argWith',-  propWith',-  remainingPropsWith',--  -- * Value-  ValueDecoder,-  any,-  text,-  number,-  bool,-  null,--  -- * Combinators-  Arrow.oneOf,-  Arrow.many,-  Arrow.optional,-  Arrow.option,-  Arrow.some,-) where--import Control.Applicative (Alternative)-import Control.Arrow qualified as Arrow-import Data.Coerce (coerce)-import Data.KDL.Decoder.Arrow qualified as Arrow-import Data.KDL.Decoder.DecodeM-import Data.KDL.Decoder.Schema (-  Schema (..),- )-import Data.KDL.Types (-  Document,-  Node,-  NodeList,-  Value,- )-import Data.Map (Map)-import Data.Scientific (Scientific)-import Data.Text (Text)-import Data.Typeable (Typeable)-import Prelude hiding (any, fail, null)---- | Decode the given KDL configuration with the given decoder.-decodeWith :: forall a. DocumentDecoder a -> Text -> Either DecodeError a-decodeWith = coerce (Arrow.decodeWith @a)---- | Read KDL configuration from the given file path and decode it with the given decoder.-decodeFileWith :: forall a. DocumentDecoder a -> FilePath -> IO (Either DecodeError a)-decodeFileWith = coerce (Arrow.decodeFileWith @a)---- | Decode an already-parsed 'Document' with the given decoder.-decodeDocWith :: forall a. DocumentDecoder a -> Document -> Either DecodeError a-decodeDocWith = coerce (Arrow.decodeDocWith @a)--{----- Decoder -----}---- | @Decoder o a@ represents an action that decodes a KDL object of type @o@--- and returns a value of type @a@.------ Exactly the same as the Arrow 'Data.KDL.Decoder.Arrow.Decoder', except--- provides a Monad instance that's useful for do-notation. The only downside of--- using this over the Arrow 'Data.KDL.Decoder.Arrow.Decoder' is you don't get a--- statically derived schema of the entire document. If you want that, you must--- use either arrows notation with the Arrow 'Data.KDL.Decoder.Arrow.Decoder' or--- use do-notation with @-XApplicativeDo@.-newtype Decoder o a = Decoder (Arrow.Decoder o () a)-  deriving-    ( Functor-    , Applicative-    , Alternative-    )--instance Monad (Decoder o) where-  Decoder (Arrow.Decoder _ run1) >>= k =-    Decoder . Arrow.Decoder SchemaUnknown $ \a -> do-      x <- run1 a-      let Decoder (Arrow.Decoder _ run2) = k x-      run2 a---- $decodeTypeClassesDoc--- To avoid code duplication, the same 'DecodeNode' and 'DecodeValue' type classes--- are used for both the Arrow and Monad decoders. However, the type classes are--- implemented with the Arrow decoder, as the Monad decoder is lossy regarding the--- schema. If you're implementing instances that may be used with the Arrow--- decoder, you should probably implement the decoder with--- the "Data.KDL.Decoder.Arrow" API.------ Otherwise, use the normal Monad decoder API and use 'noSchema' at the very end,--- which indicates that this instance doesn't provide any schema information.------ @--- instance KDL.DecodeNode Person where---   nodeDecoder = KDL.noSchema $ do---     name <- KDL.arg---     age <- KDL.prop "age"---     pure Person{..}------ instance KDL.DecodeValue MyVal where---   valueDecoder =---     KDL.noSchema . KDL.oneOf $---       [ MyText \<$> KDL.text---       , KDL.withDecoder KDL.number $ \x -> do---           if x == 0---             then pure MyZero---             else KDL.failM "integer value must be zero"---       ]--- @---- | Drop the Monad Decoder back down to an Arrow Decoder.-noSchema :: Decoder o a -> Arrow.Decoder o () a-noSchema (Decoder decoder) = decoder---- | Unconditionally fail the decoder.------ === __Example__------ @--- decoder = do---   x <- KDL.arg---   when (x > 100) $ do---     KDL.fail $ "argument is too large: " <> (Text.pack . show) x---   pure x--- @-fail :: forall a o. Text -> Decoder o a-fail msg = coerce (Arrow.arr (\() -> msg) Arrow.>>> Arrow.fail @a)---- | Run actions within a t'Decoder'. Useful for adding post-processing logic.------ === __Example__------ @--- decoder = KDL.withDecoder KDL.number $ \\x -> do---   when (x > 100)---     KDL.failM $ "argument is too large: " <> (Text.pack . show) x---   pure $ MyVal x--- @-withDecoder :: forall o a b. Decoder o a -> (a -> DecodeM b) -> Decoder o b-withDecoder = coerce (Arrow.withDecoder @o @() @a @b)---- | Debug the current state of the object being decoded.------ === __Example__------ @--- decoder = do---   KDL.debug    -- Node{entries = [Entry{}, Entry{}]}---   x <- KDL.arg---   KDL.debug    -- Node{entries = [Entry{}]}---   y <- KDL.arg---   KDL.debug    -- Node{entries = []}---   pure (x, y)--- @-debug :: forall o. (Show o) => Decoder o ()-debug = coerce (Arrow.debug @o @())--{----- Decoding Document -----}--newtype DocumentDecoder a = DocumentDecoder (NodeListDecoder a)---- | 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.-document :: forall a. NodeListDecoder a -> DocumentDecoder a-document = coerce (Arrow.document @a)--{----- Decoding NodeList -----}--type NodeListDecoder = Decoder NodeList---- | Decode a node with the given name and decoder.------ == __Example__------ @--- instance KDL.DecodeNode Person where---   nodeTypeAnns _ = ["Person"]---   nodeDecoder = KDL.noSchema $ do---     name <- KDL.arg---     pure Person{..}------ let---   config =---     """---     person "Alice"---     person "Bob"---     person "Charlie"---     (Person)person "Danielle"---     (Dog)person "Fido"---     """---   decoder = KDL.document $ do---     many $ KDL.node "person"--- KDL.decodeWith decoder config == Right ["Alice", "Bob", "Charlie", "Danielle"]--- @-node :: forall a. (Arrow.DecodeNode a) => Text -> NodeListDecoder a-node = coerce (Arrow.node @a)---- | Same as 'node', except explicitly specify the 'NodeDecoder' instead of using 'Arrow.DecodeNode'.------ == __Example__------ @--- let---   config =---     """---     person "Alice"---     person "Bob"---     person "Charlie"---     (Person)person "Danielle"---     (Dog)person "Fido"---     """---   decoder = KDL.document $ do---     many . KDL.nodeWith "person" ["Person"] $ KDL.arg--- KDL.decodeWith decoder config == Right ["Alice", "Bob", "Charlie", "Danielle"]--- @-nodeWith :: forall a. (Typeable a) => Text -> NodeDecoder a -> NodeListDecoder a-nodeWith = coerce (Arrow.nodeWith @() @a)---- | Same as 'nodeWith', except allow specifying type annotations.-nodeWith' :: forall a. (Typeable a) => Text -> [Text] -> NodeDecoder a -> NodeListDecoder a-nodeWith' = coerce (Arrow.nodeWith' @() @a)---- | Decode all remaining nodes with the given decoder.------ == __Example__------ @--- instance KDL.DecodeNode MyArg where---   nodeDecoder = KDL.noSchema $ do---     name <- KDL.arg---     pure MyArg{..}------ let---   config =---     """---     build "pkg1"---     build "pkg2"---     lint "pkg1"---     """---   decoder = KDL.document $ do---     KDL.remainingNodes--- KDL.decodeWith decoder config == Right (Map.fromList [("build", [MyArg "pkg1", MyArg "pkg2"]), ("lint", [MyArg "pkg1"])])--- @-remainingNodes :: forall a. (Arrow.DecodeNode a) => NodeListDecoder (Map Text [a])-remainingNodes = coerce (Arrow.remainingNodes @a)---- | Same as 'remainingNodes', except explicitly specify the 'NodeDecoder' instead of using 'Arrow.DecodeNode'------ == __Example__------ @--- let---   config =---     """---     build "pkg1"---     build "pkg2"---     lint "pkg1"---     """---   decoder = KDL.document $ do---     KDL.remainingNodesWith [] KDL.arg--- KDL.decodeWith decoder config == Right (Map.fromList [("build", ["pkg1", "pkg2"]), ("lint", ["pkg1"])])--- @-remainingNodesWith :: forall a. (Typeable a) => NodeDecoder a -> NodeListDecoder (Map Text [a])-remainingNodesWith = coerce (Arrow.remainingNodesWith @() @a)---- | Same as 'remainingNodesWith', except allow specifying type annotations.-remainingNodesWith' :: forall a. (Typeable a) => [Text] -> NodeDecoder a -> NodeListDecoder (Map Text [a])-remainingNodesWith' = coerce (Arrow.remainingNodesWith' @() @a)---- | 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.------ == __Example__------ @--- let---   config =---     """---     verbose #true---     """---   decoder = KDL.document $ do---     KDL.argAt "verbose"--- KDL.decodeWith decoder config == Right True--- @-argAt :: forall a. (Arrow.DecodeValue a) => Text -> NodeListDecoder a-argAt = coerce (Arrow.argAt @a)---- | Same as 'argAt', except explicitly specify the 'ValueDecoder' instead of using 'Arrow.DecodeValue'------ == __Example__------ @--- let---   config =---     """---     verbose #true---     """---   decoder = KDL.document $ do---     KDL.argAtWith "verbose" [] KDL.bool--- KDL.decodeWith decoder config == Right True--- @-argAtWith :: forall a. (Typeable a) => Text -> ValueDecoder a -> NodeListDecoder a-argAtWith = coerce (Arrow.argAtWith @() @a)---- | Same as 'argAtWith', except allow specifying type annotations.-argAtWith' :: forall a. (Typeable a) => Text -> [Text] -> ValueDecoder a -> NodeListDecoder a-argAtWith' = coerce (Arrow.argAtWith' @() @a)---- | 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.------ == __Example__------ @--- let---   config =---     """---     email "a@example.com" "b@example.com"---     """---   decoder = KDL.document $ do---     KDL.argsAt "email"--- KDL.decodeWith decoder config == Right ["a@example.com", "b@example.com"]--- @-argsAt :: forall a. (Arrow.DecodeValue a) => Text -> NodeListDecoder [a]-argsAt = coerce (Arrow.argsAt @a)---- | Same as 'argsAt', except explicitly specify the 'ValueDecoder' instead of using 'Arrow.DecodeValue'------ == __Example__------ @--- let---   config =---     """---     email "a@example.com" "b@example.com"---     """---   decoder = KDL.document $ do---     KDL.argsAtWith "email" [] KDL.text--- KDL.decodeWith decoder config == Right ["a@example.com", "b@example.com"]--- @-argsAtWith :: forall a. (Typeable a) => Text -> ValueDecoder a -> NodeListDecoder [a]-argsAtWith = coerce (Arrow.argsAtWith @() @a)---- | Same as 'argsAtWith', except allow specifying type annotations.-argsAtWith' :: forall a. (Typeable a) => Text -> [Text] -> ValueDecoder a -> NodeListDecoder [a]-argsAtWith' = coerce (Arrow.argsAtWith' @() @a)---- | A helper for decoding child values in a list following the KDL convention of being named "-".------ == __Example__------ @--- let---   config =---     """---     attendees {---       - "Alice"---       - "Bob"---     }---     """---   decoder = KDL.document $ do---     KDL.dashChildrenAt "attendees"--- KDL.decodeWith decoder config == Right ["Alice", "Bob"]--- @-dashChildrenAt :: forall a. (Arrow.DecodeValue a) => Text -> NodeListDecoder [a]-dashChildrenAt = coerce (Arrow.dashChildrenAt @a)---- | Same as 'dashChildrenAt', except explicitly specify the 'ValueDecoder' instead of using 'Arrow.DecodeValue'------ == __Example__------ @--- let---   config =---     """---     attendees {---       - "Alice"---       - "Bob"---     }---     """---   decoder = KDL.document $ do---     KDL.dashChildrenAtWith "attendees" [] KDL.text--- KDL.decodeWith decoder config == Right ["Alice", "Bob"]--- @-dashChildrenAtWith :: forall a. (Typeable a) => Text -> ValueDecoder a -> NodeListDecoder [a]-dashChildrenAtWith = coerce (Arrow.dashChildrenAtWith @() @a)---- | Same as 'dashChildrenAtWith', except allow specifying type annotations.-dashChildrenAtWith' :: forall a. (Typeable a) => Text -> [Text] -> ValueDecoder a -> NodeListDecoder [a]-dashChildrenAtWith' = coerce (Arrow.dashChildrenAtWith' @() @a)---- | A helper for decoding child values in a list following the KDL convention of being named "-".------ == __Example__------ @--- instance KDL.DecodeNode Attendee where---   nodeDecoder = KDL.noSchema $ do---     name <- KDL.arg---     pure Attendee{..}------ let---   config =---     """---     attendees {---       - "Alice"---       - "Bob"---     }---     """---   decoder = KDL.document $ do---     KDL.dashNodesAt "attendees"--- KDL.decodeWith decoder config == Right [Attendee "Alice", Attendee "Bob"]--- @-dashNodesAt :: forall a. (Arrow.DecodeNode a) => Text -> NodeListDecoder [a]-dashNodesAt = coerce (Arrow.dashNodesAt @a)---- | Same as 'dashChildrenAt', except explicitly specify the 'NodeDecoder' instead of using 'Arrow.DecodeNode'------ == __Example__------ @--- let---   config =---     """---     attendees {---       - "Alice"---       - "Bob"---     }---     """---   decoder = KDL.document $ do---     KDL.dashNodesAtWith "attendees" [] KDL.arg--- KDL.decodeWith decoder config == Right ["Alice", "Bob"]--- @-dashNodesAtWith :: forall a. (Typeable a) => Text -> NodeDecoder a -> NodeListDecoder [a]-dashNodesAtWith = coerce (Arrow.dashNodesAtWith @() @a)--{----- Decoding Node -----}--type NodeDecoder a = Decoder Node a---- | Decode an argument in the node.------ === __Example__------ @--- let---   config =---     """---     person \"Alice" 1 2 3---     """---   decoder = KDL.document $ do---     KDL.nodeWith "person" decodePerson---   decodePerson = do---     name <- KDL.arg---     vals <- KDL.many KDL.arg---     pure (name, vals)--- KDL.decodeWith decoder config == Right (\"Alice", [1, 2, 3])--- @-arg :: forall a. (Arrow.DecodeValue a) => NodeDecoder a-arg = coerce (Arrow.arg @a)---- | Same as 'arg', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- let---   config =---     """---     person \"Alice" 1 2 3---     """---   decoder = KDL.document $ do---     KDL.nodeWith "person" $ decodePerson---   decodePerson = do---     name \<- KDL.argWith $ Text.toUpper \<$> KDL.text---     vals \<- KDL.many $ KDL.argWith $ show \<$> KDL.valueDecoder @Int---     pure (name, vals)--- KDL.decodeWith decoder config == Right (\"ALICE", ["1", "2", "3"])--- @-argWith :: forall a. (Typeable a) => ValueDecoder a -> NodeDecoder a-argWith = coerce (Arrow.argWith @() @a)---- | Same as 'argWith', except allow specifying type annotations.-argWith' :: forall a. (Typeable a) => [Text] -> ValueDecoder a -> NodeDecoder a-argWith' = coerce (Arrow.argWith' @() @a)---- | 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__------ @--- let---   config =---     """---     my-node a=1 b=2 a=3---     """---   decoder = KDL.document $ do---     KDL.nodeWith "my-node" $ KDL.prop @Int "a"--- KDL.decodeWith decoder config == Right 3--- @-prop :: forall a. (Arrow.DecodeValue a) => Text -> NodeDecoder a-prop = coerce (Arrow.prop @a)---- | Same as 'prop', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- let---   config =---     """---     my-node a=1 b=2 a=3---     """---   decoder = KDL.document $ do---     KDL.nodeWith "my-node" $ do---       KDL.propWith "a" $ show \<$> KDL.number--- KDL.decodeWith decoder config == Right "3.0"--- @-propWith :: forall a. (Typeable a) => Text -> ValueDecoder a -> NodeDecoder a-propWith = coerce (Arrow.propWith @() @a)---- | Same as 'propWith', except allow specifying type annotations.-propWith' :: forall a. (Typeable a) => Text -> [Text] -> ValueDecoder a -> NodeDecoder a-propWith' = coerce (Arrow.propWith' @() @a)---- | Decode all remaining props------ === __Example__------ @--- let---   config =---     """---     my-node a=1 b=2 a=3---     """---   decoder = KDL.document $ do---     KDL.nodeWith "my-node" $ KDL.remainingProps @Int--- KDL.decodeWith decoder config == (Right . Map.fromList) [("a", 3), ("b", 2)]--- @-remainingProps :: forall a. (Arrow.DecodeValue a) => NodeDecoder (Map Text a)-remainingProps = coerce (Arrow.remainingProps @a)---- | Same as 'remainingProps', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'------ === __Example__------ @--- let---   config =---     """---     my-node a=1 b=2 a=3---     """---   decoder = KDL.document $ do---     KDL.nodeWith "my-node" $ do---       KDL.remainingPropsWith $ show \<$> KDL.number--- KDL.decodeWith decoder config == (Right . Map.fromList) [("a", "3.0"), ("b", "2.0")]--- @-remainingPropsWith :: forall a. (Typeable a) => ValueDecoder a -> NodeDecoder (Map Text a)-remainingPropsWith = coerce (Arrow.remainingPropsWith @() @a)---- | Same as 'remainingPropsWith', except allow specifying type annotations.-remainingPropsWith' :: forall a. (Typeable a) => [Text] -> ValueDecoder a -> NodeDecoder (Map Text a)-remainingPropsWith' = coerce (Arrow.remainingPropsWith' @() @a)---- | Decode the children of the node.------ === __Example__------ @--- let---   config =---     """---     person \"Alice" {---       email "alice@example.com"---     }---     """---   decoder = KDL.document $ do---     KDL.nodeWith "person" $ do---       name <- KDL.arg---       email <- KDL.children $ KDL.argAt "email"---       pure Person{..}--- KDL.decodeWith decoder config == Right Person{name = \"Alice", email = "alice\@example.com"}--- @-children :: forall a. NodeListDecoder a -> NodeDecoder a-children = coerce (Arrow.children @() @a)--{----- Decoding Value -----}--type ValueDecoder a = Decoder Value a---- | Decode any value, without any possibility of failure.-any :: ValueDecoder Value-any = coerce (Arrow.any @())---- | Decode a KDL text value.-text :: ValueDecoder Text-text = coerce (Arrow.text @())---- | Decode a KDL number value.-number :: ValueDecoder Scientific-number = coerce (Arrow.number @())---- | Decode a KDL bool value.-bool :: ValueDecoder Bool-bool = coerce (Arrow.bool @())---- | Decode a KDL null value.-null :: ValueDecoder ()-null = coerce (Arrow.null @())
− src/Data/KDL/Decoder/Schema.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE TypeFamilies #-}--module Data.KDL.Decoder.Schema (-  SchemaOf,-  Schema (..),-  SchemaItem (..),-  TypedNodeSchema (..),-  TypedValueSchema (..),-  schemaJoin,-  schemaAlt,-) where--import Data.KDL.Types (-  Node,-  NodeList,-  Value,- )-import Data.Text (Text)-import Data.Typeable (TypeRep)--type SchemaOf o = Schema (SchemaItem o)--data Schema a-  = SchemaOne a-  | SchemaSome (Schema a)-  | SchemaAnd [Schema a]-  | SchemaOr [Schema a]-  | SchemaUnknown-  deriving (Show, Eq)--data family SchemaItem a--data instance SchemaItem NodeList-  = NodeNamed Text TypedNodeSchema-  | RemainingNodes TypedNodeSchema-  deriving (Show, Eq)--data TypedNodeSchema = TypedNodeSchema-  { typeHint :: TypeRep-  , validTypeAnns :: [Text]-  , nodeSchema :: SchemaOf Node-  }-  deriving (Show, Eq)--data instance SchemaItem Node-  = NodeArg TypedValueSchema-  | NodeProp Text TypedValueSchema-  | NodeRemainingProps TypedValueSchema-  | NodeChildren (SchemaOf NodeList)-  deriving (Show, Eq)--data TypedValueSchema = TypedValueSchema-  { typeHint :: TypeRep-  , validTypeAnns :: [Text]-  , dataSchema :: SchemaOf Value-  }-  deriving (Show, Eq)--data instance SchemaItem Value-  = TextSchema-  | NumberSchema-  | BoolSchema-  | NullSchema-  deriving (Show, Eq, Ord, Enum, Bounded)--schemaJoin :: Schema a -> Schema a -> Schema a-schemaJoin = curry $ \case-  (SchemaAnd l, SchemaAnd r) -> SchemaAnd (l <> r)-  (l, SchemaAnd r) -> SchemaAnd (l : r)-  (SchemaAnd l, r) -> SchemaAnd (l <> [r])-  (l, r) -> SchemaAnd [l, r]--schemaAlt :: Schema a -> Schema a -> Schema a-schemaAlt = curry $ \case-  (SchemaOr l, SchemaOr r) -> SchemaOr (l <> r)-  (l, SchemaOr r) -> SchemaOr (l : r)-  (SchemaOr l, r) -> SchemaOr (r : l)-  (l, r) -> SchemaOr [l, r]
− src/Data/KDL/Parser.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}--module Data.KDL.Parser (-  parse,-  parseFile,-) where--import Data.KDL.Parser.Hustle qualified as Hustle-import Data.KDL.Types (-  Ann (..),-  Document,-  Entry (..),-  Identifier (..),-  Node (..),-  NodeList (..),-  Value (..),-  ValueData (..),-  ValueFormat (..),- )-import Data.Map qualified as Map-import Data.Text (Text)-import Data.Text qualified as Text-import Data.Text.IO qualified as Text---- TODO: Implement our own parser that implements the v2.0.0 spec + preserves formatting and comments-parse :: Text -> Either Text Document-parse input =-  case Hustle.parse Hustle.document "" input of-    Left e -> Left . Text.strip . Text.pack . Hustle.errorBundlePretty $ e-    Right (Hustle.Document nodes) -> Right $ fromNodes nodes- where-  fromNodes nodes =-    NodeList-      { nodes = map fromNode nodes-      , format = Nothing-      }--  fromAnn identifier =-    Ann-      { identifier = fromIdentifier identifier-      , format = Nothing-      }--  fromNode Hustle.Node{..} =-    Node-      { ann = fromAnn <$> nodeAnn-      , name = fromIdentifier nodeName-      , entries = map fromArgEntry nodeArgs <> map fromPropEntry (Map.toList nodeProps)-      , children = Just $ fromNodes nodeChildren-      , format = Nothing-      }--  fromArgEntry v =-    Entry-      { name = Nothing-      , value = fromValue v-      , format = Nothing-      }--  fromPropEntry (name, v) =-    Entry-      { name = Just $ fromIdentifier name-      , value = fromValue v-      , format = Nothing-      }--  fromValue Hustle.Value{..} =-    Value-      { ann = fromAnn <$> valueAnn-      , data_ =-          case valueExp of-            Hustle.StringValue s -> Text s-            Hustle.IntegerValue x -> Number (fromInteger x)-            Hustle.SciValue x -> Number x-            Hustle.BooleanValue x -> Bool x-            Hustle.NullValue -> Null-      , format =-          case valueExp of-            Hustle.IntegerValue x -> Just ValueFormat{repr = Text.pack $ show x}-            _ -> Nothing-      }--  fromIdentifier (Hustle.Identifier s) =-    Identifier-      { value = s-      , format = Nothing-      }--parseFile :: FilePath -> IO (Either Text Document)-parseFile = fmap parse . Text.readFile
− src/Data/KDL/Parser/Hustle.hs
@@ -1,28 +0,0 @@-{- FOURMOLU_DISABLE -}--{- | Vendered from https://github.com/fuzzypixelz/hustle -}-module Data.KDL.Parser.Hustle-  ( Parser-  , Document(..)-  , Node(..)-  , Value(..)-  , ValueType(..)-  , Identifier(..)-  , pretty-  , document-  , parse-  , errorBundlePretty-  ) where--import           Data.KDL.Parser.Hustle.Formatter ( Pretty(pretty) )-import           Data.KDL.Parser.Hustle.Parser  ( document )-import           Data.KDL.Parser.Hustle.Types   ( Document(..)-                                                , Identifier(..)-                                                , Node(..)-                                                , Parser-                                                , Value(..)-                                                , ValueType(..)-                                                )-import           Text.Megaparsec                ( errorBundlePretty-                                                , parse-                                                )
− src/Data/KDL/Parser/Hustle/Formatter.hs
@@ -1,83 +0,0 @@-{- FOURMOLU_DISABLE -}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS_GHC -Wno-orphans #-}--{- | Vendered from https://github.com/fuzzypixelz/hustle -}-module Data.KDL.Parser.Hustle.Formatter-  ( Pretty(pretty)-  ) where--import           Data.Map                       ( Map )-import qualified Data.Map.Strict               as Map-import           Data.Maybe                     ( catMaybes )-import           Data.Scientific                ( Scientific )-import qualified Data.Text                     as T-import           Data.KDL.Parser.Hustle.Internal ( escChar-                                                , match-                                                )-import           Data.KDL.Parser.Hustle.Parser  ( identifier )-import           Data.KDL.Parser.Hustle.Types-import           Prettyprinter                  ( Pretty(pretty)-                                                , braces-                                                , dquotes-                                                , enclose-                                                , hsep-                                                , nest-                                                , parens-                                                , viaShow-                                                , vsep-                                                )--instance Pretty Scientific where-  pretty = viaShow--instance Pretty Identifier where-  pretty (Identifier i) =-    if match identifier i then pretty i else dquotes (pretty i)--instance Pretty Value where-  pretty v = vann <> vexp-   where-    vann = case valueAnn v of-      Nothing -> ""-      Just a  -> parens (pretty a)-    vexp = case valueExp v of-      StringValue  s -> dquotes . pretty $ T.concatMap escChar s-      IntegerValue i -> pretty i-      SciValue     s -> pretty s-      BooleanValue b -> if b then "true" else "false"-      NullValue      -> "null"--instance Pretty (Map Identifier Value) where-  pretty ps = hsep . Map.elems $ Map.mapWithKey prop ps-    where prop i v = pretty i <> "=" <> pretty v--instance Pretty Node where-  pretty n = hsep . catMaybes $ [nname, nargs, nprops, nchildren]-   where-    nann = case nodeAnn n of-      Nothing -> ""-      Just a  -> parens (pretty a)-    nname = Just $ nann <> pretty (nodeName n)-    nargs = case nodeArgs n of-      []  -> Nothing-      nas -> Just . hsep . map pretty $ nas-    nprops | nodeProps n == Map.empty = Nothing-           | otherwise                = Just (pretty (nodeProps n))-    nchildren = case nodeChildren n of-      [] -> Nothing-      ncs ->-        Just-          . nest 4-          . braces-          . enclose "\n" (nest (-4) "\n")-          . vsep-          . map pretty-          $ ncs--instance Pretty Document where-  pretty d = vsep (map pretty (docNodes d)) <> "\n"--instance Show Document where-  show d = show (pretty d)
− src/Data/KDL/Parser/Hustle/Internal.hs
@@ -1,96 +0,0 @@-{- FOURMOLU_DISABLE -}-{-# LANGUAGE OverloadedStrings #-}--{- | Vendered from https://github.com/fuzzypixelz/hustle -}-module Data.KDL.Parser.Hustle.Internal where--import           Control.Monad                  ( void )-import           Data.Char                      ( digitToInt-                                                , isDigit-                                                )-import           Data.Either                    ( isRight )-import           Data.Scientific                ( Scientific )-import qualified Data.Scientific               as Sci-import           Data.Text                      ( Text )-import qualified Data.Text                     as T-import           Data.KDL.Parser.Hustle.Types   ( Parser )-import           Text.Megaparsec                ( (<|>)-                                                , MonadParsec-                                                  ( eof-                                                  , takeWhileP-                                                  , try-                                                  )-                                                , option-                                                , runParser-                                                , satisfy-                                                )-import           Text.Megaparsec.Char           ( char-                                                , char'-                                                , digitChar-                                                , newline-                                                )-import qualified Text.Megaparsec.Char.Lexer    as L--signed :: Num a => Parser a -> Parser a-signed p = option id sign <*> p-  where sign = (id <$ char '+') <|> (negate <$ char '-')--lineComment :: Parser ()-lineComment = L.skipLineComment "//" >> (void newline <|> eof)--blockComment :: Parser ()-blockComment = L.skipBlockCommentNested "/*" "*/"--isBinDigit :: Char -> Bool-isBinDigit c = c `elem` ['0', '1']--data SP = SP Integer Int--number :: Integer -> (Char -> Bool) -> Parser Integer-number b isNumDigit = mkNum . T.filter (/= '_') <$> digits- where-  mkNum = T.foldl' step 0-  step a c = a * b + fromIntegral (digitToInt c)-  digits = T.cons <$> satisfy isNumDigit <*> takeWhileP-    (Just "digit")-    (\c -> isNumDigit c || c == '_')--decimal_ :: Parser Integer-decimal_ = number 10 isDigit--scientific_ :: Parser Scientific-scientific_ = do-  c'      <- decimal_-  SP c e' <- dotDecimal_ c'-  e       <- option e' (try $ exponent_ e')-  return (Sci.scientific c e)--dotDecimal_ :: Integer -> Parser SP-dotDecimal_ c' = do-  void (char '.')-  let digits = T.cons <$> digitChar <*> takeWhileP-        (Just "digit")-        (\c -> isDigit c || c == '_')-  let mkNum = T.foldl' step (SP c' 0)-      step (SP a e') c = SP (a * 10 + fromIntegral (digitToInt c)) (e' - 1)-  mkNum . T.filter (/= '_') <$> digits--exponent_ :: Int -> Parser Int-exponent_ e' = do-  void (char' 'e')-  (+ e') <$> L.signed (return ()) (fromIntegral <$> decimal_)--match :: Parser a -> Text -> Bool-match p t = isRight $ runParser (p >> eof) "" t--escChar :: Char -> Text-escChar c = case c of-  '\x08' -> "\\b"-  '\x09' -> "\\t"-  '\x0A' -> "\\n"-  '\x0C' -> "\\f"-  '\x0D' -> "\\r"-  '\x22' -> "\\\""-  '\x2F' -> "\\/"-  '\x5C' -> "\\\\"-  _      -> T.singleton c
− src/Data/KDL/Parser/Hustle/Parser.hs
@@ -1,310 +0,0 @@-{- FOURMOLU_DISABLE -}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}--{- | Vendered from https://github.com/fuzzypixelz/hustle -}-module Data.KDL.Parser.Hustle.Parser where--import           Data.KDL.Parser.Hustle.Internal-import           Data.KDL.Parser.Hustle.Types--import           Control.Monad                  ( void )-import           Data.Char                      ( chr-                                                , isHexDigit-                                                , isOctDigit-                                                , isDigit-                                                , isSpace-                                                )-import qualified Data.Map.Strict               as Map-import           Data.Maybe                     ( catMaybes-                                                , fromMaybe-                                                , mapMaybe-                                                , maybeToList-                                                )-import           Data.Scientific                ( Scientific )-import           Data.Text                      ( Text )-import qualified Data.Text                     as T-import           Text.Megaparsec                ( (<?>)-                                                , (<|>)-                                                , MonadParsec(eof, label, try)-                                                , anySingle-                                                , between-                                                , choice-                                                , many-                                                , manyTill-                                                , noneOf-                                                , optional-                                                , satisfy-                                                , some-                                                )-import           Text.Megaparsec.Char           ( char-                                                , char'-                                                , crlf-                                                , newline-                                                , string-                                                )---- WHITESPACE--escline :: Parser ()-escline =-  char '\\' >> many ws >> (try linebreak <|> lineComment) <?> "Escape Line"--linespace :: Parser ()-linespace = try linebreak <|> try ws <|> try lineComment <?> "Line Space"--linebreak :: Parser ()-linebreak = label "Newline" $ do-  choice $ void crlf : map-    void-    [ char '\r' <?> "carriage return"-    , char '\n' <?> "newline"-    , char '\x85' <?> "next line"-    , char '\f' <?> "form feed"-    , char '\x2028' <?> "line seperator"-    , char '\x2029' <?> "paragraph seperator"-    ]--ws :: Parser ()-ws = bom <|> hspacechar <|> blockComment <?> "Whitespace"--bom :: Parser ()-bom = void (char '\xFEFF') <?> "BOM"--hspacechar :: Parser ()-hspacechar = label "Unicode Space" $ do-  choice $ map-    void-    [ char '\x0009' <?> "character tabulation"-    , char '\x0020' <?> "space"-    , char '\x00A0' <?> "bo-break space"-    , char '\x1680' <?> "ogham space mark"-    , char '\x2000' <?> "en quad"-    , char '\x2001' <?> "em quad"-    , char '\x2002' <?> "en space"-    , char '\x2003' <?> "em space"-    , char '\x2004' <?> "three-per-em space"-    , char '\x2005' <?> "four-per-em space"-    , char '\x2006' <?> "six-per-em space"-    , char '\x2007' <?> "figure space"-    , char '\x2008' <?> "punctuation space"-    , char '\x2009' <?> "thin space"-    , char '\x200A' <?> "hair space"-    , char '\x202F' <?> "narrow no-break space"-    , char '\x205F' <?> "medium mathmatical space"-    , char '\x3000' <?> "ideographic space"-    ]---- STRINGS--anystring :: Parser Text-anystring = try unquotedstring <|> try rawstring <|> quotedstring--unquotedstring :: Parser Text-unquotedstring = label "Unquoted String" $ do-  c0 <- satisfy $ \c -> isValidChar c && not (isDigit c) && c /= '"'-  rest <- many $ satisfy isValidChar-  -- TODO: Forbid true, false, null, inf, -inf, nan, or "looks like a number"-  pure $ T.pack (c0 : rest)-  where-    isValidChar c = not (isSpace c) && c `notElem` ("[]{}()\\/#\";=" :: [Char])--quotedstring :: Parser Text-quotedstring = label "Quoted String" $ do-  T.concat <$> (char '"' *> manyTill character (char '"'))--rawstring :: Parser Text-rawstring = label "Raw String" $ do-  void (char 'r')-  hs <- T.pack <$> many (char '#')-  void (char '"')-  s <- manyTill anySingle (string (T.cons '"' hs))-  return (T.pack s)--character :: Parser Text-character = void (char '\\') *> escape <|> nonescape--{--  As per the Haskell 2010 Language Report,-  (https://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6)-  The '\/' Solidus escape isn't defined, so if we try to parse it directly the-  compiler throws a lexical error. Hence this terribleness.--}-escape :: Parser Text-escape =-  do-    e <- choice-      [ '\x08' <$ char 'b'-      , '\x09' <$ char 't'-      , '\x0A' <$ char 'n'-      , '\x0C' <$ char 'f'-      , '\x0D' <$ char 'r'-      , '\x22' <$ char '\"'-      , '\x2F' <$ char '/'-      , '\x5C' <$ char '\\'-      ]-    return (T.singleton e)-  <|> uescape--uescape :: Parser Text-uescape = do-  void (string "u{")-  u <- fromInteger <$> number 16 isHexDigit-  if u >= 0x10ffff-    then fail "Exceeded Unicode code point limit."-    else do-      void (char '}')-      let c = chr u-      return (T.singleton c)--nonescape :: Parser Text-nonescape = do-  c <- noneOf ("\\\"" :: [Char])-  return (T.singleton c)---- NUMBERS--binary :: Parser Integer-binary = signed $ char '0' >> char' 'b' >> number 2 isBinDigit--octal :: Parser Integer-octal = signed $ char '0' >> char 'o' >> number 8 isOctDigit--hexadecimal :: Parser Integer-hexadecimal = signed $ char '0' >> char 'x' >> number 16 isHexDigit--integer :: Parser Integer-integer = signed decimal_--scientific :: Parser Scientific-scientific = signed scientific_---- CONTENT--name :: Parser Identifier-name = Identifier <$> (try anystring <|> identifier)--identifier :: Parser Text-identifier = label "Identifier" $ do-  i  <- satisfy iichar-  is <- many (satisfy ichar)-  let result = T.pack (i : is)-  case result of-    "true"  -> fail "keyword true in identifier"-    "false" -> fail "keyword false in identifier"-    "null"  -> fail "keyword null in identifier"-    _       -> return result- where-  ichar c =-    (c > '\x20')-      && (c <= '\x10FFFF')-      && (c `notElem` ("\\/(){}<>;[]=,\"" :: [Char]))-      && not (match linespace (T.singleton c))-  iichar c = ichar c && c `notElem` ['0' .. '9']--nullvalue :: Parser Text-nullvalue = string "null"--bool :: Parser Bool-bool = True <$ string "true" <|> False <$ string "false"--property :: Parser (Identifier, Value)-property = label "Property" $ do-  propKey <- name-  void (char '=')-  propValue <- value-  return (propKey, propValue)--value :: Parser Value-value = label "Value" $ do-  valueAnn <- optional typeAnnotation-  valueExp <- choice-    [ IntegerValue <$> try binary <?> "Binary"-    , IntegerValue <$> try octal <?> "Octal"-    , IntegerValue <$> try hexadecimal <?> "Hexadecimal"-    , SciValue <$> try scientific <?> "Decimal"-    , IntegerValue <$> try integer <?> "Integer"-    , BooleanValue <$> try bool <?> "Boolean"-    , NullValue <$ try nullvalue <?> "Null"-    , StringValue <$> anystring <?> "String"-    ]-  return Value { .. }--typeAnnotation :: Parser Identifier-typeAnnotation = label "Type Annotation" $ do-  void (char '(')-  i <- name-  void (char ')')-  return i---- NODES--nodes :: Parser [Node]-nodes = between (many linespace) (many linespace) (fromMaybe [] <$> body)- where-  body = optional $ do-    n  <- maybeToList <$> node-    ns <- fromMaybe [] <$> optional nodes-    return (n ++ ns)--node :: Parser (Maybe Node)-node = label "Node" $ do-  discard      <- optional comment-  nodeAnn      <- optional typeAnnotation-  nodeName     <- name-  nodeContent  <- catMaybes <$> content-  nodeChildren <- fromMaybe [] <$> optional children-  _            <- many nodespace-  _            <- terminator-  let nodeArgs  = mapMaybe isArg nodeContent-      nodeProps = Map.fromList $ mapMaybe isProp nodeContent-  case discard of-    Just _  -> return Nothing-    Nothing -> return $ Just Node { .. }- where-  isArg c = case c of-    NodeValue v -> Just v-    _           -> Nothing-  isProp c = case c of-    NodeProperty p -> Just p-    _              -> Nothing--content :: Parser [Maybe Content]-content = many . try $ do-  void (some nodespace)-  discard <- optional $ comment <* many nodespace-  c       <- choice [NodeProperty <$> try property, NodeValue <$> try value]-  case discard of-    Just _  -> return Nothing-    Nothing -> return $ Just c--children :: Parser [Node]-children = label "Node Child" . try $ do-  void (many nodespace)-  discard <- optional comment-  void (char '{')-  ns <- nodes-  void (char '}')-  void (many ws)-  case discard of-    Just _  -> return []-    Nothing -> return ns--comment :: Parser ()-comment = label "/-Comment" . try $ do-  void (string "/-")-  void $ many nodespace--nodespace :: Parser ()-nodespace = label "Node Space" $ do-  try (many ws *> escline <* many ws) <|> try (void $ some ws)--terminator :: Parser ()-terminator = label "Node Terminator" $ do-  choice [try (void (char ';')), try (void newline), try lineComment, eof]--document :: Parser Document-document = do-  docNodes <- nodes-  void eof-  return Document { .. }
− src/Data/KDL/Parser/Hustle/Types.hs
@@ -1,73 +0,0 @@-{- FOURMOLU_DISABLE -}-{- | Vendered from https://github.com/fuzzypixelz/hustle -}-module Data.KDL.Parser.Hustle.Types-  ( Parser-  , Document(..)-  , Content(..)-  , Node(..)-  , Value(..)-  , ValueType(..)-  , Identifier(..)-  ) where--import           Data.Map                       ( Map )-import           Data.Scientific                ( Scientific )-import           Data.Text                      ( Text )-import           Data.Void                      ( Void )-import           Text.Megaparsec                ( Parsec )--{--  String has exactly one use, -  and that’s showing Hello World in tutorials.-                  -- Albert Einstein--}-type Parser = Parsec Void Text--newtype Document = Document-  { docNodes :: [Node]-  }-  deriving (Eq)--{- -  This data type serves as an abstraction over Values-  and Properties of a Node, in order simplify the the node-  Parser, i.e group the two types together to-  consume any number of them in any order. --}-data Content-  = NodeValue    { getValue :: Value }-  | NodeProperty { getProp :: (Identifier, Value) }-  deriving (Eq)--data Node = Node-  { nodeAnn      :: Maybe Identifier-  , nodeName     :: Identifier-  , nodeArgs     :: [Value]-  , nodeProps    :: Map Identifier Value-  , nodeChildren :: [Node]-  }-  deriving (Show, Eq)--newtype Identifier = Identifier Text-  deriving (Show, Eq)--data Value = Value-  { valueAnn :: Maybe Identifier-  , valueExp :: ValueType-  }-  deriving (Show, Eq)--data ValueType-  = StringValue Text-  | IntegerValue Integer-  | SciValue Scientific-  | BooleanValue Bool-  | NullValue-  deriving (Show, Eq)--{- -  This allows for querying properties in alphabetical order-  upon printing out, the rest is handled automatically by Map.--}-instance Ord Identifier where-  Identifier t1 `compare` Identifier t2 = t1 `compare` t2
− src/Data/KDL/Render.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedRecordDot #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}--module Data.KDL.Render (-  render,--  -- * Rendering components-  renderAnn,-  renderValue,-  renderValueData,-  renderIdentifier,-) where--import Data.Char (isDigit)-import Data.KDL.Types (-  Ann (..),-  AnnFormat (..),-  Document,-  Identifier (..),-  IdentifierFormat (..),-  Value (..),-  ValueData (..),-  ValueFormat (..),- )-import Data.Text (Text)-import Data.Text qualified as Text---- TODO: Implement render after parsing Document with formatting information-render :: Document -> Text-render = error "render is not implemented yet"--renderAnn :: Ann -> Text-renderAnn Ann{..} =-  Text.concat-    [ maybe "" (.leading) format-    , "("-    , maybe "" (.before_id) format-    , renderIdentifier identifier-    , maybe "" (.after_id) format-    , ")"-    , maybe "" (.trailing) format-    ]--renderValue :: Value -> Text-renderValue Value{..} =-  Text.concat-    [ maybe "" renderAnn ann-    , maybe (renderValueData data_) (.repr) format-    ]--renderValueData :: ValueData -> Text-renderValueData = \case-  Text s -> renderString s-  Number x -> (Text.pack . show) x-  Bool b -> if b then "#true" else "#false"-  Null -> "#null"- where-  renderString s = if isPlainIdent s then s else "\"" <> Text.concatMap escapeChar s <> "\""-  isPlainIdent s =-    and . map not $-      [ Text.any isDisallowedChar s-      , case fmap Text.uncons <$> Text.uncons s of-          Just (c, _) | isDigit c -> True-          Just (c0, Just (c1, _))-            | c0 `elem` ['.', '-', '+']-            , isDigit c1 ->-                True-          _ -> False-      , s `elem` ["inf", "-inf", "nan", "true", "false", "null"]-      ]-  isDisallowedChar c =-    or-      [ c `elem` disallowedIdentChars-      , any (c `Text.elem`) newlines-      , c `elem` unicodeSpaces-      , isDisallowedUnicode c-      , c == '='-      ]-  disallowedIdentChars = ['\\', '/', '(', ')', '{', '}', '[', ']', ';', '"', '#']-  newlines =-    [ "\x000D\x000A"-    , "\x000D"-    , "\x000A"-    , "\x0085"-    , "\x000B"-    , "\x000C"-    , "\x2028"-    , "\x2029"-    ]-  unicodeSpaces =-    [ '\x0009'-    , '\x0020'-    , '\x00A0'-    , '\x1680'-    , '\x2000'-    , '\x2001'-    , '\x2002'-    , '\x2003'-    , '\x2004'-    , '\x2005'-    , '\x2006'-    , '\x2007'-    , '\x2008'-    , '\x2009'-    , '\x200A'-    , '\x202F'-    , '\x205F'-    , '\x3000'-    ]-  isDisallowedUnicode c =-    or-      [ '\x0000' < c && c <= '\x0008'-      , '\x000E' < c && c <= '\x001F'-      , '\x200E' < c && c <= '\x200F'-      , '\x202A' < c && c <= '\x202E'-      , '\x2066' < c && c <= '\x2069'-      , c == '\xFEFF'-      ]-  escapeChar = \case-    '\\' -> "\\\\"-    '"' -> "\\\""-    '\n' -> "\\n"-    '\r' -> "\\r"-    '\t' -> "\\t"-    '\x08' -> "\\b"-    '\x0C' -> "\\f"-    c -> Text.singleton c--renderIdentifier :: Identifier -> Text-renderIdentifier ident = maybe ident.value (.repr) ident.format
− src/Data/KDL/Types.hs
@@ -1,367 +0,0 @@-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedRecordDot #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE NoFieldSelectors #-}--{-|-Defines the types that make up a KDL document.--This module enables @-XNoFieldSelectors@, so none of the fields create implicit-selector functions. Instead, use @-XOverloadedRecordDot@, or the functions-provided by this module.--}-module Data.KDL.Types (-  -- * Document-  Document,-  docNodes,--  -- * NodeList-  NodeList (..),-  NodeListFormat (..),-  fromNodeList,-  nodeListFormat,--  -- ** Helpers-  filterNodes,-  lookupNode,-  getArgAt,-  getArgsAt,-  getDashChildrenAt,-  getDashNodesAt,--  -- * Node-  Node (..),-  NodeFormat (..),-  nodeAnn,-  nodeName,-  nodeEntries,-  nodeChildren,-  nodeFormat,--  -- ** Helpers-  getArgs,-  getArg,-  getProps,-  getProp,--  -- * Entry-  Entry (..),-  EntryFormat (..),-  entryName,-  entryValue,-  entryFormat,--  -- * Value-  Value (..),-  ValueFormat (..),-  valueAnn,-  valueData,-  valueFormat,-  ValueData (..),--  -- * Ann-  Ann (..),-  AnnFormat (..),-  annIdentifier,-  annFormat,--  -- * Identifier-  Identifier (..),-  IdentifierFormat (..),-  fromIdentifier,-  identifierFormat,-  toIdentifier,-) where--import Control.Monad ((<=<))-import Data.Map (Map)-import Data.Map qualified as Map-import Data.Maybe (listToMaybe, mapMaybe)-import Data.Scientific (Scientific)-import Data.Text (Text)--{----- Document -----}--type Document = NodeList--docNodes :: Document -> [Node]-docNodes = fromNodeList--{----- NodeList -----}--data NodeList = NodeList-  { nodes :: [Node]-  , format :: Maybe NodeListFormat-  }-  deriving (Show, Eq)--data NodeListFormat = NodeListFormat-  { leading :: Text-  -- ^ Whitespace and comments preceding the document's first node.-  , trailing :: Text-  -- ^ Whitespace and comments following the document's last node.-  }-  deriving (Show, Eq)--fromNodeList :: NodeList -> [Node]-fromNodeList = (.nodes)--nodeListFormat :: NodeList -> Maybe NodeListFormat-nodeListFormat = (.format)---- | A helper to get all nodes with the given name-filterNodes :: Text -> NodeList -> [Node]-filterNodes name = filter ((== name) . (.name.value)) . (.nodes)---- | A helper to get the first node with the given name-lookupNode :: Text -> NodeList -> Maybe Node-lookupNode name = listToMaybe . filterNodes name---- | A helper to get the first argument of the first node with the given name.--- A utility for nodes that are acting like a key-value store.------ == __Example__------ @--- let---   config =---     """---     foo 1---     """--- Right doc <- pure $ parse config--- getArgAt "foo" doc == Just (Number 1)--- @-getArgAt :: Text -> NodeList -> Maybe Value-getArgAt name = listToMaybe . getArgsAt name---- | A helper to get 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.------ == __Example__------ @--- let---   config =---     """---     foo 1 2 "test"---     """--- Right doc <- pure $ parse config--- getArgsAt "foo" doc == [Number 1, Number 2, Text "test"]--- @-getArgsAt :: Text -> NodeList -> [Value]-getArgsAt name = maybe [] getArgs . lookupNode name---- | A helper for getting child values following the KDL convention of being named "-".------ == __Example__------ @--- let---   config =---     """---     foo {---       - 1---       - 2---       - "test"---     }---     """--- Right doc <- pure $ parse config--- getDashChildrenAt "foo" doc == [Number 1, Number 2, Text "test"]--- @-getDashChildrenAt :: Text -> NodeList -> [Value]-getDashChildrenAt name = mapMaybe getArg . getDashNodesAt name---- | A helper for getting child nodes following the KDL convention of being named "-".------ == __Example__------ @--- let---   config =---     """---     foo {---       - 1---       - 2---       - "test"---     }---     """--- Right doc <- pure $ parse config--- mapM getArg (getDashNodesAt "foo" doc) == Just [Number 1, Number 2, Text "test"]--- @-getDashNodesAt :: Text -> NodeList -> [Node]-getDashNodesAt name = maybe [] (filterNodes "-") . (nodeChildren <=< lookupNode name)--{----- Ann -----}--data Ann = Ann-  { identifier :: Identifier-  , format :: Maybe AnnFormat-  }-  deriving (Show, Eq)--data AnnFormat = AnnFormat-  { leading :: Text-  -- ^ Whitespace and comments preceding the annotation itself.-  , before_id :: Text-  -- ^ Whitespace and comments between the opening `(` and the identifier.-  , after_id :: Text-  -- ^ Whitespace and comments between the identifier and the closing `)`.-  , trailing :: Text-  -- ^ Whitespace and comments following the annotation itself.-  }-  deriving (Show, Eq)--annIdentifier :: Ann -> Identifier-annIdentifier = (.identifier)--annFormat :: Ann -> Maybe AnnFormat-annFormat = (.format)--{----- Node -----}--data Node = Node-  { ann :: Maybe Ann-  , name :: Identifier-  , entries :: [Entry]-  , children :: Maybe NodeList-  , format :: Maybe NodeFormat-  }-  deriving (Show, Eq)--data NodeFormat = NodeFormat-  { leading :: Text-  -- ^ Whitespace and comments preceding the node itself.-  , before_children :: Text-  -- ^ Whitespace and comments preceding the node's children block.-  , before_terminator :: Text-  -- ^ Whitespace and comments right before the node's terminator.-  , terminator :: Text-  -- ^ The terminator for the node.-  , trailing :: Text-  -- ^ Whitespace and comments following the node, after the terminator.-  }-  deriving (Show, Eq)--nodeAnn :: Node -> Maybe Ann-nodeAnn = (.ann)--nodeName :: Node -> Identifier-nodeName = (.name)--nodeEntries :: Node -> [Entry]-nodeEntries = (.entries)--nodeChildren :: Node -> Maybe NodeList-nodeChildren = (.children)--nodeFormat :: Node -> Maybe NodeFormat-nodeFormat = (.format)---- | Get all the positional arguments of the node.-getArgs :: Node -> [Value]-getArgs node =-  [ value-  | Entry{name = Nothing, value} <- node.entries-  ]---- | Get the first argument of the node.-getArg :: Node -> Maybe Value-getArg = listToMaybe . getArgs---- | Get the properties of the node.-getProps :: Node -> Map Text Value-getProps node =-  Map.fromList-    [ (name.value, value)-    | Entry{name = Just name, value} <- node.entries-    ]---- | Get the property with the given name in the node.-getProp :: Text -> Node -> Maybe Value-getProp name = Map.lookup name . getProps--{----- Entry -----}--data Entry = Entry-  { name :: Maybe Identifier-  -- ^ The name of the entry, if it's a property, Nothing if it's a positional arg-  , value :: Value-  , format :: Maybe EntryFormat-  }-  deriving (Show, Eq)--data EntryFormat = EntryFormat-  { leading :: Text-  -- ^ Whitespace and comments preceding the entry itself.-  , after_key :: Text-  -- ^ Whitespace and comments between an entry's key name and its equals sign.-  , after_eq :: Text-  -- ^ Whitespace and comments between an entry's equals sign and its value.-  , trailing :: Text-  -- ^ Whitespace and comments following the entry itself.-  }-  deriving (Show, Eq)--entryName :: Entry -> Maybe Identifier-entryName = (.name)--entryValue :: Entry -> Value-entryValue = (.value)--entryFormat :: Entry -> Maybe EntryFormat-entryFormat = (.format)--{----- Value -----}--data Value = Value-  { ann :: Maybe Ann-  , data_ :: ValueData-  , format :: Maybe ValueFormat-  }-  deriving (Show, Eq)--data ValueFormat = ValueFormat-  { repr :: Text-  -- ^ The actual text representation of the value.-  }-  deriving (Show, Eq)--valueAnn :: Value -> Maybe Ann-valueAnn = (.ann)--valueData :: Value -> ValueData-valueData = (.data_)--valueFormat :: Value -> Maybe ValueFormat-valueFormat = (.format)--data ValueData-  = Text Text-  | Number Scientific-  | Bool Bool-  | Null-  deriving (Show, Eq)--{----- Identifier -----}--data Identifier = Identifier-  { value :: Text-  , format :: Maybe IdentifierFormat-  }-  deriving (Show, Eq, Ord)--data IdentifierFormat = IdentifierFormat-  { repr :: Text-  }-  deriving (Show, Eq, Ord)--fromIdentifier :: Identifier -> Text-fromIdentifier = (.value)--identifierFormat :: Identifier -> Maybe IdentifierFormat-identifierFormat = (.format)--toIdentifier :: Text -> Identifier-toIdentifier value = Identifier{value = value, format = Nothing}
+ src/KDL.hs view
@@ -0,0 +1,74 @@+{-|+This module is intended to be imported qualified as:++> import KDL qualified++This provides a Monad interface for decoding KDL files, which is sufficient for+most cases. You may wish to use "KDL.Arrow" if you would like to+statically analyze a decoder's schema, e.g. to generate documentation.++= Quickstart++Given a file @config.kdl@:++@+package {+  name my-pkg+  version "1.2.3"++  dependencies {+    aeson ">= 2.2.3.0" optional=#true+    text ">= 2"+  }+}+@++Parse it with @kdl-hs@:++@+import KDL qualified++main :: IO ()+main = do+  config <- KDL.decodeFileWith decoder "config.kdl"+  print config++decoder :: KDL.DocumentDecoder Config+decoder = KDL.document $ do+  KDL.node "package"++data Config = Config+  { name :: Text+  , version :: Text+  , dependencies :: Map Text Dep+  }+  deriving (Show)++data Dep = Dep+  { version :: Text+  , optional :: Bool+  }+  deriving (Show)++instance KDL.DecodeNode Config where+  nodeDecoder = do+    name <- KDL.argAt "name"+    version <- KDL.argAt "version"+    dependencies <- KDL.nodeWith "dependencies" . KDL.children $ KDL.remainingNodes+    pure Config{..}++instance KDL.DecodeNode Dep where+  nodeDecoder = do+    version <- KDL.arg+    optional <- KDL.option False $ KDL.prop "optional"+    pure Dep{..}+@+-}+module KDL (+  module X,+) where++import KDL.Decoder as X+import KDL.Parser as X+import KDL.Render as X+import KDL.Types as X
+ src/KDL/Arrow.hs view
@@ -0,0 +1,78 @@+{-|+This module defines the Arrow interface for decoding a KDL document. Intended to+be imported qualified as:++> import KDL.Arrow qualified as KDL++For most use-cases, the Monad interface exported by "KDL" is sufficient. You+may wish to use the Arrow interface if you would like to statically analyze a+decoder's schema, e.g. to generate documentation.++= Quickstart++Given a file @config.kdl@:++@+package {+  name my-pkg+  version "1.2.3"++  dependencies {+    aeson ">= 2.2.3.0" optional=#true+    text ">= 2"+  }+}+@++Parse it with:++@+{\-# LANGUAGE Arrows #-\}++import KDL.Arrow qualified as KDL++main :: IO ()+main = do+  config <- KDL.decodeFileWith decoder "config.kdl"+  print config++decoder :: KDL.DocumentDecoder Config+decoder = KDL.document $ proc () -> do+  KDL.node "package" -< ()++data Config = Config+  { name :: Text+  , version :: Text+  , dependencies :: Map Text Dep+  }+  deriving (Show)++data Dep = Dep+  { version :: Text+  , optional :: Bool+  }+  deriving (Show)++instance KDL.DecodeNode Config where+  nodeDecoder = proc () -> do+    name <- KDL.argAt "name" -< ()+    version <- KDL.argAt "version" -< ()+    dependencies <- KDL.nodeWith "dependencies" . KDL.children $ KDL.remainingNodes -< ()+    returnA -< Config{..}++instance KDL.DecodeNode Dep where+  nodeDecoder = proc () -> do+    version <- KDL.arg -< ()+    optional <- KDL.option False $ KDL.prop "optional" -< ()+    returnA -< Dep{..}+@+-}+module KDL.Arrow (+  module X,+) where++import KDL.Decoder.Arrow as X+import KDL.Decoder.Schema as X+import KDL.Parser as X+import KDL.Render as X+import KDL.Types as X
+ src/KDL/Decoder.hs view
@@ -0,0 +1,10 @@+module KDL.Decoder (+  -- * Decoder monad+  module KDL.Decoder.Monad,++  -- * Schema+  module KDL.Decoder.Schema,+) where++import KDL.Decoder.Monad+import KDL.Decoder.Schema
+ src/KDL/Decoder/Arrow.hs view
@@ -0,0 +1,1060 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}++module KDL.Decoder.Arrow (+  decodeWith,+  decodeFileWith,+  decodeDocWith,++  -- * Decoder+  Decoder,+  module KDL.Decoder.Internal.DecodeM,+  fail,+  withDecoder,+  debug,++  -- * Document+  DocumentDecoder (..),+  document,+  documentSchema,++  -- * NodeList+  NodeListDecoder,+  NodeListDecodeArrow,+  node,+  remainingNodes,+  argAt,+  argsAt,+  dashChildrenAt,+  dashNodesAt,++  -- ** Explicitly specify decoders+  nodeWith,+  remainingNodesWith,+  argAtWith,+  argsAtWith,+  dashChildrenAtWith,+  dashNodesAtWith,++  -- ** Explicitly specify decoders and type annotations+  nodeWith',+  remainingNodesWith',+  argAtWith',+  argsAtWith',+  dashChildrenAtWith',++  -- * Node+  NodeDecoder,+  NodeDecodeArrow,+  DecodeNode (..),+  arg,+  prop,+  remainingProps,+  children,++  -- ** Explicitly specify decoders+  argWith,+  propWith,+  remainingPropsWith,++  -- ** Explicitly specify decoders and type annotations+  argWith',+  propWith',+  remainingPropsWith',++  -- * Value+  ValueDecoder,+  ValueDecodeArrow,+  DecodeValue (..),+  any,+  text,+  number,+  bool,+  null,++  -- * Combinators+  oneOf,+  many,+  optional,+  option,+  some,+) where++import Control.Applicative (+  Alternative (..),+  optional,+ )+import Control.Monad (unless, when)+import Control.Monad.Trans.Class qualified as Trans+import Control.Monad.Trans.State.Strict (StateT)+import Control.Monad.Trans.State.Strict qualified as StateT+import Data.Bits (finiteBitSize)+import Data.Int (Int64)+import Data.List (partition)+import Data.List.NonEmpty qualified as NonEmpty+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Maybe (fromMaybe, isNothing)+import Data.Proxy (Proxy (..))+import Data.Scientific (Scientific)+import Data.Scientific qualified as Scientific+import Data.Set qualified as Set+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Typeable (Typeable, typeRep)+import Data.Word (Word16, Word32, Word64, Word8)+import GHC.Int (Int16, Int32, Int8)+import KDL.Decoder.Internal.DecodeM+import KDL.Decoder.Internal.Decoder+import KDL.Decoder.Schema (+  Schema (..),+  SchemaItem (..),+  SchemaOf,+  TypedNodeSchema (..),+  TypedValueSchema (..),+ )+import KDL.Parser (parse, parseFile)+import KDL.Types (+  Ann (..),+  Document,+  Entry (..),+  Identifier (..),+  Node (..),+  NodeList (..),+  Value (..),+  ValueData (..),+ )+import Numeric.Natural (Natural)+import Prelude hiding (any, fail, null)+import Prelude qualified++-- | Decode the given KDL configuration with the given decoder.+decodeWith :: DocumentDecoder a -> Text -> Either DecodeError a+decodeWith decoder = decodeFromParseResult decoder . parse++-- | Read KDL configuration from the given file path and decode it with the given decoder.+decodeFileWith :: DocumentDecoder a -> FilePath -> IO (Either DecodeError a)+decodeFileWith decoder = fmap (decodeFromParseResult decoder) . parseFile++-- | Decode an already-parsed 'Document' with the given decoder.+decodeDocWith :: DocumentDecoder a -> Document -> Either DecodeError a+decodeDocWith (UnsafeDocumentDecoder decoder) doc =+  runDecodeM . runDecodeStateM doc emptyDecodeHistory $+    decoder.run ()++decodeFromParseResult :: DocumentDecoder a -> Either Text Document -> Either DecodeError a+decodeFromParseResult decoder = \case+  Left e -> runDecodeM . decodeThrow $ DecodeError_ParseError e+  Right doc -> decodeDocWith decoder doc++{----- Decoder -----}++{----- Decoding Document -----}++newtype DocumentDecoder a = UnsafeDocumentDecoder (NodeListDecoder a)++-- | 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.+document :: NodeListDecoder a -> DocumentDecoder a+document decoder =+  UnsafeDocumentDecoder+    decoder+      { run = \() -> do+          a <- decoder.run ()+          validateNodeList+          pure a+      }++-- | Get the schema of a 'DocumentDecoder'.+--+-- The schema is statically determined without running the decoder.+documentSchema :: DocumentDecoder a -> SchemaOf NodeList+documentSchema (UnsafeDocumentDecoder decoder) = decoder.schema++{----- Decoding NodeList -----}++type NodeListDecoder a = NodeListDecodeArrow () a+type NodeListDecodeArrow a b = DecodeArrow NodeList a b++getNodeIndex :: Text -> DecodeState NodeList -> Int+getNodeIndex name = Map.findWithDefault 0 name . (.history.nodesSeen)++validateNodeList :: StateT (DecodeState NodeList) DecodeM ()+validateNodeList = do+  nodeList <- StateT.gets (.object)+  case nodeList.nodes of+    [] -> pure ()+    node_ : _ -> do+      let identifier = node_.name+      index <- StateT.gets (getNodeIndex identifier.value)+      Trans.lift . decodeThrow $+        DecodeError_UnexpectedNode+          { identifier = identifier+          , index = index+          }++-- | 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__+--+-- @+-- 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"]+-- @+node :: (DecodeNode a) => Text -> NodeListDecoder a+node name = withDecodeNode $ nodeWith' name++-- | Same as 'node', except explicitly specify the 'NodeDecoder' instead of using 'DecodeNode'.+--+-- === __Example__+--+-- @+-- let+--   config =+--     """+--     person \"Alice"+--     person \"Bob"+--     """+--   decoder = KDL.document $ proc () -> do+--     many . KDL.nodeWith "person" $ KDL.arg -< ()+-- KDL.decodeWith decoder config == Right [\"Alice", \"Bob"]+-- @+nodeWith :: forall a b. (Typeable b) => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a b+nodeWith name = nodeWith' name []++-- | Same as 'nodeWith', except allow specifying type annotations.+nodeWith' :: forall a b. (Typeable b) => Text -> [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a b+nodeWith' name =+  withTypedNodeDecoder $ \schema decodeNode ->+    DecodeArrow (SchemaOne $ NodeNamed name schema) $ \a -> do+      decodeFirstNodeWhere ((== name) . (.name.value)) (decodeNode a) >>= \case+        Just (_, b) -> pure b+        Nothing -> do+          index <- StateT.gets (getNodeIndex name)+          Trans.lift $ decodeThrow DecodeError_ExpectedNode{name = name, index = index}++decodeFirstNodeWhere ::+  (Node -> Bool) ->+  (Node -> DecodeM a) ->+  StateT (DecodeState NodeList) DecodeM (Maybe (Node, a))+decodeFirstNodeWhere matcher decodeNode = do+  nodes <- StateT.gets (.object.nodes)+  case extractFirst matcher nodes of+    Nothing -> do+      pure Nothing+    Just (node_, nodes') -> do+      let name = node_.name+      index <- StateT.gets (getNodeIndex name.value)+      StateT.modify $ \s -> s{object = s.object{nodes = nodes'}}+      b <-+        Trans.lift . makeFatal . addContext ContextNode{name = name, index = index} $+          decodeNode node_+      StateT.modify $ \s -> s{history = s.history{nodesSeen = inc name.value s.history.nodesSeen}}+      pure $ Just (node_, b)+ where+  inc k = Map.insertWith (+) k 1++-- | Decode all remaining nodes.+--+-- === __Example__+--+-- @+-- 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"])]+-- @+remainingNodes :: (DecodeNode a) => NodeListDecoder (Map Text [a])+remainingNodes = withDecodeNode remainingNodesWith'++-- | Same as 'remainingNodes', except explicitly specify the 'NodeDecoder' instead of using 'DecodeNode'+--+-- === __Example__+--+-- @+-- 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"])]+-- @+remainingNodesWith :: forall a b. (Typeable b) => NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b])+-- TODO: Detect duplicate `remainingNodes` calls and fail to build a decoder+remainingNodesWith = remainingNodesWith' []++-- | Same as 'remainingNodesWith', except allow specifying type annotations.+remainingNodesWith' :: forall a b. (Typeable b) => [Text] -> NodeDecodeArrow a b -> NodeListDecodeArrow a (Map Text [b])+-- TODO: Detect duplicate `remainingNodes` calls and fail to build a decoder+remainingNodesWith' =+  withTypedNodeDecoder $ \schema decodeNode ->+    DecodeArrow (SchemaOne $ RemainingNodes schema) $ \a -> do+      Map.fromListWith (<>) <$> go (decodeNode a)+ where+  go decodeNode = do+    decodeFirstNodeWhere (const True) decodeNode >>= \case+      Nothing -> pure []+      Just (node_, b) -> do+        nodeMap <- go decodeNode+        pure $ (node_.name.value, [b]) : nodeMap++-- | 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__+--+-- @+-- let+--   config =+--     """+--     verbose #true+--     """+--   decoder = KDL.document $ proc () -> do+--     KDL.argAt "verbose" -< ()+-- KDL.decodeWith decoder config == Right True+-- @+argAt :: (DecodeValue a) => Text -> NodeListDecoder a+argAt name = withDecodeValue $ argAtWith' name++-- | Same as 'argAt', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'+--+-- === __Example__+--+-- @+-- let+--   config =+--     """+--     verbose #true+--     """+--   decoder = KDL.document $ proc () -> do+--     KDL.argAtWith "verbose" KDL.bool -< ()+-- KDL.decodeWith decoder config == Right True+-- @+argAtWith :: forall a b. (Typeable b) => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a b+argAtWith name = argAtWith' name []++-- | Same as 'argAtWith', except allow specifying type annotations.+argAtWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a b+argAtWith' name typeAnns decoder = nodeWith name $ argWith' typeAnns decoder++-- | 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__+--+-- @+-- let+--   config =+--     """+--     email "a@example.com" "b@example.com"+--     """+--   decoder = KDL.document $ proc () -> do+--     KDL.argsAt "email" -< ()+-- KDL.decodeWith decoder config == Right ["a@example.com", "b@example.com"]+-- @+argsAt :: (DecodeValue a) => Text -> NodeListDecoder [a]+argsAt name = withDecodeValue $ argsAtWith' name++-- | Same as 'argsAt', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'+--+-- === __Example__+--+-- @+-- let+--   config =+--     """+--     email "a@example.com" "b@example.com"+--     """+--   decoder = KDL.document $ proc () -> do+--     KDL.argsAtWith "email" KDL.text -< ()+-- KDL.decodeWith decoder config == Right ["a@example.com", "b@example.com"]+-- @+argsAtWith :: forall a b. (Typeable b) => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]+argsAtWith name = argsAtWith' name []++-- | Same as 'argsAtWith', except allow specifying type annotations.+argsAtWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]+argsAtWith' name typeAnns decoder = option [] $ nodeWith name $ many $ argWith' typeAnns decoder++-- | 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__+--+-- @+-- let+--   config =+--     """+--     attendees {+--       - \"Alice"+--       - \"Bob"+--     }+--     """+--   decoder = KDL.document $ proc () -> do+--     KDL.dashChildrenAt "attendees" -< ()+-- KDL.decodeWith decoder config == Right [\"Alice", \"Bob"]+-- @+dashChildrenAt :: (DecodeValue a) => Text -> NodeListDecoder [a]+dashChildrenAt name = withDecodeValue $ dashChildrenAtWith' name++-- | Same as 'dashChildrenAt', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'+--+-- === __Example__+--+-- @+-- let+--   config =+--     """+--     attendees {+--       - \"Alice"+--       - \"Bob"+--     }+--     """+--   decoder = KDL.document $ proc () -> do+--     KDL.dashChildrenAtWith "attendees" $ KDL.text -< ()+-- KDL.decodeWith decoder config == Right [\"Alice", \"Bob"]+-- @+dashChildrenAtWith :: forall a b. (Typeable b) => Text -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]+dashChildrenAtWith name = dashChildrenAtWith' name []++-- | Same as 'dashChildrenAtWith', except allow specifying type annotations.+dashChildrenAtWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecodeArrow a b -> NodeListDecodeArrow a [b]+dashChildrenAtWith' name typeAnns decoder = dashNodesAtWith name $ argWith' typeAnns decoder++-- | 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__+--+-- @+-- 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"]+-- @+dashNodesAt :: (DecodeNode a) => Text -> NodeListDecoder [a]+dashNodesAt name = withDecodeNode $ \_ decoder -> dashNodesAtWith name decoder++-- | Same as 'dashChildrenAt', except explicitly specify the 'NodeDecoder' instead of using 'DecodeNode'+--+-- === __Example__+--+-- @+-- let+--   config =+--     """+--     attendees {+--       - \"Alice"+--       - \"Bob"+--     }+--     """+--   decoder = KDL.document $ proc () -> do+--     KDL.dashNodesAtWith "attendees" KDL.arg -< ()+-- KDL.decodeWith decoder config == Right ["Alice", "Bob"]+-- @+dashNodesAtWith :: forall a b. (Typeable b) => Text -> NodeDecodeArrow a b -> NodeListDecodeArrow a [b]+dashNodesAtWith name decoder =+  option [] . nodeWith name $+    children $+      many (nodeWith "-" decoder)++{----- Decoding Ann -----}++validateAnn :: [Text] -> Maybe Ann -> DecodeM ()+validateAnn typeAnns mGivenAnn =+  case mGivenAnn of+    Nothing -> pure ()+    Just givenAnn -> do+      let isValidAnn = Prelude.null typeAnns || givenAnn.identifier.value `elem` typeAnns+      unless isValidAnn $ do+        decodeThrow DecodeError_MismatchedAnn{givenAnn = givenAnn.identifier, validAnns = typeAnns}++{----- Decoding Node -----}++withDecodeNode :: forall a r. (DecodeNode a) => ([Text] -> NodeDecoder a -> r) -> r+withDecodeNode k = k (validNodeTypeAnns (Proxy @a)) nodeDecoder++withTypedNodeDecoder ::+  forall a b r.+  (Typeable b) =>+  (TypedNodeSchema -> (a -> Node -> DecodeM b) -> r) ->+  ([Text] -> NodeDecodeArrow a b -> r)+withTypedNodeDecoder k typeAnns decoder = k schema decodeNode+ where+  schema =+    TypedNodeSchema+      { typeHint = typeRep (Proxy @b)+      , validTypeAnns = typeAnns+      , nodeSchema = decoder.schema+      }+  decodeNode a node_ = do+    validateAnn typeAnns node_.ann+    runDecodeStateM node_ emptyDecodeHistory $ do+      -- TODO: add typeHint to Context+      decoder.run a <* validateNode++validateNode :: StateT (DecodeState Node) DecodeM ()+validateNode = do+  node_ <- StateT.gets (.object)+  case node_.entries of+    [] -> pure ()+    Entry{name = Nothing, value} : _ -> do+      index <- StateT.gets getArgIndex+      Trans.lift . decodeThrow $+        DecodeError_UnexpectedArg+          { index = index+          , value = value+          }+    Entry{name = Just identifier, value} : _ -> do+      Trans.lift . decodeThrow $+        DecodeError_UnexpectedProp+          { identifier = identifier+          , value = value+          }+  case node_.children of+    Nothing -> pure ()+    Just children_ -> do+      childrenHistory <- StateT.gets (.history.childrenHistory)+      Trans.lift . runDecodeStateM children_ childrenHistory $ validateNodeList++type NodeDecoder a = NodeDecodeArrow () a+type NodeDecodeArrow a b = DecodeArrow Node a b++getArgIndex :: DecodeState Node -> Int+getArgIndex = (.history.argsSeen)++-- | The type class for specifying how a type should be decoded from a KDL node.+class (Typeable a) => DecodeNode a where+  -- | 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__+  --+  -- @+  -- instance DecodeNode Person where+  --   validNodeTypeAnns _ = ["person"]+  -- @+  validNodeTypeAnns :: Proxy a -> [Text]+  validNodeTypeAnns _ = []++  -- | Decode a t'Node' to a value of type @a@+  nodeDecoder :: NodeDecoder a++instance DecodeNode Node where+  nodeDecoder =+    DecodeArrow SchemaUnknown $ \() -> do+      node_ <- StateT.gets (.object)+      StateT.modify $ \s -> s{object = emptyNode node_.name}+      pure node_+   where+    emptyNode name =+      Node+        { ann = Nothing+        , name = name+        , entries = []+        , children = Nothing+        , format = Nothing+        }++-- | Decode an argument in the node.+--+-- === __Example__+--+-- @+-- 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])+-- @+arg :: (DecodeValue a) => NodeDecoder a+arg = withDecodeValue argWith'++-- | Same as 'arg', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'+--+-- === __Example__+--+-- @+-- 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.text -< ()+--     vals \<- KDL.many $ KDL.argWith $ show \<$> KDL.valueDecoder @Int -< ()+--     returnA -< (name, vals)+-- KDL.decodeWith decoder config == Right (\"ALICE", ["1", "2", "3"])+-- @+argWith :: forall a b. (Typeable b) => ValueDecodeArrow a b -> NodeDecodeArrow a b+argWith = argWith' []++-- | Same as 'argWith', except allow specifying type annotations.+argWith' :: forall a b. (Typeable b) => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b+argWith' =+  withTypedValueDecoder $ \schema decodeValue ->+    DecodeArrow (SchemaOne $ NodeArg schema) $ \a -> do+      index <- StateT.gets getArgIndex++      entries <- StateT.gets (.object.entries)+      (entry, entries') <-+        maybe (Trans.lift $ decodeThrow DecodeError_ExpectedArg{index = index}) pure $+          extractFirst (isNothing . (.name)) entries+      StateT.modify $ \s -> s{object = s.object{entries = entries'}}++      b <-+        Trans.lift . makeFatal . addContext ContextArg{index = index} $+          decodeValue a entry.value+      StateT.modify $ \s -> s{history = s.history{argsSeen = s.history.argsSeen + 1}}+      pure b++-- | 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__+--+-- @+-- 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+-- @+prop :: (DecodeValue a) => Text -> NodeDecoder a+prop name = withDecodeValue $ propWith' name++-- | Same as 'prop', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'+--+-- === __Example__+--+-- @+-- 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"+-- @+propWith :: forall a b. (Typeable b) => Text -> ValueDecodeArrow a b -> NodeDecodeArrow a b+propWith name = propWith' name []++-- | Same as 'propWith', except allow specifying type annotations.+propWith' :: forall a b. (Typeable b) => Text -> [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a b+propWith' name =+  withTypedValueDecoder $ \schema decodeValue ->+    DecodeArrow (SchemaOne $ NodeProp name schema) $ \a -> do+      decodeOnePropWhere (== name) (decodeValue a)+        >>= maybe (Trans.lift $ decodeThrow DecodeError_ExpectedProp{name = name}) (pure . snd)++decodeOnePropWhere ::+  (Text -> Bool) ->+  (Value -> DecodeM a) ->+  StateT (DecodeState Node) DecodeM (Maybe (Identifier, a))+decodeOnePropWhere matcher decodeValue = do+  entries <- StateT.gets (.object.entries)+  case findProp entries of+    Nothing -> pure Nothing+    Just (name, prop_, entries') -> do+      StateT.modify $ \s -> s{object = s.object{entries = entries'}}+      b <-+        Trans.lift . makeFatal . addContext ContextProp{name = name} $+          decodeValue prop_.value+      StateT.modify $ \s -> s{history = s.history{propsSeen = Set.insert name s.history.propsSeen}}+      pure $ Just (name, b)+ where+  isPropWhere f entry = (f . (.value) <$> entry.name) == Just True+  findProp entries =+    case break (isPropWhere matcher) entries of+      (entries1, prop0@Entry{name = Just name} : remainingEntries) ->+        -- Collect remaining props with the same name, latter props override earlier props+        let (props, entries2) = partition (isPropWhere (== name.value)) remainingEntries+         in Just (name, NonEmpty.last $ prop0 NonEmpty.:| props, entries1 <> entries2)+      _ ->+        Nothing++-- | Decode all remaining props.+--+-- === __Example__+--+-- @+-- 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)]+-- @+remainingProps :: (DecodeValue a) => NodeDecoder (Map Text a)+remainingProps = withDecodeValue remainingPropsWith'++-- | Same as 'remainingProps', except explicitly specify the 'ValueDecoder' instead of using 'DecodeValue'+--+-- === __Example__+--+-- @+-- 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")]+-- @+remainingPropsWith :: forall a b. (Typeable b) => ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b)+remainingPropsWith = remainingPropsWith' []++-- | Same as 'remainingPropsWith', except allow specifying type annotations.+remainingPropsWith' :: forall a b. (Typeable b) => [Text] -> ValueDecodeArrow a b -> NodeDecodeArrow a (Map Text b)+remainingPropsWith' =+  withTypedValueDecoder $ \schema decodeValue ->+    DecodeArrow (SchemaOne $ NodeRemainingProps schema) $ \a -> do+      Map.fromList <$> go (decodeValue a)+ where+  go decodeValue =+    decodeOnePropWhere (const True) decodeValue >>= \case+      Nothing -> pure []+      Just (name, b) -> do+        propMap <- go decodeValue+        pure $ (name.value, b) : propMap++-- | Decode the children of the node.+--+-- === __Example__+--+-- @+-- 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"}+-- @+children :: forall a b. NodeListDecodeArrow a b -> NodeDecodeArrow a b+children decoder =+  DecodeArrow (SchemaOne $ NodeChildren decoder.schema) $ \a -> do+    mChildren <- StateT.gets (.object.children)+    childrenHistory <- StateT.gets (.history.childrenHistory)+    (b, decodeState) <-+      Trans.lift $+        StateT.runStateT (decoder.run a) $+          DecodeState+            { object = fromMaybe emptyNodeList mChildren+            , history = childrenHistory+            }+    StateT.modify $ \s ->+      s+        { object = s.object{children = decodeState.object <$ mChildren}+        , history = s.history{childrenHistory = decodeState.history}+        }+    pure b+ where+  emptyNodeList = NodeList{nodes = [], format = Nothing}++{----- Decoding ValueData -----}++withDecodeValue :: forall a r. (DecodeValue a) => ([Text] -> ValueDecoder a -> r) -> r+withDecodeValue k = k (validValueTypeAnns (Proxy @a)) valueDecoder++withTypedValueDecoder ::+  forall a b r.+  (Typeable b) =>+  (TypedValueSchema -> (a -> Value -> DecodeM b) -> r) ->+  ([Text] -> ValueDecodeArrow a b -> r)+withTypedValueDecoder k typeAnns decoder = k schema decodeValue+ where+  schema =+    TypedValueSchema+      { typeHint = typeRep (Proxy @b)+      , validTypeAnns = typeAnns+      , dataSchema = decoder.schema+      }+  decodeValue a value = do+    validateAnn typeAnns value.ann+    runDecodeStateM value emptyDecodeHistory $ do+      -- TODO: add typeHint to Context+      decoder.run a++type ValueDecoder a = ValueDecodeArrow () a+type ValueDecodeArrow a b = DecodeArrow Value a b++-- | The type class for specifying how a type should be decoded from a KDL value.+class (Typeable a) => DecodeValue a where+  -- | 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__+  --+  -- @+  -- instance DecodeValue Age where+  --   validValueTypeAnns _ = ["age"]+  -- @+  validValueTypeAnns :: Proxy a -> [Text]+  validValueTypeAnns _ = []++  -- | Decode a t'Value' to a value of type @a@+  --+  -- Helpers that may be useful:+  --+  --   * 'oneOf'+  --   * 'withDecoder'+  --   * 'failM'+  valueDecoder :: ValueDecoder a++instance DecodeValue Value where+  valueDecoder = any+instance DecodeValue ValueData where+  valueDecoder = (.data_) <$> any+instance DecodeValue Text where+  validValueTypeAnns _ = ["text"]+  valueDecoder = text+instance DecodeValue String where+  validValueTypeAnns _ = ["string"]+  valueDecoder = Text.unpack <$> text+instance DecodeValue Bool where+  validValueTypeAnns _ = ["bool", "boolean"]+  valueDecoder = bool+instance (DecodeValue a) => DecodeValue (Maybe a) where+  validValueTypeAnns _ = validValueTypeAnns (Proxy @a)+  valueDecoder = oneOf [Nothing <$ null, Just <$> valueDecoder]+instance (DecodeValue a, DecodeValue b) => DecodeValue (Either a b) where+  validValueTypeAnns _ = validValueTypeAnns (Proxy @a) <> validValueTypeAnns (Proxy @b)+  valueDecoder = oneOf [Left <$> valueDecoder, Right <$> valueDecoder]++decodeInt :: (Integral b, Bounded b) => DecodeArrow Value a b+decodeInt = withDecoder number $ \x -> do+  unless (Scientific.isInteger x) $ do+    failM $ "Expected integer, got: " <> (Text.pack . show) x+  maybe (failM $ "Number doesn't fit bounds: " <> (Text.pack . show) x) pure $+    Scientific.toBoundedInteger x+instance DecodeValue Integer where+  validValueTypeAnns _ =+    concat+      [ ["i8", "i16", "i32", "i64", "i128", "isize"]+      , ["u8", "u16", "u32", "u64", "u128", "usize"]+      ]+  valueDecoder = toInteger <$> decodeInt @Int64+instance DecodeValue Int where+  validValueTypeAnns _ =+    concat+      [ ["i8", "i16", "isize"]+      , if bits >= 32 then ["i32"] else []+      , if bits >= 64 then ["i64"] else []+      ]+   where+    bits = finiteBitSize (0 :: Int)+  valueDecoder = decodeInt+instance DecodeValue Int8 where+  validValueTypeAnns _ = ["i8"]+  valueDecoder = decodeInt+instance DecodeValue Int16 where+  validValueTypeAnns _ = ["i16"]+  valueDecoder = decodeInt+instance DecodeValue Int32 where+  validValueTypeAnns _ = ["i32"]+  valueDecoder = decodeInt+instance DecodeValue Int64 where+  validValueTypeAnns _ = ["i64"]+  valueDecoder = decodeInt+instance DecodeValue Word where+  validValueTypeAnns _ =+    concat+      [ ["u8", "u16", "usize"]+      , if bits >= 32 then ["u32"] else []+      , if bits >= 64 then ["u64"] else []+      ]+   where+    bits = finiteBitSize (0 :: Word)+  valueDecoder = decodeInt+instance DecodeValue Word8 where+  validValueTypeAnns _ = ["u8"]+  valueDecoder = decodeInt+instance DecodeValue Word16 where+  validValueTypeAnns _ = ["u16"]+  valueDecoder = decodeInt+instance DecodeValue Word32 where+  validValueTypeAnns _ = ["u32"]+  valueDecoder = decodeInt+instance DecodeValue Word64 where+  validValueTypeAnns _ = ["u64"]+  valueDecoder = decodeInt+instance DecodeValue Natural where+  validValueTypeAnns _ = ["u8", "u16", "u32", "u64", "usize"]+  valueDecoder = withDecoder (valueDecoder @Integer) $ \x -> do+    when (x < 0) $ do+      failM $ "Expected a non-negative number, got: " <> (Text.pack . show) x+    pure $ fromIntegral x++decodeRealFloat :: (RealFloat b) => DecodeArrow Value a b+decodeRealFloat = withDecoder number $ \x -> do+  either (\_ -> failM $ "Number is too small or too large: " <> (Text.pack . show) x) pure $+    Scientific.toBoundedRealFloat x+instance DecodeValue Scientific where+  validValueTypeAnns _ = ["f32", "f64", "decimal64", "decimal128"]+  valueDecoder = number+instance DecodeValue Float where+  validValueTypeAnns _ = ["f32"]+  valueDecoder = decodeRealFloat+instance DecodeValue Double where+  validValueTypeAnns _ = ["f64"]+  valueDecoder = decodeRealFloat+instance DecodeValue Rational where+  validValueTypeAnns _ = ["decimal64", "decimal128"]+  valueDecoder = withDecoder number $ \x -> do+    -- Use toBoundedRealFloat to guard against large values, but use+    -- toRational after checking to maintain precision+    case Scientific.toBoundedRealFloat @Double x of+      Right _ -> pure $ toRational x+      Left _ -> failM $ "Number is too small or too large: " <> (Text.pack . show) x++valueDataDecoderPrim :: SchemaOf Value -> (Value -> DecodeM b) -> DecodeArrow Value a b+valueDataDecoderPrim schema f = DecodeArrow schema $ \_ -> Trans.lift . f =<< StateT.gets (.object)++-- | Decode any value, without any possibility of failure.+any :: DecodeArrow Value a Value+any = valueDataDecoderPrim (SchemaOr $ map SchemaOne [minBound .. maxBound]) pure++-- | Decode a KDL text value.+text :: DecodeArrow Value a Text+text = valueDataDecoderPrim (SchemaOne TextSchema) $ \case+  Value{data_ = Text s} -> pure s+  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "text", value = v}++-- | Decode a KDL number value.+number :: DecodeArrow Value a Scientific+number = valueDataDecoderPrim (SchemaOne NumberSchema) $ \case+  Value{data_ = Number x} -> pure x+  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "number", value = v}++-- | Decode a KDL bool value.+bool :: DecodeArrow Value a Bool+bool = valueDataDecoderPrim (SchemaOne BoolSchema) $ \case+  Value{data_ = Bool x} -> pure x+  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "bool", value = v}++-- | Decode a KDL null value.+null :: DecodeArrow Value a ()+null = valueDataDecoderPrim (SchemaOne NullSchema) $ \case+  Value{data_ = Null} -> pure ()+  v -> decodeThrow DecodeError_ValueDecodeFail{expectedType = "null", value = v}++{----- Utilities -----}++-- | Return the first result that succeeds.+--+-- > oneOf [a, b, c] === a <|> b <|> c <|> empty+oneOf :: (Alternative f) => [f a] -> f a+oneOf = foldr (<|>) empty++-- | Return the given default value if the given action fails.+--+-- > option a f === f <|> pure a+option :: (Alternative f) => a -> f a -> f a+option a f = f <|> pure a++extractFirst :: (a -> Bool) -> [a] -> Maybe (a, [a])+extractFirst f = go+ where+  go = \case+    [] -> Nothing+    x : xs ->+      if f x+        then Just (x, xs)+        else fmap (x :) <$> go xs
+ src/KDL/Decoder/Internal/DecodeM.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++module KDL.Decoder.Internal.DecodeM (+  -- * Decoding errors+  module KDL.Decoder.Internal.Error,++  -- * DecodeM monad+  DecodeM (..),+  runDecodeM,+  decodeThrow,+  failM,+  makeFatal,+  makeNonFatal,+  addContext,+) where++import Control.Applicative (Alternative (..))+import Data.Text (Text)+import KDL.Decoder.Internal.Error++-- | The monad that returns either a 'DecodeError' or a result of type @a@.+--+-- To a first approximation, this monad is equivalent to the @Either DecodeError@+-- monad, with the following changes:+--+--   * Uses continuation-passing style for performance+--   * Has two error channels, one for fatal errors and one for non-fatal errors (see 'makeFatal')+--   * Collects as many errors as possible, within an Applicative context+data DecodeM a+  = DecodeM+      ( forall r.+        (DecodeError -> r) -> -- fatal error, not handled by <|>+        (DecodeError -> r) -> -- non-fatal error, handled by <|>+        (a -> r) ->+        r+      )++instance Functor DecodeM where+  fmap f (DecodeM k) = DecodeM $ \onFatal onFail onSuccess -> k onFatal onFail (onSuccess . f)+instance Applicative DecodeM where+  pure x = DecodeM $ \_ _ onSuccess -> onSuccess x+  DecodeM kf <*> DecodeM ka = DecodeM $ \onFatal onFail onSuccess ->+    -- Collect all errors+    kf+      (\e1 -> ka (\e2 -> onFatal $ e1 <> e2) (\e2 -> onFatal $ e1 <> e2) (\_ -> onFatal e1))+      (\e1 -> ka (\e2 -> onFatal $ e1 <> e2) (\e2 -> onFail $ e1 <> e2) (\_ -> onFail e1))+      (\f -> ka onFatal onFail (onSuccess . f))+instance Monad DecodeM where+  (>>) = (*>)+  DecodeM ka >>= k = DecodeM $ \onFatal onFail onSuccess ->+    ka onFatal onFail $ \a -> let DecodeM kb = k a in kb onFatal onFail onSuccess+instance Alternative DecodeM where+  empty = DecodeM $ \_ onFail _ -> onFail mempty+  DecodeM k1 <|> DecodeM k2 = DecodeM $ \onFatal onFail onSuccess ->+    k1+      onFatal+      (\e1 -> k2 onFatal (\e2 -> onFail $ e1 <> e2) onSuccess)+      onSuccess++-- | Run a 'DecodeM' action and return the result or the error.+runDecodeM :: DecodeM a -> Either DecodeError a+runDecodeM (DecodeM f) = f Left Left Right++-- | Throw an error.+--+-- This error is non-fatal and can be handled by '<|>'. See 'makeFatal'+-- for more information.+decodeThrow :: BaseDecodeError -> DecodeM a+decodeThrow e = DecodeM $ \_ onFail _ -> onFail $ DecodeError [([], e)]++-- | Throw a 'DecodeError_Custom' error.+failM :: Text -> DecodeM a+failM = decodeThrow . DecodeError_Custom++-- | Make all errors in the given action fatal errors.+--+-- A la standard parsing libraries like megaparsec, errors should be+-- considered fatal when decoding has started consuming something.+makeFatal :: DecodeM a -> DecodeM a+makeFatal (DecodeM f) = DecodeM $ \onFatal _ onSuccess -> f onFatal onFatal onSuccess++-- | Make all errors non-fatal errors.+makeNonFatal :: DecodeM a -> DecodeM a+makeNonFatal (DecodeM f) = DecodeM $ \_ onFail onSuccess -> f onFail onFail onSuccess++-- | Add context to all errors that occur in the given action.+addContext :: ContextItem -> DecodeM a -> DecodeM a+addContext ctxItem (DecodeM f) = DecodeM $ \onFatal onFail onSuccess -> f (onFatal . addCtx) (onFail . addCtx) onSuccess+ where+  addCtx (DecodeError es) = DecodeError [(ctxItem : ctx, msg) | (ctx, msg) <- es]
+ src/KDL/Decoder/Internal/Decoder.hs view
@@ -0,0 +1,205 @@+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE TypeFamilies #-}++module KDL.Decoder.Internal.Decoder (+  -- * Decoder+  Decoder,++  -- * DecodeArrow+  DecodeArrow (..),+  liftDecodeM,+  withDecoder,+  fail,+  debug,++  -- * DecodeStateM+  DecodeStateM,+  runDecodeStateM,++  -- * DecodeState+  HasDecodeHistory (..),+  DecodeHistory (..),+  DecodeState (..),+) where++import Control.Applicative (+  Alternative (..),+ )+import Control.Arrow (Arrow (..), ArrowChoice (..), (>>>))+import Control.Category (Category)+import Control.Category qualified+import Control.Monad ((>=>))+import Control.Monad.Trans.Class qualified as Trans+import Control.Monad.Trans.State.Strict (StateT)+import Control.Monad.Trans.State.Strict qualified as StateT+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Set qualified as Set+import Data.Text (Text)+import Debug.Trace (traceM)+import KDL.Decoder.Internal.DecodeM+import KDL.Decoder.Schema (+  Schema (..),+  SchemaOf,+  schemaAlt,+  schemaJoin,+ )+import KDL.Types (Identifier, Node, NodeList, Value)+import Prelude hiding (any, fail, null)++class HasDecodeHistory o where+  data DecodeHistory o+  emptyDecodeHistory :: DecodeHistory o++instance HasDecodeHistory NodeList where+  data DecodeHistory NodeList = DecodeHistory_NodeList+    { nodesSeen :: Map Text Int+    }+  emptyDecodeHistory = DecodeHistory_NodeList{nodesSeen = Map.empty}++instance HasDecodeHistory Node where+  data DecodeHistory Node = DecodeHistory_Node+    { argsSeen :: Int+    , propsSeen :: Set Identifier+    , childrenHistory :: DecodeHistory NodeList+    }+  emptyDecodeHistory =+    DecodeHistory_Node+      { argsSeen = 0+      , propsSeen = Set.empty+      , childrenHistory = emptyDecodeHistory+      }++instance HasDecodeHistory Value where+  data DecodeHistory Value = DecodeHistory_Value+  emptyDecodeHistory = DecodeHistory_Value++-- | The state to track when decoding an object of type @o@.+--+-- At each decode step, some value within @o@ is consumed and+-- the action is recorded in the history.+data DecodeState o = DecodeState+  { object :: !o+  , history :: DecodeHistory o+  -- ^ Not strict, since this only matters for reporting errors+  }++type DecodeStateM o a = StateT (DecodeState o) DecodeM a++runDecodeStateM :: o -> DecodeHistory o -> DecodeStateM o a -> DecodeM a+runDecodeStateM o hist m =+  StateT.evalStateT m $+    DecodeState+      { object = o+      , history = hist+      }++-- | @DecodeArrow o a b@ represents an arrow with input @a@ and output @b@, within+-- the context of decoding a KDL object of type @o@. It also knows the expected+-- schema of @o@. Most of the time, @a@ is @()@; it would only be different if+-- you're using Arrows notation.+--+-- We're using arrows here so that we can:+--+--   1. Get the schema without running the decoder, and also+--   2. Use previously decoded values to inform decoding other values+--+-- Using monads alone would lose (1), but applicatives can't do (2).+data DecodeArrow o a b = DecodeArrow+  { schema :: SchemaOf o+  , run :: a -> DecodeStateM o b+  }++instance Category (DecodeArrow o) where+  id = liftDecodeM pure+  DecodeArrow sch2 bc . DecodeArrow sch1 ab = DecodeArrow (sch1 `schemaJoin` sch2) $ ab >=> bc+instance Arrow (DecodeArrow o) where+  arr f = liftDecodeM (pure . f)+  DecodeArrow sch1 bc *** DecodeArrow sch2 bc' =+    DecodeArrow (sch1 `schemaJoin` sch2) $ \(b, b') -> (,) <$> bc b <*> bc' b'+instance ArrowChoice (DecodeArrow o) where+  DecodeArrow sch1 bc +++ DecodeArrow sch2 bc' =+    DecodeArrow (sch1 `schemaAlt` sch2) $ either (fmap Left . bc) (fmap Right . bc')++instance Functor (DecodeArrow o a) where+  fmap f (DecodeArrow schema run) = DecodeArrow schema $ (fmap f . run)+instance Applicative (DecodeArrow o a) where+  pure = arr . const+  DecodeArrow sch1 kf <*> DecodeArrow sch2 kx =+    DecodeArrow (sch1 `schemaJoin` sch2) $ \a -> kf a <*> kx a+instance Alternative (DecodeArrow o a) where+  -- Can't use StateT's Alternative instance: https://hub.darcs.net/ross/transformers/issue/78+  empty = DecodeArrow (SchemaOr []) $ \_ -> Trans.lift empty+  DecodeArrow sch1 run1 <|> DecodeArrow sch2 run2 =+    DecodeArrow (sch1 `schemaAlt` sch2) $ \a -> StateT.StateT $ \s -> do+      StateT.runStateT (run1 a) s <|> StateT.runStateT (run2 a) s+  some (DecodeArrow sch run) =+    DecodeArrow (SchemaSome sch) $ \a ->+      StateT.StateT $+        let go s0 = do+              (x, s1) <- StateT.runStateT (run a) s0+              (xs, s2) <- go s1 <|> pure ([], s1)+              pure (x : xs, s2)+         in go+  many (DecodeArrow sch run) = some (DecodeArrow sch run) <|> pure []++type Decoder o a = DecodeArrow o () a++-- | Eliminates all schema information; avoid whenever possible.+instance Monad (DecodeArrow o a) where+  DecodeArrow _ run1 >>= k =+    DecodeArrow SchemaUnknown $ \a -> do+      x <- run1 a+      let DecodeArrow _ run2 = k x+      run2 a++liftDecodeM :: (a -> DecodeM b) -> DecodeArrow o a b+liftDecodeM f = DecodeArrow (SchemaAnd []) (Trans.lift . f)++-- | Run actions within a t'DecodeArrow'. Useful for adding post-processing logic.+--+-- === __Example__+--+-- @+-- decoder = KDL.withDecoder KDL.number $ \\x -> do+--   when (x > 100)+--     KDL.failM $ "argument is too large: " <> (Text.pack . show) x+--   pure $ MyVal x+-- @+withDecoder :: forall o a b c. DecodeArrow o a b -> (b -> DecodeM c) -> DecodeArrow o a c+withDecoder decoder f = decoder >>> liftDecodeM f++-- | Unconditionally fail the decoder.+--+-- === __Example__+--+-- @+-- decoder = proc () -> do+--   x <- KDL.arg -< ()+--   if x > 100+--     then KDL.fail -\< "argument is too large: " <> (Text.pack . show) x+--     else returnA -< ()+--   returnA -< x+-- @+fail :: forall b o. DecodeArrow o Text b+fail = DecodeArrow (SchemaOr []) (Trans.lift . failM)++-- | Debug the current state of the object being decoded.+--+-- === __Example__+--+-- @+-- 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)+-- @+debug :: forall o a. (Show o) => DecodeArrow o a ()+debug =+  DecodeArrow (SchemaAnd []) $ \_ -> do+    o <- StateT.gets (.object)+    traceM $ "[kdl-hs] DEBUG: " ++ show o
+ src/KDL/Decoder/Internal/Error.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++module KDL.Decoder.Internal.Error (+  DecodeError (..),+  BaseDecodeError (..),+  Context,+  ContextItem (..),+  renderDecodeError,+) where++import Data.Map qualified as Map+import Data.Text (Text)+import Data.Text qualified as Text+import KDL.Render (+  renderIdentifier,+  renderValue,+ )+import KDL.Types (+  Identifier,+  Value,+ )++data DecodeError = DecodeError [(Context, BaseDecodeError)]+  deriving (Show, Eq)+instance Semigroup DecodeError where+  DecodeError e1 <> DecodeError e2 = DecodeError (e1 <> e2)+instance Monoid DecodeError where+  mempty = DecodeError []++type Context = [ContextItem]++data ContextItem+  = ContextNode+      { name :: Identifier+      , index :: Int+      }+  | ContextArg+      { index :: Int+      }+  | ContextProp+      { name :: Identifier+      }+  deriving (Show, Eq, Ord)++data BaseDecodeError+  = DecodeError_Custom Text+  | DecodeError_ParseError Text+  | DecodeError_ExpectedNode {name :: Text, index :: Int}+  | DecodeError_ExpectedArg {index :: Int}+  | DecodeError_ExpectedProp {name :: Text}+  | DecodeError_MismatchedAnn {givenAnn :: Identifier, validAnns :: [Text]}+  | DecodeError_ValueDecodeFail {expectedType :: Text, value :: Value}+  | DecodeError_UnexpectedNode {identifier :: Identifier, index :: Int}+  | DecodeError_UnexpectedArg {index :: Int, value :: Value}+  | DecodeError_UnexpectedProp {identifier :: Identifier, value :: Value}+  deriving (Show, Eq)++renderDecodeError :: DecodeError -> Text+renderDecodeError = Text.intercalate "\n" . map renderCtxErrors . groupCtxErrors+ where+  -- Group errors with the same contexts together+  groupCtxErrors (DecodeError es) = Map.toAscList $ Map.fromListWith (<>) [(ctx, [e]) | (ctx, e) <- es]++  renderCtxErrors (ctx, errs) =+    Text.intercalate "\n" $ ("At: " <> renderCtxItems ctx) : renderErrors errs++  renderCtxItems items+    | null items = "<root>"+    | otherwise = Text.intercalate " > " . map renderCtxItem $ items+  renderCtxItem = \case+    ContextNode{..} -> renderIdentifier name <> " #" <> showT index+    ContextArg{..} -> "arg #" <> showT index+    ContextProp{..} -> "prop " <> renderIdentifier name++  renderErrors = map ("  " <>) . concatMap (Text.lines . renderError)+  renderError = \case+    DecodeError_Custom msg -> msg+    DecodeError_ParseError msg -> msg+    DecodeError_ExpectedNode{..}+      | index == 0 -> "Expected node: " <> name+      | otherwise -> "Expected another node: " <> name+    DecodeError_ExpectedArg{..} -> "Expected arg #" <> showT index+    DecodeError_ExpectedProp{..} -> "Expected prop: " <> name+    DecodeError_MismatchedAnn{..} -> "Expected annotation to be one of " <> showT validAnns <> ", got: " <> renderIdentifier givenAnn+    DecodeError_ValueDecodeFail{..} -> "Expected " <> expectedType <> ", got: " <> renderValue value+    DecodeError_UnexpectedNode{..} -> "Unexpected node: " <> renderIdentifier identifier <> " #" <> showT index+    DecodeError_UnexpectedArg{..} -> "Unexpected arg #" <> showT index <> ": " <> renderValue value+    DecodeError_UnexpectedProp{..} -> "Unexpected prop: " <> renderIdentifier identifier <> "=" <> renderValue value++  -- Replace with Text.show after requiring at least text-2.1.2+  showT :: (Show a) => a -> Text+  showT = Text.pack . show
+ src/KDL/Decoder/Monad.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE LambdaCase #-}++{-|+See "KDL" for more information.+-}+module KDL.Decoder.Monad (+  -- * Monad-specific definitions+  fail,++  -- * Re-exports from "KDL.Decoder.Arrow"+  module KDL.Decoder.Arrow,+) where++import Control.Arrow (arr, (>>>))+import Data.Text (Text)+import KDL.Decoder.Arrow hiding (+  fail,+ )+import KDL.Decoder.Arrow qualified as Arrow+import Prelude hiding (any, fail, null)++-- | Same as 'KDL.Decoder.Arrow.fail', except a more ergonomic signature+-- for use in do-notation.+fail :: forall a o. Text -> Decoder o a+fail msg = arr (\() -> msg) >>> Arrow.fail
+ src/KDL/Decoder/Schema.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TypeFamilies #-}++module KDL.Decoder.Schema (+  SchemaOf,+  Schema (..),+  SchemaItem (..),+  TypedNodeSchema (..),+  TypedValueSchema (..),+  schemaJoin,+  schemaAlt,+) where++import Data.Text (Text)+import Data.Typeable (TypeRep)+import KDL.Types (+  Node,+  NodeList,+  Value,+ )++type SchemaOf o = Schema (SchemaItem o)++data Schema a+  = SchemaOne a+  | SchemaSome (Schema a)+  | SchemaAnd [Schema a]+  | SchemaOr [Schema a]+  | SchemaUnknown+  deriving (Show, Eq)++data family SchemaItem a++data instance SchemaItem NodeList+  = NodeNamed Text TypedNodeSchema+  | RemainingNodes TypedNodeSchema+  deriving (Show, Eq)++data TypedNodeSchema = TypedNodeSchema+  { typeHint :: TypeRep+  , validTypeAnns :: [Text]+  , nodeSchema :: SchemaOf Node+  }+  deriving (Show, Eq)++data instance SchemaItem Node+  = NodeArg TypedValueSchema+  | NodeProp Text TypedValueSchema+  | NodeRemainingProps TypedValueSchema+  | NodeChildren (SchemaOf NodeList)+  deriving (Show, Eq)++data TypedValueSchema = TypedValueSchema+  { typeHint :: TypeRep+  , validTypeAnns :: [Text]+  , dataSchema :: SchemaOf Value+  }+  deriving (Show, Eq)++data instance SchemaItem Value+  = TextSchema+  | NumberSchema+  | BoolSchema+  | NullSchema+  deriving (Show, Eq, Ord, Enum, Bounded)++schemaJoin :: Schema a -> Schema a -> Schema a+schemaJoin = curry $ \case+  (SchemaAnd l, SchemaAnd r) -> SchemaAnd (l <> r)+  (l, SchemaAnd []) -> l+  (l, SchemaAnd r) -> SchemaAnd (l : r)+  (SchemaAnd [], r) -> r+  (SchemaAnd l, r) -> SchemaAnd (l <> [r])+  (l, r) -> SchemaAnd [l, r]++schemaAlt :: Schema a -> Schema a -> Schema a+schemaAlt = curry $ \case+  (SchemaOr l, SchemaOr r) -> SchemaOr (l <> r)+  (l, SchemaOr []) -> l+  (l, SchemaOr r) -> SchemaOr (l : r)+  (SchemaOr [], r) -> r+  (SchemaOr l, r) -> SchemaOr (l <> [r])+  (l, r) -> SchemaOr [l, r]
+ src/KDL/Parser.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module KDL.Parser (+  parse,+  parseFile,+) where++import Data.Map qualified as Map+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Text.IO qualified as Text+import KDL.Parser.Hustle qualified as Hustle+import KDL.Types (+  Ann (..),+  Document,+  Entry (..),+  Identifier (..),+  Node (..),+  NodeList (..),+  Value (..),+  ValueData (..),+  ValueFormat (..),+ )++-- TODO: Implement our own parser that implements the v2.0.0 spec + preserves formatting and comments+parse :: Text -> Either Text Document+parse input =+  case Hustle.parse Hustle.document "" input of+    Left e -> Left . Text.strip . Text.pack . Hustle.errorBundlePretty $ e+    Right (Hustle.Document nodes) -> Right $ fromNodes nodes+ where+  fromNodes nodes =+    NodeList+      { nodes = map fromNode nodes+      , format = Nothing+      }++  fromAnn identifier =+    Ann+      { identifier = fromIdentifier identifier+      , format = Nothing+      }++  fromNode Hustle.Node{..} =+    Node+      { ann = fromAnn <$> nodeAnn+      , name = fromIdentifier nodeName+      , entries = map fromArgEntry nodeArgs <> map fromPropEntry (Map.toList nodeProps)+      , children = Just $ fromNodes nodeChildren+      , format = Nothing+      }++  fromArgEntry v =+    Entry+      { name = Nothing+      , value = fromValue v+      , format = Nothing+      }++  fromPropEntry (name, v) =+    Entry+      { name = Just $ fromIdentifier name+      , value = fromValue v+      , format = Nothing+      }++  fromValue Hustle.Value{..} =+    Value+      { ann = fromAnn <$> valueAnn+      , data_ =+          case valueExp of+            Hustle.StringValue s -> Text s+            Hustle.IntegerValue x -> Number (fromInteger x)+            Hustle.SciValue x -> Number x+            Hustle.BooleanValue x -> Bool x+            Hustle.NullValue -> Null+      , format =+          case valueExp of+            Hustle.IntegerValue x -> Just ValueFormat{repr = Text.pack $ show x}+            _ -> Nothing+      }++  fromIdentifier (Hustle.Identifier s) =+    Identifier+      { value = s+      , format = Nothing+      }++parseFile :: FilePath -> IO (Either Text Document)+parseFile = fmap parse . Text.readFile
+ src/KDL/Parser/Hustle.hs view
@@ -0,0 +1,28 @@+{- FOURMOLU_DISABLE -}++{- | Vendered from https://github.com/fuzzypixelz/hustle -}+module KDL.Parser.Hustle+  ( Parser+  , Document(..)+  , Node(..)+  , Value(..)+  , ValueType(..)+  , Identifier(..)+  , pretty+  , document+  , parse+  , errorBundlePretty+  ) where++import           KDL.Parser.Hustle.Formatter ( Pretty(pretty) )+import           KDL.Parser.Hustle.Parser  ( document )+import           KDL.Parser.Hustle.Types   ( Document(..)+                                                , Identifier(..)+                                                , Node(..)+                                                , Parser+                                                , Value(..)+                                                , ValueType(..)+                                                )+import           Text.Megaparsec                ( errorBundlePretty+                                                , parse+                                                )
+ src/KDL/Parser/Hustle/Formatter.hs view
@@ -0,0 +1,83 @@+{- FOURMOLU_DISABLE -}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -Wno-orphans #-}++{- | Vendered from https://github.com/fuzzypixelz/hustle -}+module KDL.Parser.Hustle.Formatter+  ( Pretty(pretty)+  ) where++import           Data.Map                       ( Map )+import qualified Data.Map.Strict               as Map+import           Data.Maybe                     ( catMaybes )+import           Data.Scientific                ( Scientific )+import qualified Data.Text                     as T+import           KDL.Parser.Hustle.Internal ( escChar+                                                , match+                                                )+import           KDL.Parser.Hustle.Parser  ( identifier )+import           KDL.Parser.Hustle.Types+import           Prettyprinter                  ( Pretty(pretty)+                                                , braces+                                                , dquotes+                                                , enclose+                                                , hsep+                                                , nest+                                                , parens+                                                , viaShow+                                                , vsep+                                                )++instance Pretty Scientific where+  pretty = viaShow++instance Pretty Identifier where+  pretty (Identifier i) =+    if match identifier i then pretty i else dquotes (pretty i)++instance Pretty Value where+  pretty v = vann <> vexp+   where+    vann = case valueAnn v of+      Nothing -> ""+      Just a  -> parens (pretty a)+    vexp = case valueExp v of+      StringValue  s -> dquotes . pretty $ T.concatMap escChar s+      IntegerValue i -> pretty i+      SciValue     s -> pretty s+      BooleanValue b -> if b then "true" else "false"+      NullValue      -> "null"++instance Pretty (Map Identifier Value) where+  pretty ps = hsep . Map.elems $ Map.mapWithKey prop ps+    where prop i v = pretty i <> "=" <> pretty v++instance Pretty Node where+  pretty n = hsep . catMaybes $ [nname, nargs, nprops, nchildren]+   where+    nann = case nodeAnn n of+      Nothing -> ""+      Just a  -> parens (pretty a)+    nname = Just $ nann <> pretty (nodeName n)+    nargs = case nodeArgs n of+      []  -> Nothing+      nas -> Just . hsep . map pretty $ nas+    nprops | nodeProps n == Map.empty = Nothing+           | otherwise                = Just (pretty (nodeProps n))+    nchildren = case nodeChildren n of+      [] -> Nothing+      ncs ->+        Just+          . nest 4+          . braces+          . enclose "\n" (nest (-4) "\n")+          . vsep+          . map pretty+          $ ncs++instance Pretty Document where+  pretty d = vsep (map pretty (docNodes d)) <> "\n"++instance Show Document where+  show d = show (pretty d)
+ src/KDL/Parser/Hustle/Internal.hs view
@@ -0,0 +1,96 @@+{- FOURMOLU_DISABLE -}+{-# LANGUAGE OverloadedStrings #-}++{- | Vendered from https://github.com/fuzzypixelz/hustle -}+module KDL.Parser.Hustle.Internal where++import           Control.Monad                  ( void )+import           Data.Char                      ( digitToInt+                                                , isDigit+                                                )+import           Data.Either                    ( isRight )+import           Data.Scientific                ( Scientific )+import qualified Data.Scientific               as Sci+import           Data.Text                      ( Text )+import qualified Data.Text                     as T+import           KDL.Parser.Hustle.Types   ( Parser )+import           Text.Megaparsec                ( (<|>)+                                                , MonadParsec+                                                  ( eof+                                                  , takeWhileP+                                                  , try+                                                  )+                                                , option+                                                , runParser+                                                , satisfy+                                                )+import           Text.Megaparsec.Char           ( char+                                                , char'+                                                , digitChar+                                                , newline+                                                )+import qualified Text.Megaparsec.Char.Lexer    as L++signed :: Num a => Parser a -> Parser a+signed p = option id sign <*> p+  where sign = (id <$ char '+') <|> (negate <$ char '-')++lineComment :: Parser ()+lineComment = L.skipLineComment "//" >> (void newline <|> eof)++blockComment :: Parser ()+blockComment = L.skipBlockCommentNested "/*" "*/"++isBinDigit :: Char -> Bool+isBinDigit c = c `elem` ['0', '1']++data SP = SP Integer Int++number :: Integer -> (Char -> Bool) -> Parser Integer+number b isNumDigit = mkNum . T.filter (/= '_') <$> digits+ where+  mkNum = T.foldl' step 0+  step a c = a * b + fromIntegral (digitToInt c)+  digits = T.cons <$> satisfy isNumDigit <*> takeWhileP+    (Just "digit")+    (\c -> isNumDigit c || c == '_')++decimal_ :: Parser Integer+decimal_ = number 10 isDigit++scientific_ :: Parser Scientific+scientific_ = do+  c'      <- decimal_+  SP c e' <- dotDecimal_ c'+  e       <- option e' (try $ exponent_ e')+  return (Sci.scientific c e)++dotDecimal_ :: Integer -> Parser SP+dotDecimal_ c' = do+  void (char '.')+  let digits = T.cons <$> digitChar <*> takeWhileP+        (Just "digit")+        (\c -> isDigit c || c == '_')+  let mkNum = T.foldl' step (SP c' 0)+      step (SP a e') c = SP (a * 10 + fromIntegral (digitToInt c)) (e' - 1)+  mkNum . T.filter (/= '_') <$> digits++exponent_ :: Int -> Parser Int+exponent_ e' = do+  void (char' 'e')+  (+ e') <$> L.signed (return ()) (fromIntegral <$> decimal_)++match :: Parser a -> Text -> Bool+match p t = isRight $ runParser (p >> eof) "" t++escChar :: Char -> Text+escChar c = case c of+  '\x08' -> "\\b"+  '\x09' -> "\\t"+  '\x0A' -> "\\n"+  '\x0C' -> "\\f"+  '\x0D' -> "\\r"+  '\x22' -> "\\\""+  '\x2F' -> "\\/"+  '\x5C' -> "\\\\"+  _      -> T.singleton c
+ src/KDL/Parser/Hustle/Parser.hs view
@@ -0,0 +1,310 @@+{- FOURMOLU_DISABLE -}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++{- | Vendered from https://github.com/fuzzypixelz/hustle -}+module KDL.Parser.Hustle.Parser where++import           KDL.Parser.Hustle.Internal+import           KDL.Parser.Hustle.Types++import           Control.Monad                  ( void )+import           Data.Char                      ( chr+                                                , isHexDigit+                                                , isOctDigit+                                                , isDigit+                                                , isSpace+                                                )+import qualified Data.Map.Strict               as Map+import           Data.Maybe                     ( catMaybes+                                                , fromMaybe+                                                , mapMaybe+                                                , maybeToList+                                                )+import           Data.Scientific                ( Scientific )+import           Data.Text                      ( Text )+import qualified Data.Text                     as T+import           Text.Megaparsec                ( (<?>)+                                                , (<|>)+                                                , MonadParsec(eof, label, try)+                                                , anySingle+                                                , between+                                                , choice+                                                , many+                                                , manyTill+                                                , noneOf+                                                , optional+                                                , satisfy+                                                , some+                                                )+import           Text.Megaparsec.Char           ( char+                                                , char'+                                                , crlf+                                                , newline+                                                , string+                                                )++-- WHITESPACE++escline :: Parser ()+escline =+  char '\\' >> many ws >> (try linebreak <|> lineComment) <?> "Escape Line"++linespace :: Parser ()+linespace = try linebreak <|> try ws <|> try lineComment <?> "Line Space"++linebreak :: Parser ()+linebreak = label "Newline" $ do+  choice $ void crlf : map+    void+    [ char '\r' <?> "carriage return"+    , char '\n' <?> "newline"+    , char '\x85' <?> "next line"+    , char '\f' <?> "form feed"+    , char '\x2028' <?> "line seperator"+    , char '\x2029' <?> "paragraph seperator"+    ]++ws :: Parser ()+ws = bom <|> hspacechar <|> blockComment <?> "Whitespace"++bom :: Parser ()+bom = void (char '\xFEFF') <?> "BOM"++hspacechar :: Parser ()+hspacechar = label "Unicode Space" $ do+  choice $ map+    void+    [ char '\x0009' <?> "character tabulation"+    , char '\x0020' <?> "space"+    , char '\x00A0' <?> "bo-break space"+    , char '\x1680' <?> "ogham space mark"+    , char '\x2000' <?> "en quad"+    , char '\x2001' <?> "em quad"+    , char '\x2002' <?> "en space"+    , char '\x2003' <?> "em space"+    , char '\x2004' <?> "three-per-em space"+    , char '\x2005' <?> "four-per-em space"+    , char '\x2006' <?> "six-per-em space"+    , char '\x2007' <?> "figure space"+    , char '\x2008' <?> "punctuation space"+    , char '\x2009' <?> "thin space"+    , char '\x200A' <?> "hair space"+    , char '\x202F' <?> "narrow no-break space"+    , char '\x205F' <?> "medium mathmatical space"+    , char '\x3000' <?> "ideographic space"+    ]++-- STRINGS++anystring :: Parser Text+anystring = try unquotedstring <|> try rawstring <|> quotedstring++unquotedstring :: Parser Text+unquotedstring = label "Unquoted String" $ do+  c0 <- satisfy $ \c -> isValidChar c && not (isDigit c) && c /= '"'+  rest <- many $ satisfy isValidChar+  -- TODO: Forbid true, false, null, inf, -inf, nan, or "looks like a number"+  pure $ T.pack (c0 : rest)+  where+    isValidChar c = not (isSpace c) && c `notElem` ("[]{}()\\/#\";=" :: [Char])++quotedstring :: Parser Text+quotedstring = label "Quoted String" $ do+  T.concat <$> (char '"' *> manyTill character (char '"'))++rawstring :: Parser Text+rawstring = label "Raw String" $ do+  void (char 'r')+  hs <- T.pack <$> many (char '#')+  void (char '"')+  s <- manyTill anySingle (string (T.cons '"' hs))+  return (T.pack s)++character :: Parser Text+character = void (char '\\') *> escape <|> nonescape++{-+  As per the Haskell 2010 Language Report,+  (https://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6)+  The '\/' Solidus escape isn't defined, so if we try to parse it directly the+  compiler throws a lexical error. Hence this terribleness.+-}+escape :: Parser Text+escape =+  do+    e <- choice+      [ '\x08' <$ char 'b'+      , '\x09' <$ char 't'+      , '\x0A' <$ char 'n'+      , '\x0C' <$ char 'f'+      , '\x0D' <$ char 'r'+      , '\x22' <$ char '\"'+      , '\x2F' <$ char '/'+      , '\x5C' <$ char '\\'+      ]+    return (T.singleton e)+  <|> uescape++uescape :: Parser Text+uescape = do+  void (string "u{")+  u <- fromInteger <$> number 16 isHexDigit+  if u >= 0x10ffff+    then fail "Exceeded Unicode code point limit."+    else do+      void (char '}')+      let c = chr u+      return (T.singleton c)++nonescape :: Parser Text+nonescape = do+  c <- noneOf ("\\\"" :: [Char])+  return (T.singleton c)++-- NUMBERS++binary :: Parser Integer+binary = signed $ char '0' >> char' 'b' >> number 2 isBinDigit++octal :: Parser Integer+octal = signed $ char '0' >> char 'o' >> number 8 isOctDigit++hexadecimal :: Parser Integer+hexadecimal = signed $ char '0' >> char 'x' >> number 16 isHexDigit++integer :: Parser Integer+integer = signed decimal_++scientific :: Parser Scientific+scientific = signed scientific_++-- CONTENT++name :: Parser Identifier+name = Identifier <$> (try anystring <|> identifier)++identifier :: Parser Text+identifier = label "Identifier" $ do+  i  <- satisfy iichar+  is <- many (satisfy ichar)+  let result = T.pack (i : is)+  case result of+    "true"  -> fail "keyword true in identifier"+    "false" -> fail "keyword false in identifier"+    "null"  -> fail "keyword null in identifier"+    _       -> return result+ where+  ichar c =+    (c > '\x20')+      && (c <= '\x10FFFF')+      && (c `notElem` ("\\/(){}<>;[]=,\"" :: [Char]))+      && not (match linespace (T.singleton c))+  iichar c = ichar c && c `notElem` ['0' .. '9']++nullvalue :: Parser Text+nullvalue = string "#null"++bool :: Parser Bool+bool = True <$ string "#true" <|> False <$ string "#false"++property :: Parser (Identifier, Value)+property = label "Property" $ do+  propKey <- name+  void (char '=')+  propValue <- value+  return (propKey, propValue)++value :: Parser Value+value = label "Value" $ do+  valueAnn <- optional typeAnnotation+  valueExp <- choice+    [ IntegerValue <$> try binary <?> "Binary"+    , IntegerValue <$> try octal <?> "Octal"+    , IntegerValue <$> try hexadecimal <?> "Hexadecimal"+    , SciValue <$> try scientific <?> "Decimal"+    , IntegerValue <$> try integer <?> "Integer"+    , BooleanValue <$> try bool <?> "Boolean"+    , NullValue <$ try nullvalue <?> "Null"+    , StringValue <$> anystring <?> "String"+    ]+  return Value { .. }++typeAnnotation :: Parser Identifier+typeAnnotation = label "Type Annotation" $ do+  void (char '(')+  i <- name+  void (char ')')+  return i++-- NODES++nodes :: Parser [Node]+nodes = between (many linespace) (many linespace) (fromMaybe [] <$> body)+ where+  body = optional $ do+    n  <- maybeToList <$> node+    ns <- fromMaybe [] <$> optional nodes+    return (n ++ ns)++node :: Parser (Maybe Node)+node = label "Node" $ do+  discard      <- optional comment+  nodeAnn      <- optional typeAnnotation+  nodeName     <- name+  nodeContent  <- catMaybes <$> content+  nodeChildren <- fromMaybe [] <$> optional children+  _            <- many nodespace+  _            <- terminator+  let nodeArgs  = mapMaybe isArg nodeContent+      nodeProps = Map.fromList $ mapMaybe isProp nodeContent+  case discard of+    Just _  -> return Nothing+    Nothing -> return $ Just Node { .. }+ where+  isArg c = case c of+    NodeValue v -> Just v+    _           -> Nothing+  isProp c = case c of+    NodeProperty p -> Just p+    _              -> Nothing++content :: Parser [Maybe Content]+content = many . try $ do+  void (some nodespace)+  discard <- optional $ comment <* many nodespace+  c       <- choice [NodeProperty <$> try property, NodeValue <$> try value]+  case discard of+    Just _  -> return Nothing+    Nothing -> return $ Just c++children :: Parser [Node]+children = label "Node Child" . try $ do+  void (many nodespace)+  discard <- optional comment+  void (char '{')+  ns <- nodes+  void (char '}')+  void (many ws)+  case discard of+    Just _  -> return []+    Nothing -> return ns++comment :: Parser ()+comment = label "/-Comment" . try $ do+  void (string "/-")+  void $ many nodespace++nodespace :: Parser ()+nodespace = label "Node Space" $ do+  try (many ws *> escline <* many ws) <|> try (void $ some ws)++terminator :: Parser ()+terminator = label "Node Terminator" $ do+  choice [try (void (char ';')), try (void newline), try lineComment, eof]++document :: Parser Document+document = do+  docNodes <- nodes+  void eof+  return Document { .. }
+ src/KDL/Parser/Hustle/Types.hs view
@@ -0,0 +1,73 @@+{- FOURMOLU_DISABLE -}+{- | Vendered from https://github.com/fuzzypixelz/hustle -}+module KDL.Parser.Hustle.Types+  ( Parser+  , Document(..)+  , Content(..)+  , Node(..)+  , Value(..)+  , ValueType(..)+  , Identifier(..)+  ) where++import           Data.Map                       ( Map )+import           Data.Scientific                ( Scientific )+import           Data.Text                      ( Text )+import           Data.Void                      ( Void )+import           Text.Megaparsec                ( Parsec )++{-+  String has exactly one use, +  and that’s showing Hello World in tutorials.+                  -- Albert Einstein+-}+type Parser = Parsec Void Text++newtype Document = Document+  { docNodes :: [Node]+  }+  deriving (Eq)++{- +  This data type serves as an abstraction over Values+  and Properties of a Node, in order simplify the the node+  Parser, i.e group the two types together to+  consume any number of them in any order. +-}+data Content+  = NodeValue    { getValue :: Value }+  | NodeProperty { getProp :: (Identifier, Value) }+  deriving (Eq)++data Node = Node+  { nodeAnn      :: Maybe Identifier+  , nodeName     :: Identifier+  , nodeArgs     :: [Value]+  , nodeProps    :: Map Identifier Value+  , nodeChildren :: [Node]+  }+  deriving (Show, Eq)++newtype Identifier = Identifier Text+  deriving (Show, Eq)++data Value = Value+  { valueAnn :: Maybe Identifier+  , valueExp :: ValueType+  }+  deriving (Show, Eq)++data ValueType+  = StringValue Text+  | IntegerValue Integer+  | SciValue Scientific+  | BooleanValue Bool+  | NullValue+  deriving (Show, Eq)++{- +  This allows for querying properties in alphabetical order+  upon printing out, the rest is handled automatically by Map.+-}+instance Ord Identifier where+  Identifier t1 `compare` Identifier t2 = t1 `compare` t2
+ src/KDL/Render.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module KDL.Render (+  render,++  -- * Rendering components+  renderAnn,+  renderValue,+  renderValueData,+  renderIdentifier,+) where++import Data.Char (isDigit)+import Data.Text (Text)+import Data.Text qualified as Text+import KDL.Types (+  Ann (..),+  AnnFormat (..),+  Document,+  Identifier (..),+  IdentifierFormat (..),+  Value (..),+  ValueData (..),+  ValueFormat (..),+ )++-- TODO: Implement render after parsing Document with formatting information+render :: Document -> Text+render = error "render is not implemented yet"++renderAnn :: Ann -> Text+renderAnn Ann{..} =+  Text.concat+    [ maybe "" (.leading) format+    , "("+    , maybe "" (.before_id) format+    , renderIdentifier identifier+    , maybe "" (.after_id) format+    , ")"+    , maybe "" (.trailing) format+    ]++renderValue :: Value -> Text+renderValue Value{..} =+  Text.concat+    [ maybe "" renderAnn ann+    , maybe (renderValueData data_) (.repr) format+    ]++renderValueData :: ValueData -> Text+renderValueData = \case+  Text s -> renderString s+  Number x -> (Text.pack . show) x+  Bool b -> if b then "#true" else "#false"+  Null -> "#null"+ where+  renderString s = if isPlainIdent s then s else "\"" <> Text.concatMap escapeChar s <> "\""+  isPlainIdent s =+    and . map not $+      [ Text.any isDisallowedChar s+      , case fmap Text.uncons <$> Text.uncons s of+          Just (c, _) | isDigit c -> True+          Just (c0, Just (c1, _))+            | c0 `elem` ['.', '-', '+']+            , isDigit c1 ->+                True+          _ -> False+      , s `elem` ["inf", "-inf", "nan", "true", "false", "null"]+      ]+  isDisallowedChar c =+    or+      [ c `elem` disallowedIdentChars+      , any (c `Text.elem`) newlines+      , c `elem` unicodeSpaces+      , isDisallowedUnicode c+      , c == '='+      ]+  disallowedIdentChars = ['\\', '/', '(', ')', '{', '}', '[', ']', ';', '"', '#']+  newlines =+    [ "\x000D\x000A"+    , "\x000D"+    , "\x000A"+    , "\x0085"+    , "\x000B"+    , "\x000C"+    , "\x2028"+    , "\x2029"+    ]+  unicodeSpaces =+    [ '\x0009'+    , '\x0020'+    , '\x00A0'+    , '\x1680'+    , '\x2000'+    , '\x2001'+    , '\x2002'+    , '\x2003'+    , '\x2004'+    , '\x2005'+    , '\x2006'+    , '\x2007'+    , '\x2008'+    , '\x2009'+    , '\x200A'+    , '\x202F'+    , '\x205F'+    , '\x3000'+    ]+  isDisallowedUnicode c =+    or+      [ '\x0000' < c && c <= '\x0008'+      , '\x000E' < c && c <= '\x001F'+      , '\x200E' < c && c <= '\x200F'+      , '\x202A' < c && c <= '\x202E'+      , '\x2066' < c && c <= '\x2069'+      , c == '\xFEFF'+      ]+  escapeChar = \case+    '\\' -> "\\\\"+    '"' -> "\\\""+    '\n' -> "\\n"+    '\r' -> "\\r"+    '\t' -> "\\t"+    '\x08' -> "\\b"+    '\x0C' -> "\\f"+    c -> Text.singleton c++renderIdentifier :: Identifier -> Text+renderIdentifier ident = maybe ident.value (.repr) ident.format
+ src/KDL/Types.hs view
@@ -0,0 +1,367 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++{-|+Defines the types that make up a KDL document.++This module enables @-XNoFieldSelectors@, so none of the fields create implicit+selector functions. Instead, use @-XOverloadedRecordDot@, or the functions+provided by this module.+-}+module KDL.Types (+  -- * Document+  Document,+  docNodes,++  -- * NodeList+  NodeList (..),+  NodeListFormat (..),+  fromNodeList,+  nodeListFormat,++  -- ** Helpers+  filterNodes,+  lookupNode,+  getArgAt,+  getArgsAt,+  getDashChildrenAt,+  getDashNodesAt,++  -- * Node+  Node (..),+  NodeFormat (..),+  nodeAnn,+  nodeName,+  nodeEntries,+  nodeChildren,+  nodeFormat,++  -- ** Helpers+  getArgs,+  getArg,+  getProps,+  getProp,++  -- * Entry+  Entry (..),+  EntryFormat (..),+  entryName,+  entryValue,+  entryFormat,++  -- * Value+  Value (..),+  ValueFormat (..),+  valueAnn,+  valueData,+  valueFormat,+  ValueData (..),++  -- * Ann+  Ann (..),+  AnnFormat (..),+  annIdentifier,+  annFormat,++  -- * Identifier+  Identifier (..),+  IdentifierFormat (..),+  fromIdentifier,+  identifierFormat,+  toIdentifier,+) where++import Control.Monad ((<=<))+import Data.Map (Map)+import Data.Map qualified as Map+import Data.Maybe (listToMaybe, mapMaybe)+import Data.Scientific (Scientific)+import Data.Text (Text)++{----- Document -----}++type Document = NodeList++docNodes :: Document -> [Node]+docNodes = fromNodeList++{----- NodeList -----}++data NodeList = NodeList+  { nodes :: [Node]+  , format :: Maybe NodeListFormat+  }+  deriving (Show, Eq)++data NodeListFormat = NodeListFormat+  { leading :: Text+  -- ^ Whitespace and comments preceding the document's first node.+  , trailing :: Text+  -- ^ Whitespace and comments following the document's last node.+  }+  deriving (Show, Eq)++fromNodeList :: NodeList -> [Node]+fromNodeList = (.nodes)++nodeListFormat :: NodeList -> Maybe NodeListFormat+nodeListFormat = (.format)++-- | A helper to get all nodes with the given name+filterNodes :: Text -> NodeList -> [Node]+filterNodes name = filter ((== name) . (.name.value)) . (.nodes)++-- | A helper to get the first node with the given name+lookupNode :: Text -> NodeList -> Maybe Node+lookupNode name = listToMaybe . filterNodes name++-- | A helper to get the first argument of the first node with the given name.+-- A utility for nodes that are acting like a key-value store.+--+-- == __Example__+--+-- @+-- let+--   config =+--     """+--     foo 1+--     """+-- Right doc <- pure $ parse config+-- getArgAt "foo" doc == Just (Number 1)+-- @+getArgAt :: Text -> NodeList -> Maybe Value+getArgAt name = listToMaybe . getArgsAt name++-- | A helper to get 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.+--+-- == __Example__+--+-- @+-- let+--   config =+--     """+--     foo 1 2 "test"+--     """+-- Right doc <- pure $ parse config+-- getArgsAt "foo" doc == [Number 1, Number 2, Text "test"]+-- @+getArgsAt :: Text -> NodeList -> [Value]+getArgsAt name = maybe [] getArgs . lookupNode name++-- | A helper for getting child values following the KDL convention of being named "-".+--+-- == __Example__+--+-- @+-- let+--   config =+--     """+--     foo {+--       - 1+--       - 2+--       - "test"+--     }+--     """+-- Right doc <- pure $ parse config+-- getDashChildrenAt "foo" doc == [Number 1, Number 2, Text "test"]+-- @+getDashChildrenAt :: Text -> NodeList -> [Value]+getDashChildrenAt name = mapMaybe getArg . getDashNodesAt name++-- | A helper for getting child nodes following the KDL convention of being named "-".+--+-- == __Example__+--+-- @+-- let+--   config =+--     """+--     foo {+--       - 1+--       - 2+--       - "test"+--     }+--     """+-- Right doc <- pure $ parse config+-- mapM getArg (getDashNodesAt "foo" doc) == Just [Number 1, Number 2, Text "test"]+-- @+getDashNodesAt :: Text -> NodeList -> [Node]+getDashNodesAt name = maybe [] (filterNodes "-") . (nodeChildren <=< lookupNode name)++{----- Ann -----}++data Ann = Ann+  { identifier :: Identifier+  , format :: Maybe AnnFormat+  }+  deriving (Show, Eq)++data AnnFormat = AnnFormat+  { leading :: Text+  -- ^ Whitespace and comments preceding the annotation itself.+  , before_id :: Text+  -- ^ Whitespace and comments between the opening `(` and the identifier.+  , after_id :: Text+  -- ^ Whitespace and comments between the identifier and the closing `)`.+  , trailing :: Text+  -- ^ Whitespace and comments following the annotation itself.+  }+  deriving (Show, Eq)++annIdentifier :: Ann -> Identifier+annIdentifier = (.identifier)++annFormat :: Ann -> Maybe AnnFormat+annFormat = (.format)++{----- Node -----}++data Node = Node+  { ann :: Maybe Ann+  , name :: Identifier+  , entries :: [Entry]+  , children :: Maybe NodeList+  , format :: Maybe NodeFormat+  }+  deriving (Show, Eq)++data NodeFormat = NodeFormat+  { leading :: Text+  -- ^ Whitespace and comments preceding the node itself.+  , before_children :: Text+  -- ^ Whitespace and comments preceding the node's children block.+  , before_terminator :: Text+  -- ^ Whitespace and comments right before the node's terminator.+  , terminator :: Text+  -- ^ The terminator for the node.+  , trailing :: Text+  -- ^ Whitespace and comments following the node, after the terminator.+  }+  deriving (Show, Eq)++nodeAnn :: Node -> Maybe Ann+nodeAnn = (.ann)++nodeName :: Node -> Identifier+nodeName = (.name)++nodeEntries :: Node -> [Entry]+nodeEntries = (.entries)++nodeChildren :: Node -> Maybe NodeList+nodeChildren = (.children)++nodeFormat :: Node -> Maybe NodeFormat+nodeFormat = (.format)++-- | Get all the positional arguments of the node.+getArgs :: Node -> [Value]+getArgs node =+  [ value+  | Entry{name = Nothing, value} <- node.entries+  ]++-- | Get the first argument of the node.+getArg :: Node -> Maybe Value+getArg = listToMaybe . getArgs++-- | Get the properties of the node.+getProps :: Node -> Map Text Value+getProps node =+  Map.fromList+    [ (name.value, value)+    | Entry{name = Just name, value} <- node.entries+    ]++-- | Get the property with the given name in the node.+getProp :: Text -> Node -> Maybe Value+getProp name = Map.lookup name . getProps++{----- Entry -----}++data Entry = Entry+  { name :: Maybe Identifier+  -- ^ The name of the entry, if it's a property, Nothing if it's a positional arg+  , value :: Value+  , format :: Maybe EntryFormat+  }+  deriving (Show, Eq)++data EntryFormat = EntryFormat+  { leading :: Text+  -- ^ Whitespace and comments preceding the entry itself.+  , after_key :: Text+  -- ^ Whitespace and comments between an entry's key name and its equals sign.+  , after_eq :: Text+  -- ^ Whitespace and comments between an entry's equals sign and its value.+  , trailing :: Text+  -- ^ Whitespace and comments following the entry itself.+  }+  deriving (Show, Eq)++entryName :: Entry -> Maybe Identifier+entryName = (.name)++entryValue :: Entry -> Value+entryValue = (.value)++entryFormat :: Entry -> Maybe EntryFormat+entryFormat = (.format)++{----- Value -----}++data Value = Value+  { ann :: Maybe Ann+  , data_ :: ValueData+  , format :: Maybe ValueFormat+  }+  deriving (Show, Eq)++data ValueFormat = ValueFormat+  { repr :: Text+  -- ^ The actual text representation of the value.+  }+  deriving (Show, Eq)++valueAnn :: Value -> Maybe Ann+valueAnn = (.ann)++valueData :: Value -> ValueData+valueData = (.data_)++valueFormat :: Value -> Maybe ValueFormat+valueFormat = (.format)++data ValueData+  = Text Text+  | Number Scientific+  | Bool Bool+  | Null+  deriving (Show, Eq)++{----- Identifier -----}++data Identifier = Identifier+  { value :: Text+  , format :: Maybe IdentifierFormat+  }+  deriving (Show, Eq, Ord)++data IdentifierFormat = IdentifierFormat+  { repr :: Text+  }+  deriving (Show, Eq, Ord)++fromIdentifier :: Identifier -> Text+fromIdentifier = (.value)++identifierFormat :: Identifier -> Maybe IdentifierFormat+identifierFormat = (.format)++toIdentifier :: Text -> Identifier+toIdentifier value = Identifier{value = value, format = Nothing}
− test/Data/KDL/Decoder/ArrowSpec.hs
@@ -1,1182 +0,0 @@-{-# LANGUAGE Arrows #-}-{-# LANGUAGE DisambiguateRecordFields #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.KDL.Decoder.ArrowSpec (spec) where--import Control.Arrow (returnA)-import Control.Monad (forM_, unless, when)-import Data.KDL.Decoder.Arrow qualified as KDL-import Data.KDL.Decoder.Schema qualified as KDL-import Data.KDL.Types (-  Entry (..),-  Identifier (..),-  Node (..),-  NodeList (..),-  Value (..),-  ValueData (..),- )-import Data.Map qualified as Map-import Data.Proxy (Proxy (..))-import Data.Text (Text)-import Data.Text qualified as Text-import Data.Typeable (typeRep)-import Skeletest-import Skeletest.Predicate qualified as P--decodeErrorMsg :: [Text] -> Predicate IO (Either KDL.DecodeError a)-decodeErrorMsg msgs = P.left (KDL.renderDecodeError P.>>> P.eq msg)- where-  msg = Text.intercalate "\n" msgs--spec :: Spec-spec = do-  apiSpec-  schemaSpec-  decodeNodeSpec-  decodeValueSpec--apiSpec :: Spec-apiSpec = do-  describe "decodeWith" $ do-    it "fails with helpful error if parsing fails" $ do-      let config = "foo hello= 123"-          decoder = KDL.document $ KDL.node @Node "foo"-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: <root>"-          , "  1:10:"-          , "    |"-          , "  1 | foo hello= 123"-          , "    |          ^^"-          , "  unexpected \"= \""-          , "  expecting Node Child, Node Space, or Node Terminator"-          ]--    it "fails with user-defined error" $ do-      let config = "foo -1"-          decoder =-            KDL.document . KDL.argAtWith "foo" $-              KDL.withDecoder KDL.number $ \x -> do-                when (x < 0) $ do-                  KDL.failM $ "Got negative number: " <> (Text.pack . show) x-                pure x-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0 > arg #0"-          , "  Got negative number: -1.0"-          ]--    it "shows context in deeply nested error" $ do-      let config = "foo; foo { bar { baz; baz; baz; baz a=1; }; }"-          decoder =-            KDL.document-              . (KDL.many . KDL.nodeWith "foo" . KDL.children)-              . (KDL.many . KDL.nodeWith "bar" . KDL.children)-              . (KDL.many . KDL.nodeWith "baz")-              $ KDL.optional (KDL.prop @Text "a")-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #1 > bar #0 > baz #3 > prop a"-          , "  Expected text, got: 1"-          ]--  describe "NodeListDecoder" $ do-    describe "node" $ do-      it "decodes a node" $ do-        let config = "foo 1.0"-            decoder = KDL.document $ proc () -> do-              KDL.node "foo" -< ()-            expected =-              Node-                { ann = Nothing-                , name = Identifier{value = "foo", format = Nothing}-                , entries =-                    [ Entry-                        { name = Nothing-                        , value = Value{ann = Nothing, data_ = Number 1.0, format = Nothing}-                        , format = Nothing-                        }-                    ]-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "decodes multiple nodes" $ do-        let config = "foo; foo"-            decoder = KDL.document $ proc () -> do-              KDL.many $ KDL.node "foo" -< ()-            expected = [fooNode, fooNode]-            fooNode =-              Node-                { ann = Nothing-                , name = Identifier{value = "foo", format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "decodes nodes in any order" $ do-        let config = "foo; bar"-            decoder = KDL.document $ proc () -> do-              bar <- KDL.node "bar" -< ()-              foo <- KDL.node "foo" -< ()-              returnA -< (bar, foo)-            expected = (node "bar", node "foo")-            node name =-              Node-                { ann = Nothing-                , name = Identifier{value = name, format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "fails when not enough nodes" $ do-        let config = "foo"-            decoder = KDL.document $ proc () -> do-              foo1 <- KDL.node @Node "foo" -< ()-              foo2 <- KDL.node @Node "foo" -< ()-              returnA -< (foo1, foo2)-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: <root>"-            , "  Expected another node: foo"-            ]--    -- Most behaviors tested with `node`-    describe "nodeWith" $ do-      it "decodes a node" $ do-        let config = "foo 1.0 { hello \"world\"; }"-            decodeFoo = proc () -> do-              arg <- KDL.arg @Int -< ()-              child <- KDL.children $ KDL.argAt @Text "hello" -< ()-              returnA -< (arg, child)-            decoder = KDL.document $ proc () -> do-              KDL.nodeWith "foo" decodeFoo -< ()-        KDL.decodeWith decoder config `shouldBe` Right (1, "world")--      it "fails when node fails to parse" $ do-        let config = "foo 1.0"-            decodeFoo = proc () -> do-              KDL.arg @Text -< ()-            decoder = KDL.document $ proc () -> do-              KDL.nodeWith "foo" decodeFoo -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected text, got: 1.0"-            ]--    -- Most behaviors tested with `nodeWith`-    describe "nodeWith'" $ do-      it "decodes a node with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "(test)foo 1"-              decoder = KDL.document $ proc () -> do-                KDL.nodeWith' "foo" anns $ KDL.arg @Int -< ()-          KDL.decodeWith decoder config `shouldBe` Right 1--      it "decodes a node without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo 1"-              decoder = KDL.document $ proc () -> do-                KDL.nodeWith' "foo" anns $ KDL.arg @Int -< ()-          KDL.decodeWith decoder config `shouldBe` Right 1--      it "fails when node has unexpected annotation" $ do-        let config = "(test)foo 2"-            decoder = KDL.document $ proc () -> do-              KDL.nodeWith' "foo" ["FOO"] $ KDL.arg @Int -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected annotation to be one of [\"FOO\"], got: test"-            ]--    describe "remainingNodes" $ do-      it "returns all remaining nodes" $ do-        let config = "foo 1.0; foo 2.0; bar"-            decoder = KDL.document $ proc () -> do-              _ <- KDL.node @Node "foo" -< ()-              KDL.remainingNodes -< ()-            expected =-              Map.fromList-                [ ("foo", [fooNode2])-                , ("bar", [barNode])-                ]-            fooNode2 =-              Node-                { ann = Nothing-                , name = Identifier{value = "foo", format = Nothing}-                , entries =-                    [ Entry-                        { name = Nothing-                        , value = Value{ann = Nothing, data_ = Number 2.0, format = Nothing}-                        , format = Nothing-                        }-                    ]-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-            barNode =-              Node-                { ann = Nothing-                , name = Identifier{value = "bar", format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--    -- Most behaviors tested with `remainingNodes`-    describe "remainingNodesWith" $ do-      it "returns all remaining nodes" $ do-        let config = "foo 1.0; foo 2.0; bar"-            decodeNode = proc () -> do-              KDL.optional $ KDL.arg @Int -< ()-            decoder = KDL.document $ proc () -> do-              _ <- KDL.node @Node "foo" -< ()-              KDL.remainingNodesWith decodeNode -< ()-            expected =-              Map.fromList-                [ ("foo", [Just 2])-                , ("bar", [Nothing])-                ]-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "fails when node fails to parse" $ do-        let config = "foo 1; bar 1; bar \"hello\""-            decodeNode = proc () -> do-              KDL.arg @Int -< ()-            decoder = KDL.document $ proc () -> do-              KDL.remainingNodesWith decodeNode -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: bar #1 > arg #0"-            , "  Expected number, got: hello"-            ]--    -- Most behaviors tested with `remainingNodesWith`-    describe "remainingNodesWith'" $ do-      it "decodes a node with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "(test)foo 1"-              decoder = KDL.document $ proc () -> do-                KDL.remainingNodesWith' anns $ KDL.arg @Int -< ()-          KDL.decodeWith decoder config-            `shouldBe` (Right . Map.fromList) [("foo", [1])]--      it "decodes a node without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo 1"-              decoder = KDL.document $ proc () -> do-                KDL.remainingNodesWith' anns $ KDL.arg @Int -< ()-          KDL.decodeWith decoder config-            `shouldBe` (Right . Map.fromList) [("foo", [1])]--      it "fails when node has unexpected annotation" $ do-        let config = "(FOO)foo 1; (test)foo 2"-            decoder = KDL.document $ proc () -> do-              KDL.remainingNodesWith' ["FOO"] $ KDL.arg @Int -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #1"-            , "  Expected annotation to be one of [\"FOO\"], got: test"-            ]--    describe "argAt" $ do-      it "gets argument at a node" $ do-        let config = "foo \"bar\"; hello \"world\""-            decoder = KDL.document $ proc () -> do-              hello <- KDL.argAt @Text "hello" -< ()-              foo <- KDL.argAt @Text "foo" -< ()-              returnA -< (hello, foo)-        KDL.decodeWith decoder config `shouldBe` Right ("world", "bar")--      it "fails if no node" $ do-        let config = "other_node"-            decoder = KDL.document $ proc () -> do-              KDL.argAt @Int "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: <root>"-            , "  Expected node: foo"-            ]--      it "fails if node has no args" $ do-        let config = "foo"-            decoder = KDL.document $ proc () -> do-              KDL.argAt @Int "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected arg #0"-            ]--      it "fails if arg fails to parse" $ do-        let config = "foo 1"-            decoder = KDL.document $ proc () -> do-              KDL.argAt @Text "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected text, got: 1"-            ]--    -- Most behaviors tested with `argAt`-    describe "argAtWith" $ do-      it "gets argument at a node" $ do-        let config = "foo 1"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" $ show . (* 10) <$> KDL.number -< ()-        KDL.decodeWith decoder config `shouldBe` Right "10.0"--    -- Most behaviors tested with `argAtWith`-    describe "argAtWith'" $ do-      it "decodes argument with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo (test)a"-              decoder = KDL.document $ proc () -> do-                KDL.argAtWith' "foo" anns KDL.text -< ()-          KDL.decodeWith decoder config `shouldBe` Right "a"--      it "decodes argument without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a"-              decoder = KDL.document $ proc () -> do-                KDL.argAtWith' "foo" anns KDL.text -< ()-          KDL.decodeWith decoder config `shouldBe` Right "a"--      it "fails when argument has unexpected annotation" $ do-        let config = "foo (test)a"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith' "foo" ["VAL"] KDL.text -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "argsAt" $ do-      it "gets arguments at a node" $ do-        let config = "foo 1 2 3"-            decoder = KDL.document $ proc () -> do-              KDL.argsAt @Int "foo" -< ()-        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]--      it "returns empty list if no node" $ do-        let config = ""-            decoder = KDL.document $ proc () -> do-              KDL.argsAt @Int "foo" -< ()-        KDL.decodeWith decoder config `shouldBe` Right []--      it "returns empty list if node has no args" $ do-        let config = "foo"-            decoder = KDL.document $ proc () -> do-              KDL.argsAt @Int "foo" -< ()-        KDL.decodeWith decoder config `shouldBe` Right []--      it "fails if any arg fails to parse" $ do-        let config = "foo 1 \"asdf\""-            decoder = KDL.document $ proc () -> do-              KDL.argsAt @Int "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #1"-            , "  Expected number, got: asdf"-            ]--    -- Most behaviors tested with `argsAt`-    describe "argsAtWith" $ do-      it "gets arguments at a node" $ do-        let config = "foo 1 2"-            decoder = KDL.document $ proc () -> do-              KDL.argsAtWith "foo" $ show . (* 10) <$> KDL.number -< ()-        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]--    -- Most behaviors tested with `argsAtWith`-    describe "argsAtWith'" $ do-      it "decodes arguments with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo (test)a (test)b"-              decoder = KDL.document $ proc () -> do-                KDL.argsAtWith' "foo" anns KDL.text -< ()-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "decodes arguments without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a b"-              decoder = KDL.document $ proc () -> do-                KDL.argsAtWith' "foo" anns KDL.text -< ()-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "fails when argument has unexpected annotation" $ do-        let config = "foo (VAL)a (test)b"-            decoder = KDL.document $ proc () -> do-              KDL.argsAtWith' "foo" ["VAL"] KDL.text -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #1"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "dashChildrenAt" $ do-      it "gets dash children at a node" $ do-        let config = "foo { - 1; - 2; - 3; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashChildrenAt @Int "foo" -< ()-        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]--      it "returns empty list if no node" $ do-        let config = ""-            decoder = KDL.document $ proc () -> do-              KDL.dashChildrenAt @Int "foo" -< ()-        KDL.decodeWith decoder config `shouldBe` Right []--      it "returns empty list if node has no dash children" $ do-        forM_ ["foo", "foo {}"] $ \config -> do-          let decoder = KDL.document $ proc () -> do-                KDL.dashChildrenAt @Int "foo" -< ()-          KDL.decodeWith decoder config `shouldBe` Right []--      it "fails if dash children have multiple args" $ do-        let config = "foo { - 1 2; - 3 4; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashChildrenAt @Int "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #0"-            , "  Unexpected arg #1: 2"-            ]--      it "fails if node has non-dash children" $ do-        let config = "foo { - 1; bar 1 2 3; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashChildrenAt @Int "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected node: bar #0"-            ]--      it "fails if any child fails to parse" $ do-        let config = "foo { - 1; - \"asdf\"; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashChildrenAt @Int "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #1 > arg #0"-            , "  Expected number, got: asdf"-            ]--    -- Most behaviors tested with `dashChildrenAt`-    describe "dashChildrenAtWith" $ do-      it "gets dash children at a node" $ do-        let config = "foo { - 1; - 2; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashChildrenAtWith "foo" $ show . (* 10) <$> KDL.number -< ()-        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]--    -- Most behaviors tested with `dashChildrenAtWith`-    describe "dashChildrenAtWith'" $ do-      it "decodes dash children with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo { - (test)a; - (test)b; }"-              decoder = KDL.document $ proc () -> do-                KDL.dashChildrenAtWith' "foo" anns KDL.text -< ()-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "decodes dash children without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo { - a; - b; }"-              decoder = KDL.document $ proc () -> do-                KDL.dashChildrenAtWith' "foo" anns KDL.text -< ()-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "fails when child has unexpected annotation" $ do-        let config = "foo { - (test)a; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashChildrenAtWith' "foo" ["VAL"] KDL.text -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #0 > arg #0"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "dashNodesAt" $ do-      it "gets dash nodes at a node" $ do-        let config = "foo { - { bar; }; - { baz; }; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashNodesAt "foo" -< ()-            expected = [node "-" [node "bar" []], node "-" [node "baz" []]]-            node name children =-              Node-                { ann = Nothing-                , name = Identifier{value = name, format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = children, format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "returns empty list if no node" $ do-        let config = ""-            decoder = KDL.document $ proc () -> do-              KDL.dashNodesAt @Node "foo" -< ()-        KDL.decodeWith decoder config `shouldBe` Right []--      it "returns empty list if node has no dash nodes" $ do-        forM_ ["foo", "foo {}"] $ \config -> do-          let decoder = KDL.document $ proc () -> do-                KDL.dashNodesAt @Node "foo" -< ()-          KDL.decodeWith decoder config `shouldBe` Right []--      it "fails if node has non-dash nodes" $ do-        let config = "foo { - 1; bar 1 2 3; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashNodesAt @Node "foo" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected node: bar #0"-            ]--    -- Most behaviors tested with `dashNodesAt`-    describe "dashNodesAtWith" $ do-      it "gets dash nodes at a node" $ do-        let config = "foo { - 1 { bar \"hello\"; }; - 2 { bar \"world\"; }; }"-            decodeChild = proc () -> do-              arg <- KDL.arg @Int -< ()-              child <- KDL.children $ KDL.nodeWith "bar" $ KDL.arg @Text -< ()-              returnA -< (arg, child)-            decoder = KDL.document $ proc () -> do-              KDL.dashNodesAtWith "foo" decodeChild -< ()-        KDL.decodeWith decoder config `shouldBe` Right [(1, "hello"), (2, "world")]--      it "fails if any child fails to parse" $ do-        let config = "foo { - { bar 1; }; - { bar \"test\"; }; }"-            decoder = KDL.document $ proc () -> do-              KDL.dashNodesAtWith "foo" $ KDL.children $ KDL.argAt @Int "bar" -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #1 > bar #0 > arg #0"-            , "  Expected number, got: test"-            ]--  describe "NodeDecoder" $ do-    let decodeNode name decoder config =-          KDL.decodeWith-            ( KDL.document $ proc () -> do-                KDL.nodeWith name decoder -< ()-            )-            config--    describe "arg" $ do-      it "decodes an argument" $ do-        let config = "foo 1 \"bar\""-            decoder = proc () -> do-              arg1 <- KDL.arg @Int -< ()-              arg2 <- KDL.arg @Text -< ()-              returnA -< (arg1, arg2)-        decodeNode "foo" decoder config `shouldBe` Right (1, "bar")--      it "decodes multiple arguments" $ do-        let config = "foo 1 2 3"-            decoder = proc () -> do-              KDL.many $ KDL.arg @Int -< ()-        decodeNode "foo" decoder config `shouldBe` Right [1, 2, 3]--      it "fails if argument doesn't exist" $ do-        let config = "foo"-            decoder = proc () -> do-              KDL.arg @Int -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected arg #0"-            ]--      it "fails if argument fails to parse" $ do-        let config = "foo \"test\""-            decoder = proc () -> do-              KDL.arg @Int -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected number, got: test"-            ]--      it "fails if not all arguments are decoded" $ do-        let config = "foo 1 2 3"-            decoder = proc () -> do-              KDL.arg @Int -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected arg #1: 2"-            ]--    -- Most behaviors tested with `arg`-    describe "argWith" $ do-      it "decodes an argument" $ do-        let config = "foo \"bar\""-            decoder = proc () -> do-              KDL.argWith KDL.text -< ()-        decodeNode "foo" decoder config `shouldBe` Right "bar"--    -- Most behaviors tested with `argWith`-    describe "argWith'" $ do-      it "decodes argument with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo (test)a"-              decoder = proc () -> do-                KDL.argWith' anns KDL.text -< ()-          decodeNode "foo" decoder config `shouldBe` Right "a"--      it "decodes argument without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a"-              decoder = proc () -> do-                KDL.argWith' anns KDL.text -< ()-          decodeNode "foo" decoder config `shouldBe` Right "a"--      it "fails when argument has unexpected annotation" $ do-        let config = "foo (test)a"-            decoder = proc () -> do-              KDL.argWith' ["VAL"] KDL.text -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "prop" $ do-      it "decodes a prop" $ do-        let config = "foo test1=1 test2=\"hello\""-            decoder = proc () -> do-              prop1 <- KDL.prop @Text "test2" -< ()-              prop2 <- KDL.prop @Int "test1" -< ()-              returnA -< (prop1, prop2)-        decodeNode "foo" decoder config `shouldBe` Right ("hello", 1)--      it "can optionally decode a prop" $ do-        let config = "foo a=1"-            decoder = proc () -> do-              a <- KDL.optional $ KDL.prop @Int "a" -< ()-              b <- KDL.optional $ KDL.prop @Int "b" -< ()-              returnA -< (a, b)-        decodeNode "foo" decoder config `shouldBe` Right (Just 1, Nothing)--      it "decodes last prop" $ do-        let config = "foo test=1 test=2"-            decoder = proc () -> do-              KDL.prop @Int "test" -< ()-        decodeNode "foo" decoder config `shouldBe` Right 2--      it "fails if prop doesn't exist" $ do-        let config = "foo 123"-            decoder = proc () -> do-              KDL.prop @Int "test" -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected prop: test"-            ]--      it "fails if prop fails to parse" $ do-        let config = "foo hello=world"-            decoder = proc () -> do-              KDL.prop @Int "hello" -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop hello"-            , "  Expected number, got: world"-            ]--      it "fails if not all props are decoded" $ do-        let config = "foo a=1 b=2"-            decoder = proc () -> do-              KDL.prop @Int "a" -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected prop: b=2"-            ]--    -- Most behaviors tested with `prop`-    describe "propWith" $ do-      it "decodes a prop" $ do-        let config = "foo a=1"-            decoder = proc () -> do-              KDL.propWith "a" KDL.number -< ()-        decodeNode "foo" decoder config `shouldBe` Right 1--    -- Most behaviors tested with `propWith`-    describe "propWith'" $ do-      it "decodes prop with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=(test)1"-              decoder = proc () -> do-                KDL.propWith' "a" anns KDL.number -< ()-          decodeNode "foo" decoder config `shouldBe` Right 1--      it "decodes prop without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=1"-              decoder = proc () -> do-                KDL.propWith' "a" anns KDL.number -< ()-          decodeNode "foo" decoder config `shouldBe` Right 1--      it "fails when prop has unexpected annotation" $ do-        let config = "foo a=(test)1"-            decoder = proc () -> do-              KDL.propWith' "a" ["VAL"] KDL.number -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop a"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "remainingProps" $ do-      it "decodes remaining props" $ do-        let config = "foo a=1 b=2 c=3 b=4"-            decoder = proc () -> do-              _ <- KDL.prop @Int "a" -< ()-              KDL.remainingProps @Int -< ()-        decodeNode "foo" decoder config-          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]--      it "returns empty map if no props left" $ do-        let config = "foo a=1"-            decoder = proc () -> do-              _ <- KDL.prop @Int "a" -< ()-              KDL.remainingProps @Int -< ()-        decodeNode "foo" decoder config `shouldBe` Right Map.empty--      it "fails if prop fails to parse" $ do-        let config = "foo a=1 b=1 c=2 c=test"-            decoder = proc () -> do-              _ <- KDL.prop @Int "a" -< ()-              KDL.remainingProps @Int -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop c"-            , "  Expected number, got: test"-            ]--    -- Most behaviors tested with `remainingProps`-    describe "remainingPropsWith" $ do-      it "decodes remaining props" $ do-        let config = "foo a=1 b=2 c=3 b=4"-            decoder = proc () -> do-              _ <- KDL.prop @Int "a" -< ()-              KDL.remainingPropsWith KDL.number -< ()-        decodeNode "foo" decoder config-          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]--    -- Most behaviors tested with `remainingPropsWith`-    describe "remainingPropsWith'" $ do-      it "decodes props with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=(test)1 b=(test)2"-              decoder = proc () -> do-                KDL.remainingPropsWith' anns KDL.number -< ()-          decodeNode "foo" decoder config-            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]--      it "decodes props without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=1 b=2"-              decoder = proc () -> do-                KDL.remainingPropsWith' anns KDL.number -< ()-          decodeNode "foo" decoder config-            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]--      it "fails when prop has unexpected annotation" $ do-        let config = "foo a=(VAL)1 b=(test)2"-            decoder = proc () -> do-              KDL.remainingPropsWith' ["VAL"] KDL.number -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop b"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "children" $ do-      it "decodes children" $ do-        let config = "foo { bar test; }"-            decoder = proc () -> do-              KDL.children $ KDL.node @Node "bar" -< ()-            expected =-              Node-                { ann = Nothing-                , name = Identifier{value = "bar", format = Nothing}-                , entries =-                    [ Entry-                        { name = Nothing-                        , value = Value{ann = Nothing, data_ = Text "test", format = Nothing}-                        , format = Nothing-                        }-                    ]-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        decodeNode "foo" decoder config `shouldBe` Right expected--      it "can be re-entered" $ do-        let config = "foo { bar a; baz b; }"-            decoder = proc () -> do-              arg1 <- KDL.children $ KDL.argAt @Text "bar" -< ()-              arg2 <- KDL.children $ KDL.argAt @Text "baz" -< ()-              returnA -< (arg1, arg2)-        decodeNode "foo" decoder config `shouldBe` Right ("a", "b")--      it "fails if not all children are decoded" $ do-        let config = "foo { asdf; bar; }"-            decoder = proc () -> do-              KDL.children $ KDL.node @Node "bar" -< ()-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected node: asdf #0"-            ]--  describe "ValueDecoder" $ do-    describe "any" $ do-      it "decodes any value" $ do-        let config = "foo 1.0 asdf true"-            decoder = KDL.document $ proc () -> do-              KDL.argsAtWith "foo" KDL.any -< ()-            val data_ =-              Value-                { ann = Nothing-                , data_ = data_-                , format = Nothing-                }-        KDL.decodeWith decoder config-          `shouldBe` Right [val $ Number 1, val $ Text "asdf", val $ Bool True]--    describe "text" $ do-      it "decodes text value" $ do-        let config = "foo asdf"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.text -< ()-        KDL.decodeWith decoder config `shouldBe` Right "asdf"--      it "fails when value is not text" $ do-        let config = "foo 1"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.text -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected text, got: 1"-            ]--    describe "number" $ do-      it "decodes number value" $ do-        let config = "foo 1"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.number -< ()-        KDL.decodeWith decoder config `shouldBe` Right 1--      it "fails when value is not number" $ do-        let config = "foo asdf"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.number -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected number, got: asdf"-            ]--    describe "bool" $ do-      it "decodes bool value" $ do-        let config = "foo true"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.bool -< ()-        KDL.decodeWith decoder config `shouldBe` Right True--      it "fails when value is not bool" $ do-        let config = "foo 1"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.bool -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected bool, got: 1"-            ]--    describe "null" $ do-      it "decodes null value" $ do-        let config = "foo null"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.null -< ()-        KDL.decodeWith decoder config `shouldBe` Right ()--      it "fails when value is not null" $ do-        let config = "foo 1"-            decoder = KDL.document $ proc () -> do-              KDL.argAtWith "foo" KDL.null -< ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected null, got: 1"-            ]--  describe "Combinators" $ do-    describe "oneOf" $ do-      it "decodes one of the options" $ do-        let config = "foo 123 hello"-            decoder = KDL.document $ proc () -> do-              KDL.nodeWith "foo" . KDL.many . KDL.argWith $-                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.text]-                -<-                  ()-        KDL.decodeWith decoder config `shouldBe` Right [Left 123, Right "hello"]--      it "fails if none can be decoded" $ do-        let config = "foo 123 hello"-            decoder = KDL.document $ proc () -> do-              KDL.nodeWith "foo" . KDL.many . KDL.argWith $-                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.bool]-                -<-                  ()-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #1"-            , "  Expected bool, got: hello"-            , "  Expected number, got: hello"-            ]--    describe "option" $ do-      it "defaults to the given value" $ do-        let config = "foo"-            decoder = KDL.document $ proc () -> do-              KDL.nodeWith "foo" $ KDL.option 123 $ KDL.arg @Int -< ()-        KDL.decodeWith decoder config `shouldBe` Right 123--schemaSpec :: Spec-schemaSpec = do-  describe "documentSchema" $ do-    it "gets the schema of a decoder" $ do-      let decoder = KDL.document $ proc () -> do-            x <- KDL.nodeWith "foo" $ show <$> KDL.argWith decodeFoo -< ()-            ys <- KDL.many $ KDL.nodeWith "bar" $ KDL.arg @String -< ()-            returnA -< (x, ys)-          decodeFoo =-            KDL.oneOf-              [ Left <$> KDL.valueDecoder @Bool-              , Right <$> KDL.valueDecoder @Text-              ]-          expected =-            KDL.SchemaAnd-              [ KDL.SchemaOne . KDL.NodeNamed "foo" $-                  KDL.TypedNodeSchema-                    { typeHint = typeRep $ Proxy @String-                    , validTypeAnns = []-                    , nodeSchema =-                        KDL.SchemaOne . KDL.NodeArg $-                          KDL.TypedValueSchema-                            { typeHint = typeRep $ Proxy @(Either Bool Text)-                            , validTypeAnns = []-                            , dataSchema =-                                KDL.SchemaOr-                                  [ KDL.SchemaOne KDL.BoolSchema-                                  , KDL.SchemaOne KDL.TextSchema-                                  ]-                            }-                    }-              , KDL.SchemaOr-                  [ KDL.SchemaSome . KDL.SchemaOne . KDL.NodeNamed "bar" $-                      KDL.TypedNodeSchema-                        { typeHint = typeRep $ Proxy @String-                        , validTypeAnns = []-                        , nodeSchema =-                            KDL.SchemaOne . KDL.NodeArg $-                              KDL.TypedValueSchema-                                { typeHint = typeRep $ Proxy @String-                                , validTypeAnns = ["string"]-                                , dataSchema = KDL.SchemaOne KDL.TextSchema-                                }-                        }-                  , KDL.SchemaAnd []-                  ]-              ]-      KDL.documentSchema decoder `shouldBe` expected--newtype MyNode = MyNode Int-  deriving (Eq)--instance KDL.DecodeNode MyNode where-  validNodeTypeAnns _ = ["MyNode"]-  nodeDecoder = proc () -> do-    x <- KDL.arg -< ()-    if not (0 < x && x < 10)-      then KDL.fail -< "Invalid argument: " <> (Text.pack . show) x-      else returnA -< ()-    returnA -< MyNode x--decodeNodeSpec :: Spec-decodeNodeSpec = do-  describe "DecodeNode" $ do-    it "decodes a custom node" $ do-      let config = "foo 1"-          decoder = KDL.document $ proc () -> do-            KDL.node "foo" -< ()-      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)--    it "throws user-specified error" $ do-      let config = "foo 100"-          decoder = KDL.document $ proc () -> do-            KDL.node @MyNode "foo" -< ()-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0"-          , "  Invalid argument: 100"-          ]--    it "decodes valid type ann" $ do-      let config = "(MyNode)foo 1"-          decoder = KDL.document $ proc () -> do-            KDL.node "foo" -< ()-      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)--    it "fails on invalid type ann" $ do-      let config = "(bad)foo 1"-          decoder = KDL.document $ proc () -> do-            KDL.node @MyNode "foo" -< ()-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0"-          , "  Expected annotation to be one of [\"MyNode\"], got: bad"-          ]--newtype MyVal = MyVal Double-  deriving (Eq)--instance KDL.DecodeValue MyVal where-  validValueTypeAnns _ = ["MyVal"]-  valueDecoder = KDL.withDecoder KDL.number $ \x -> do-    unless (0 < x && x < 10) $ do-      KDL.failM $ "Invalid value: " <> (Text.pack . show) x-    pure $ MyVal (realToFrac x)--decodeValueSpec :: Spec-decodeValueSpec = do-  describe "DecodeValue" $ do-    it "decodes a custom value" $ do-      let config = "foo 1"-          decoder = KDL.document $ proc () -> do-            KDL.argAt "foo" -< ()-      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)--    it "throws user-specified error" $ do-      let config = "foo 100.0"-          decoder = KDL.document $ proc () -> do-            KDL.argAt @MyVal "foo" -< ()-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0 > arg #0"-          , "  Invalid value: 100.0"-          ]--    it "decodes valid type ann" $ do-      let config = "foo (MyVal)1"-          decoder = KDL.document $ proc () -> do-            KDL.argAt "foo" -< ()-      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)--    it "fails on invalid type ann" $ do-      let config = "foo (bad)1"-          decoder = KDL.document $ proc () -> do-            KDL.argAt @MyVal "foo" -< ()-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0 > arg #0"-          , "  Expected annotation to be one of [\"MyVal\"], got: bad"-          ]
− test/Data/KDL/Decoder/MonadSpec.hs
@@ -1,1122 +0,0 @@-{-# LANGUAGE DisambiguateRecordFields #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.KDL.Decoder.MonadSpec (spec) where--import Control.Monad (forM_, unless, when)-import Data.KDL qualified as KDL-import Data.KDL.Types (-  Entry (..),-  Identifier (..),-  Node (..),-  NodeList (..),-  Value (..),-  ValueData (..),- )-import Data.Map qualified as Map-import Data.Text (Text)-import Data.Text qualified as Text-import Skeletest-import Skeletest.Predicate qualified as P--decodeErrorMsg :: [Text] -> Predicate IO (Either KDL.DecodeError a)-decodeErrorMsg msgs = P.left (KDL.renderDecodeError P.>>> P.eq msg)- where-  msg = Text.intercalate "\n" msgs--spec :: Spec-spec = do-  apiSpec-  decodeNodeSpec-  decodeValueSpec--apiSpec :: Spec-apiSpec = do-  describe "decodeWith" $ do-    it "fails with helpful error if parsing fails" $ do-      let config = "foo hello= 123"-          decoder = KDL.document $ KDL.node @Node "foo"-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: <root>"-          , "  1:10:"-          , "    |"-          , "  1 | foo hello= 123"-          , "    |          ^^"-          , "  unexpected \"= \""-          , "  expecting Node Child, Node Space, or Node Terminator"-          ]--    it "fails with user-defined error" $ do-      let config = "foo -1"-          decoder =-            KDL.document . KDL.argAtWith "foo" $-              KDL.withDecoder KDL.number $ \x -> do-                when (x < 0) $ do-                  KDL.failM $ "Got negative number: " <> (Text.pack . show) x-                pure x-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0 > arg #0"-          , "  Got negative number: -1.0"-          ]--    it "shows context in deeply nested error" $ do-      let config = "foo; foo { bar { baz; baz; baz; baz a=1; }; }"-          decoder =-            KDL.document-              . (KDL.many . KDL.nodeWith "foo" . KDL.children)-              . (KDL.many . KDL.nodeWith "bar" . KDL.children)-              . (KDL.many . KDL.nodeWith "baz")-              $ KDL.optional (KDL.prop @Text "a")-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #1 > bar #0 > baz #3 > prop a"-          , "  Expected text, got: 1"-          ]--  describe "NodeListDecoder" $ do-    describe "node" $ do-      it "decodes a node" $ do-        let config = "foo 1.0"-            decoder = KDL.document $ do-              KDL.node "foo"-            expected =-              Node-                { ann = Nothing-                , name = Identifier{value = "foo", format = Nothing}-                , entries =-                    [ Entry-                        { name = Nothing-                        , value = Value{ann = Nothing, data_ = Number 1.0, format = Nothing}-                        , format = Nothing-                        }-                    ]-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "decodes multiple nodes" $ do-        let config = "foo; foo"-            decoder = KDL.document $ do-              KDL.many $ KDL.node "foo"-            expected = [fooNode, fooNode]-            fooNode =-              Node-                { ann = Nothing-                , name = Identifier{value = "foo", format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "decodes nodes in any order" $ do-        let config = "foo; bar"-            decoder = KDL.document $ do-              bar <- KDL.node "bar"-              foo <- KDL.node "foo"-              pure (bar, foo)-            expected = (node "bar", node "foo")-            node name =-              Node-                { ann = Nothing-                , name = Identifier{value = name, format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "fails when not enough nodes" $ do-        let config = "foo"-            decoder = KDL.document $ do-              foo1 <- KDL.node @Node "foo"-              foo2 <- KDL.node @Node "foo"-              pure (foo1, foo2)-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: <root>"-            , "  Expected another node: foo"-            ]--    -- Most behaviors tested with `node`-    describe "nodeWith" $ do-      it "decodes a node" $ do-        let config = "foo 1.0 { hello \"world\"; }"-            decodeFoo = do-              arg <- KDL.arg @Int-              child <- KDL.children $ KDL.argAt @Text "hello"-              pure (arg, child)-            decoder = KDL.document $ do-              KDL.nodeWith "foo" decodeFoo-        KDL.decodeWith decoder config `shouldBe` Right (1, "world")--      it "fails when node fails to parse" $ do-        let config = "foo 1.0"-            decodeFoo = do-              KDL.arg @Text-            decoder = KDL.document $ do-              KDL.nodeWith "foo" decodeFoo-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected text, got: 1.0"-            ]--    -- Most behaviors tested with `nodeWith`-    describe "nodeWith'" $ do-      it "decodes a node with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "(test)foo 1"-              decoder = KDL.document $ do-                KDL.nodeWith' "foo" anns $ KDL.arg @Int-          KDL.decodeWith decoder config `shouldBe` Right 1--      it "decodes a node without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo 1"-              decoder = KDL.document $ do-                KDL.nodeWith' "foo" anns $ KDL.arg @Int-          KDL.decodeWith decoder config `shouldBe` Right 1--      it "fails when node has unexpected annotation" $ do-        let config = "(test)foo 2"-            decoder = KDL.document $ do-              KDL.nodeWith' "foo" ["FOO"] $ KDL.arg @Int-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected annotation to be one of [\"FOO\"], got: test"-            ]--    describe "remainingNodes" $ do-      it "returns all remaining nodes" $ do-        let config = "foo 1.0; foo 2.0; bar"-            decoder = KDL.document $ do-              _ <- KDL.node @Node "foo"-              KDL.remainingNodes-            expected =-              Map.fromList-                [ ("foo", [fooNode2])-                , ("bar", [barNode])-                ]-            fooNode2 =-              Node-                { ann = Nothing-                , name = Identifier{value = "foo", format = Nothing}-                , entries =-                    [ Entry-                        { name = Nothing-                        , value = Value{ann = Nothing, data_ = Number 2.0, format = Nothing}-                        , format = Nothing-                        }-                    ]-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-            barNode =-              Node-                { ann = Nothing-                , name = Identifier{value = "bar", format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--    -- Most behaviors tested with `remainingNodes`-    describe "remainingNodesWith" $ do-      it "returns all remaining nodes" $ do-        let config = "foo 1.0; foo 2.0; bar"-            decodeNode = do-              KDL.optional $ KDL.arg @Int-            decoder = KDL.document $ do-              _ <- KDL.node @Node "foo"-              KDL.remainingNodesWith decodeNode-            expected =-              Map.fromList-                [ ("foo", [Just 2])-                , ("bar", [Nothing])-                ]-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "fails when node fails to parse" $ do-        let config = "foo 1; bar 1; bar \"hello\""-            decodeNode = do-              KDL.arg @Int-            decoder = KDL.document $ do-              KDL.remainingNodesWith decodeNode-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: bar #1 > arg #0"-            , "  Expected number, got: hello"-            ]--    -- Most behaviors tested with `remainingNodesWith`-    describe "remainingNodesWith'" $ do-      it "decodes a node with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "(test)foo 1"-              decoder = KDL.document $ do-                KDL.remainingNodesWith' anns $ KDL.arg @Int-          KDL.decodeWith decoder config-            `shouldBe` (Right . Map.fromList) [("foo", [1])]--      it "decodes a node without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo 1"-              decoder = KDL.document $ do-                KDL.remainingNodesWith' anns $ KDL.arg @Int-          KDL.decodeWith decoder config-            `shouldBe` (Right . Map.fromList) [("foo", [1])]--      it "fails when node has unexpected annotation" $ do-        let config = "(FOO)foo 1; (test)foo 2"-            decoder = KDL.document $ do-              KDL.remainingNodesWith' ["FOO"] $ KDL.arg @Int-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #1"-            , "  Expected annotation to be one of [\"FOO\"], got: test"-            ]--    describe "argAt" $ do-      it "gets argument at a node" $ do-        let config = "foo \"bar\"; hello \"world\""-            decoder = KDL.document $ do-              hello <- KDL.argAt @Text "hello"-              foo <- KDL.argAt @Text "foo"-              pure (hello, foo)-        KDL.decodeWith decoder config `shouldBe` Right ("world", "bar")--      it "fails if no node" $ do-        let config = "other_node"-            decoder = KDL.document $ do-              KDL.argAt @Int "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: <root>"-            , "  Expected node: foo"-            ]--      it "fails if node has no args" $ do-        let config = "foo"-            decoder = KDL.document $ do-              KDL.argAt @Int "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected arg #0"-            ]--      it "fails if arg fails to parse" $ do-        let config = "foo 1"-            decoder = KDL.document $ do-              KDL.argAt @Text "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected text, got: 1"-            ]--    -- Most behaviors tested with `argAt`-    describe "argAtWith" $ do-      it "gets argument at a node" $ do-        let config = "foo 1"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" $ show . (* 10) <$> KDL.number-        KDL.decodeWith decoder config `shouldBe` Right "10.0"--    -- Most behaviors tested with `argAtWith`-    describe "argAtWith'" $ do-      it "decodes argument with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo (test)a"-              decoder = KDL.document $ do-                KDL.argAtWith' "foo" anns KDL.text-          KDL.decodeWith decoder config `shouldBe` Right "a"--      it "decodes argument without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a"-              decoder = KDL.document $ do-                KDL.argAtWith' "foo" anns KDL.text-          KDL.decodeWith decoder config `shouldBe` Right "a"--      it "fails when argument has unexpected annotation" $ do-        let config = "foo (test)a"-            decoder = KDL.document $ do-              KDL.argAtWith' "foo" ["VAL"] KDL.text-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "argsAt" $ do-      it "gets arguments at a node" $ do-        let config = "foo 1 2 3"-            decoder = KDL.document $ do-              KDL.argsAt @Int "foo"-        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]--      it "returns empty list if no node" $ do-        let config = ""-            decoder = KDL.document $ do-              KDL.argsAt @Int "foo"-        KDL.decodeWith decoder config `shouldBe` Right []--      it "returns empty list if node has no args" $ do-        let config = "foo"-            decoder = KDL.document $ do-              KDL.argsAt @Int "foo"-        KDL.decodeWith decoder config `shouldBe` Right []--      it "fails if any arg fails to parse" $ do-        let config = "foo 1 \"asdf\""-            decoder = KDL.document $ do-              KDL.argsAt @Int "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #1"-            , "  Expected number, got: asdf"-            ]--    -- Most behaviors tested with `argsAt`-    describe "argsAtWith" $ do-      it "gets arguments at a node" $ do-        let config = "foo 1 2"-            decoder = KDL.document $ do-              KDL.argsAtWith "foo" $ show . (* 10) <$> KDL.number-        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]--    -- Most behaviors tested with `argsAtWith`-    describe "argsAtWith'" $ do-      it "decodes arguments with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo (test)a (test)b"-              decoder = KDL.document $ do-                KDL.argsAtWith' "foo" anns KDL.text-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "decodes arguments without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a b"-              decoder = KDL.document $ do-                KDL.argsAtWith' "foo" anns KDL.text-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "fails when argument has unexpected annotation" $ do-        let config = "foo (VAL)a (test)b"-            decoder = KDL.document $ do-              KDL.argsAtWith' "foo" ["VAL"] KDL.text-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #1"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "dashChildrenAt" $ do-      it "gets dash children at a node" $ do-        let config = "foo { - 1; - 2; - 3; }"-            decoder = KDL.document $ do-              KDL.dashChildrenAt @Int "foo"-        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]--      it "returns empty list if no node" $ do-        let config = ""-            decoder = KDL.document $ do-              KDL.dashChildrenAt @Int "foo"-        KDL.decodeWith decoder config `shouldBe` Right []--      it "returns empty list if node has no dash children" $ do-        forM_ ["foo", "foo {}"] $ \config -> do-          let decoder = KDL.document $ do-                KDL.dashChildrenAt @Int "foo"-          KDL.decodeWith decoder config `shouldBe` Right []--      it "fails if dash children have multiple args" $ do-        let config = "foo { - 1 2; - 3 4; }"-            decoder = KDL.document $ do-              KDL.dashChildrenAt @Int "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #0"-            , "  Unexpected arg #1: 2"-            ]--      it "fails if node has non-dash children" $ do-        let config = "foo { - 1; bar 1 2 3; }"-            decoder = KDL.document $ do-              KDL.dashChildrenAt @Int "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected node: bar #0"-            ]--      it "fails if any child fails to parse" $ do-        let config = "foo { - 1; - \"asdf\"; }"-            decoder = KDL.document $ do-              KDL.dashChildrenAt @Int "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #1 > arg #0"-            , "  Expected number, got: asdf"-            ]--    -- Most behaviors tested with `dashChildrenAt`-    describe "dashChildrenAtWith" $ do-      it "gets dash children at a node" $ do-        let config = "foo { - 1; - 2; }"-            decoder = KDL.document $ do-              KDL.dashChildrenAtWith "foo" $ show . (* 10) <$> KDL.number-        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]--    -- Most behaviors tested with `dashChildrenAtWith`-    describe "dashChildrenAtWith'" $ do-      it "decodes dash children with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo { - (test)a; - (test)b; }"-              decoder = KDL.document $ do-                KDL.dashChildrenAtWith' "foo" anns KDL.text-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "decodes dash children without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo { - a; - b; }"-              decoder = KDL.document $ do-                KDL.dashChildrenAtWith' "foo" anns KDL.text-          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]--      it "fails when child has unexpected annotation" $ do-        let config = "foo { - (test)a; }"-            decoder = KDL.document $ do-              KDL.dashChildrenAtWith' "foo" ["VAL"] KDL.text-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #0 > arg #0"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "dashNodesAt" $ do-      it "gets dash nodes at a node" $ do-        let config = "foo { - { bar; }; - { baz; }; }"-            decoder = KDL.document $ do-              KDL.dashNodesAt "foo"-            expected = [node "-" [node "bar" []], node "-" [node "baz" []]]-            node name children =-              Node-                { ann = Nothing-                , name = Identifier{value = name, format = Nothing}-                , entries = []-                , children = Just NodeList{nodes = children, format = Nothing}-                , format = Nothing-                }-        KDL.decodeWith decoder config `shouldBe` Right expected--      it "returns empty list if no node" $ do-        let config = ""-            decoder = KDL.document $ do-              KDL.dashNodesAt @Node "foo"-        KDL.decodeWith decoder config `shouldBe` Right []--      it "returns empty list if node has no dash nodes" $ do-        forM_ ["foo", "foo {}"] $ \config -> do-          let decoder = KDL.document $ do-                KDL.dashNodesAt @Node "foo"-          KDL.decodeWith decoder config `shouldBe` Right []--      it "fails if node has non-dash nodes" $ do-        let config = "foo { - 1; bar 1 2 3; }"-            decoder = KDL.document $ do-              KDL.dashNodesAt @Node "foo"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected node: bar #0"-            ]--    -- Most behaviors tested with `dashNodesAt`-    describe "dashNodesAtWith" $ do-      it "gets dash nodes at a node" $ do-        let config = "foo { - 1 { bar \"hello\"; }; - 2 { bar \"world\"; }; }"-            decodeChild = do-              arg <- KDL.arg @Int-              child <- KDL.children $ KDL.nodeWith "bar" $ KDL.arg @Text-              pure (arg, child)-            decoder = KDL.document $ do-              KDL.dashNodesAtWith "foo" decodeChild-        KDL.decodeWith decoder config `shouldBe` Right [(1, "hello"), (2, "world")]--      it "fails if any child fails to parse" $ do-        let config = "foo { - { bar 1; }; - { bar \"test\"; }; }"-            decoder = KDL.document $ do-              KDL.dashNodesAtWith "foo" $ KDL.children $ KDL.argAt @Int "bar"-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > - #1 > bar #0 > arg #0"-            , "  Expected number, got: test"-            ]--  describe "NodeDecoder" $ do-    let decodeNode name decoder config =-          KDL.decodeWith-            ( KDL.document $ do-                KDL.nodeWith name decoder-            )-            config--    describe "arg" $ do-      it "decodes an argument" $ do-        let config = "foo 1 \"bar\""-            decoder = do-              arg1 <- KDL.arg @Int-              arg2 <- KDL.arg @Text-              pure (arg1, arg2)-        decodeNode "foo" decoder config `shouldBe` Right (1, "bar")--      it "decodes multiple arguments" $ do-        let config = "foo 1 2 3"-            decoder = do-              KDL.many $ KDL.arg @Int-        decodeNode "foo" decoder config `shouldBe` Right [1, 2, 3]--      it "fails if argument doesn't exist" $ do-        let config = "foo"-            decoder = do-              KDL.arg @Int-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected arg #0"-            ]--      it "fails if argument fails to parse" $ do-        let config = "foo \"test\""-            decoder = do-              KDL.arg @Int-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected number, got: test"-            ]--      it "fails if not all arguments are decoded" $ do-        let config = "foo 1 2 3"-            decoder = do-              KDL.arg @Int-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected arg #1: 2"-            ]--    -- Most behaviors tested with `arg`-    describe "argWith" $ do-      it "decodes an argument" $ do-        let config = "foo \"bar\""-            decoder = do-              KDL.argWith KDL.text-        decodeNode "foo" decoder config `shouldBe` Right "bar"--    -- Most behaviors tested with `argWith`-    describe "argWith'" $ do-      it "decodes argument with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo (test)a"-              decoder = do-                KDL.argWith' anns KDL.text-          decodeNode "foo" decoder config `shouldBe` Right "a"--      it "decodes argument without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a"-              decoder = do-                KDL.argWith' anns KDL.text-          decodeNode "foo" decoder config `shouldBe` Right "a"--      it "fails when argument has unexpected annotation" $ do-        let config = "foo (test)a"-            decoder = do-              KDL.argWith' ["VAL"] KDL.text-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "prop" $ do-      it "decodes a prop" $ do-        let config = "foo test1=1 test2=\"hello\""-            decoder = do-              prop1 <- KDL.prop @Text "test2"-              prop2 <- KDL.prop @Int "test1"-              pure (prop1, prop2)-        decodeNode "foo" decoder config `shouldBe` Right ("hello", 1)--      it "can optionally decode a prop" $ do-        let config = "foo a=1"-            decoder = do-              a <- KDL.optional $ KDL.prop @Int "a"-              b <- KDL.optional $ KDL.prop @Int "b"-              pure (a, b)-        decodeNode "foo" decoder config `shouldBe` Right (Just 1, Nothing)--      it "decodes last prop" $ do-        let config = "foo test=1 test=2"-            decoder = do-              KDL.prop @Int "test"-        decodeNode "foo" decoder config `shouldBe` Right 2--      it "fails if prop doesn't exist" $ do-        let config = "foo 123"-            decoder = do-              KDL.prop @Int "test"-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Expected prop: test"-            ]--      it "fails if prop fails to parse" $ do-        let config = "foo hello=world"-            decoder = do-              KDL.prop @Int "hello"-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop hello"-            , "  Expected number, got: world"-            ]--      it "fails if not all props are decoded" $ do-        let config = "foo a=1 b=2"-            decoder = do-              KDL.prop @Int "a"-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected prop: b=2"-            ]--    -- Most behaviors tested with `prop`-    describe "propWith" $ do-      it "decodes a prop" $ do-        let config = "foo a=1"-            decoder = do-              KDL.propWith "a" KDL.number-        decodeNode "foo" decoder config `shouldBe` Right 1--    -- Most behaviors tested with `propWith`-    describe "propWith'" $ do-      it "decodes prop with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=(test)1"-              decoder = do-                KDL.propWith' "a" anns KDL.number-          decodeNode "foo" decoder config `shouldBe` Right 1--      it "decodes prop without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=1"-              decoder = do-                KDL.propWith' "a" anns KDL.number-          decodeNode "foo" decoder config `shouldBe` Right 1--      it "fails when prop has unexpected annotation" $ do-        let config = "foo a=(test)1"-            decoder = do-              KDL.propWith' "a" ["VAL"] KDL.number-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop a"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "remainingProps" $ do-      it "decodes remaining props" $ do-        let config = "foo a=1 b=2 c=3 b=4"-            decoder = do-              _ <- KDL.prop @Int "a"-              KDL.remainingProps @Int-        decodeNode "foo" decoder config-          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]--      it "returns empty map if no props left" $ do-        let config = "foo a=1"-            decoder = do-              _ <- KDL.prop @Int "a"-              KDL.remainingProps @Int-        decodeNode "foo" decoder config `shouldBe` Right Map.empty--      it "fails if prop fails to parse" $ do-        let config = "foo a=1 b=1 c=2 c=test"-            decoder = do-              _ <- KDL.prop @Int "a"-              KDL.remainingProps @Int-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop c"-            , "  Expected number, got: test"-            ]--    -- Most behaviors tested with `remainingProps`-    describe "remainingPropsWith" $ do-      it "decodes remaining props" $ do-        let config = "foo a=1 b=2 c=3 b=4"-            decoder = do-              _ <- KDL.prop @Int "a"-              KDL.remainingPropsWith KDL.number-        decodeNode "foo" decoder config-          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]--    -- Most behaviors tested with `remainingPropsWith`-    describe "remainingPropsWith'" $ do-      it "decodes props with an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=(test)1 b=(test)2"-              decoder = do-                KDL.remainingPropsWith' anns KDL.number-          decodeNode "foo" decoder config-            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]--      it "decodes props without an annotation" $ do-        let testCases =-              [ []-              , ["test"]-              , ["test", "other"]-              ]-        forM_ testCases $ \anns -> do-          let config = "foo a=1 b=2"-              decoder = do-                KDL.remainingPropsWith' anns KDL.number-          decodeNode "foo" decoder config-            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]--      it "fails when prop has unexpected annotation" $ do-        let config = "foo a=(VAL)1 b=(test)2"-            decoder = do-              KDL.remainingPropsWith' ["VAL"] KDL.number-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > prop b"-            , "  Expected annotation to be one of [\"VAL\"], got: test"-            ]--    describe "children" $ do-      it "decodes children" $ do-        let config = "foo { bar test; }"-            decoder = do-              KDL.children $ KDL.node @Node "bar"-            expected =-              Node-                { ann = Nothing-                , name = Identifier{value = "bar", format = Nothing}-                , entries =-                    [ Entry-                        { name = Nothing-                        , value = Value{ann = Nothing, data_ = Text "test", format = Nothing}-                        , format = Nothing-                        }-                    ]-                , children = Just NodeList{nodes = [], format = Nothing}-                , format = Nothing-                }-        decodeNode "foo" decoder config `shouldBe` Right expected--      it "can be re-entered" $ do-        let config = "foo { bar a; baz b; }"-            decoder = do-              arg1 <- KDL.children $ KDL.argAt @Text "bar"-              arg2 <- KDL.children $ KDL.argAt @Text "baz"-              pure (arg1, arg2)-        decodeNode "foo" decoder config `shouldBe` Right ("a", "b")--      it "fails if not all children are decoded" $ do-        let config = "foo { asdf; bar; }"-            decoder = do-              KDL.children $ KDL.node @Node "bar"-        decodeNode "foo" decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0"-            , "  Unexpected node: asdf #0"-            ]--  describe "ValueDecoder" $ do-    describe "any" $ do-      it "decodes any value" $ do-        let config = "foo 1.0 asdf true"-            decoder = KDL.document $ do-              KDL.argsAtWith "foo" KDL.any-            val data_ =-              Value-                { ann = Nothing-                , data_ = data_-                , format = Nothing-                }-        KDL.decodeWith decoder config-          `shouldBe` Right [val $ Number 1, val $ Text "asdf", val $ Bool True]--    describe "text" $ do-      it "decodes text value" $ do-        let config = "foo asdf"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.text-        KDL.decodeWith decoder config `shouldBe` Right "asdf"--      it "fails when value is not text" $ do-        let config = "foo 1"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.text-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected text, got: 1"-            ]--    describe "number" $ do-      it "decodes number value" $ do-        let config = "foo 1"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.number-        KDL.decodeWith decoder config `shouldBe` Right 1--      it "fails when value is not number" $ do-        let config = "foo asdf"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.number-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected number, got: asdf"-            ]--    describe "bool" $ do-      it "decodes bool value" $ do-        let config = "foo true"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.bool-        KDL.decodeWith decoder config `shouldBe` Right True--      it "fails when value is not bool" $ do-        let config = "foo 1"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.bool-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected bool, got: 1"-            ]--    describe "null" $ do-      it "decodes null value" $ do-        let config = "foo null"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.null-        KDL.decodeWith decoder config `shouldBe` Right ()--      it "fails when value is not null" $ do-        let config = "foo 1"-            decoder = KDL.document $ do-              KDL.argAtWith "foo" KDL.null-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #0"-            , "  Expected null, got: 1"-            ]--  describe "Combinators" $ do-    describe "oneOf" $ do-      it "decodes one of the options" $ do-        let config = "foo 123 hello"-            decoder = KDL.document $ do-              KDL.nodeWith "foo" . KDL.many . KDL.argWith $-                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.text]-        KDL.decodeWith decoder config `shouldBe` Right [Left 123, Right "hello"]--      it "fails if none can be decoded" $ do-        let config = "foo 123 hello"-            decoder = KDL.document $ do-              KDL.nodeWith "foo" . KDL.many . KDL.argWith $-                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.bool]-        KDL.decodeWith decoder config-          `shouldSatisfy` decodeErrorMsg-            [ "At: foo #0 > arg #1"-            , "  Expected bool, got: hello"-            , "  Expected number, got: hello"-            ]--    describe "option" $ do-      it "defaults to the given value" $ do-        let config = "foo"-            decoder = KDL.document $ do-              KDL.nodeWith "foo" $ KDL.option 123 $ KDL.arg @Int-        KDL.decodeWith decoder config `shouldBe` Right 123--newtype MyNode = MyNode Int-  deriving (Eq)--instance KDL.DecodeNode MyNode where-  validNodeTypeAnns _ = ["MyNode"]-  nodeDecoder = KDL.noSchema $ do-    x <- KDL.arg-    unless (0 < x && x < 10) $ do-      KDL.fail $ "Invalid argument: " <> (Text.pack . show) x-    pure $ MyNode x--decodeNodeSpec :: Spec-decodeNodeSpec = do-  describe "DecodeNode" $ do-    it "decodes a custom node" $ do-      let config = "foo 1"-          decoder = KDL.document $ do-            KDL.node "foo"-      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)--    it "throws user-specified error" $ do-      let config = "foo 100"-          decoder = KDL.document $ do-            KDL.node @MyNode "foo"-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0"-          , "  Invalid argument: 100"-          ]--    it "decodes valid type ann" $ do-      let config = "(MyNode)foo 1"-          decoder = KDL.document $ do-            KDL.node "foo"-      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)--    it "fails on invalid type ann" $ do-      let config = "(bad)foo 1"-          decoder = KDL.document $ do-            KDL.node @MyNode "foo"-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0"-          , "  Expected annotation to be one of [\"MyNode\"], got: bad"-          ]--newtype MyVal = MyVal Double-  deriving (Eq)--instance KDL.DecodeValue MyVal where-  validValueTypeAnns _ = ["MyVal"]-  valueDecoder = KDL.noSchema . KDL.withDecoder KDL.number $ \x -> do-    unless (0 < x && x < 10) $ do-      KDL.failM $ "Invalid value: " <> (Text.pack . show) x-    pure $ MyVal (realToFrac x)--decodeValueSpec :: Spec-decodeValueSpec = do-  describe "DecodeValue" $ do-    it "decodes a custom value" $ do-      let config = "foo 1"-          decoder = KDL.document $ do-            KDL.argAt "foo"-      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)--    it "throws user-specified error" $ do-      let config = "foo 100.0"-          decoder = KDL.document $ do-            KDL.argAt @MyVal "foo"-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0 > arg #0"-          , "  Invalid value: 100.0"-          ]--    it "decodes valid type ann" $ do-      let config = "foo (MyVal)1"-          decoder = KDL.document $ do-            KDL.argAt "foo"-      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)--    it "fails on invalid type ann" $ do-      let config = "foo (bad)1"-          decoder = KDL.document $ do-            KDL.argAt @MyVal "foo"-      KDL.decodeWith decoder config-        `shouldSatisfy` decodeErrorMsg-          [ "At: foo #0 > arg #0"-          , "  Expected annotation to be one of [\"MyVal\"], got: bad"-          ]
− test/Data/KDL/ParserSpec.hs
@@ -1,113 +0,0 @@-{-# LANGUAGE DisambiguateRecordFields #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.KDL.ParserSpec (spec) where--import Data.KDL.Parser-import Data.KDL.Types (-  Entry (..),-  Identifier (..),-  Node (..),-  NodeList (..),-  Value (..),-  ValueData (..),- )-import Data.Text (Text)-import Data.Text qualified as Text-import Skeletest-import Skeletest.Predicate qualified as P-import System.IO.Temp (withSystemTempDirectory)--spec :: Spec-spec = do-  describe "parse" $ do-    it "parses a KDL document" $ do-      let expected =-            newNodeList-              [ newNode-                  "foo"-                  [ newArg $ Number 1.0-                  , newProp "hello" $ Text "world"-                  ]-                  ( Just-                      [ newNode "bar" [] (Just [])-                      ]-                  )-              ]-      parse "foo hello=world 1.0 { bar; }" `shouldBe` Right expected--    it "returns a textual error on parse failure" $ do-      let msg =-            Text.intercalate "\n" $-              [ "1:10:"-              , "  |"-              , "1 | foo hello= 123"-              , "  |          ^^"-              , "unexpected \"= \""-              , "expecting Node Child, Node Space, or Node Terminator"-              ]-      parse "foo hello= 123" `shouldBe` Left msg--  describe "parseFile" $ do-    it "parses a KDL document from a filepath" $ do-      withSystemTempDirectory "" $ \tmpdir -> do-        let file = tmpdir ++ "/test.kdl"-        writeFile file "foo hello=world 1.0 { bar; }"-        let expected =-              newNodeList-                [ newNode-                    "foo"-                    [ newArg $ Number 1.0-                    , newProp "hello" $ Text "world"-                    ]-                    ( Just-                        [ newNode "bar" [] (Just [])-                        ]-                    )-                ]-        parseFile file `shouldSatisfy` P.returns (P.right (P.eq expected))--{----- Helpers -----}--newNodeList :: [Node] -> NodeList-newNodeList nodes =-  NodeList-    { nodes = nodes-    , format = Nothing-    }--newNode :: Text -> [Entry] -> Maybe [Node] -> Node-newNode name entries children =-  Node-    { ann = Nothing-    , name = newIdentifier name-    , entries = entries-    , children = newNodeList <$> children-    , format = Nothing-    }--newArg :: ValueData -> Entry-newArg = newEntry Nothing--newProp :: Text -> ValueData -> Entry-newProp = newEntry . Just--newEntry :: Maybe Text -> ValueData -> Entry-newEntry mName data_ =-  Entry-    { name = newIdentifier <$> mName-    , value =-        Value-          { ann = Nothing-          , data_ = data_-          , format = Nothing-          }-    , format = Nothing-    }--newIdentifier :: Text -> Identifier-newIdentifier value =-  Identifier-    { value = value-    , format = Nothing-    }
+ test/KDL/Decoder/ArrowSpec.hs view
@@ -0,0 +1,1219 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE OverloadedStrings #-}++module KDL.Decoder.ArrowSpec (spec) where++import Control.Arrow (returnA)+import Control.Monad (forM_, unless, when)+import Data.Int (Int64)+import Data.Map qualified as Map+import Data.Proxy (Proxy (..))+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Typeable (typeRep)+import KDL.Arrow qualified as KDL+import KDL.Types (+  Entry (..),+  Identifier (..),+  Node (..),+  NodeList (..),+  Value (..),+  ValueData (..),+ )+import Skeletest+import Skeletest.Predicate qualified as P++decodeErrorMsg :: [Text] -> Predicate IO (Either KDL.DecodeError a)+decodeErrorMsg msgs = P.left (KDL.renderDecodeError P.>>> P.eq msg)+ where+  msg = Text.intercalate "\n" msgs++spec :: Spec+spec = do+  apiSpec+  schemaSpec+  decodeNodeSpec+  decodeValueSpec++apiSpec :: Spec+apiSpec = do+  describe "decodeWith" $ do+    it "fails with helpful error if parsing fails" $ do+      let config = "foo hello= 123"+          decoder = KDL.document $ KDL.node @Node "foo"+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: <root>"+          , "  1:10:"+          , "    |"+          , "  1 | foo hello= 123"+          , "    |          ^^"+          , "  unexpected \"= \""+          , "  expecting Node Child, Node Space, or Node Terminator"+          ]++    it "fails with user-defined error" $ do+      let config = "foo -1"+          decoder =+            KDL.document . KDL.argAtWith "foo" $+              KDL.withDecoder KDL.number $ \x -> do+                when (x < 0) $ do+                  KDL.failM $ "Got negative number: " <> (Text.pack . show) x+                pure x+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0 > arg #0"+          , "  Got negative number: -1.0"+          ]++    it "shows context in deeply nested error" $ do+      let config = "foo; foo { bar { baz; baz; baz; baz a=1; }; }"+          decoder =+            KDL.document+              . (KDL.many . KDL.nodeWith "foo" . KDL.children)+              . (KDL.many . KDL.nodeWith "bar" . KDL.children)+              . (KDL.many . KDL.nodeWith "baz")+              $ KDL.optional (KDL.prop @Text "a")+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #1 > bar #0 > baz #3 > prop a"+          , "  Expected text, got: 1"+          ]++  describe "NodeListDecoder" $ do+    describe "node" $ do+      it "decodes a node" $ do+        let config = "foo 1.0"+            decoder = KDL.document $ proc () -> do+              KDL.node "foo" -< ()+            expected =+              Node+                { ann = Nothing+                , name = Identifier{value = "foo", format = Nothing}+                , entries =+                    [ Entry+                        { name = Nothing+                        , value = Value{ann = Nothing, data_ = Number 1.0, format = Nothing}+                        , format = Nothing+                        }+                    ]+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "decodes multiple nodes" $ do+        let config = "foo; foo"+            decoder = KDL.document $ proc () -> do+              KDL.many $ KDL.node "foo" -< ()+            expected = [fooNode, fooNode]+            fooNode =+              Node+                { ann = Nothing+                , name = Identifier{value = "foo", format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "decodes nodes in any order" $ do+        let config = "foo; bar"+            decoder = KDL.document $ proc () -> do+              bar <- KDL.node "bar" -< ()+              foo <- KDL.node "foo" -< ()+              returnA -< (bar, foo)+            expected = (node "bar", node "foo")+            node name =+              Node+                { ann = Nothing+                , name = Identifier{value = name, format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "fails when not enough nodes" $ do+        let config = "foo"+            decoder = KDL.document $ proc () -> do+              foo1 <- KDL.node @Node "foo" -< ()+              foo2 <- KDL.node @Node "foo" -< ()+              returnA -< (foo1, foo2)+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: <root>"+            , "  Expected another node: foo"+            ]++    -- Most behaviors tested with `node`+    describe "nodeWith" $ do+      it "decodes a node" $ do+        let config = "foo 1.0 { hello \"world\"; }"+            decodeFoo = proc () -> do+              arg <- KDL.arg @Int -< ()+              child <- KDL.children $ KDL.argAt @Text "hello" -< ()+              returnA -< (arg, child)+            decoder = KDL.document $ proc () -> do+              KDL.nodeWith "foo" decodeFoo -< ()+        KDL.decodeWith decoder config `shouldBe` Right (1, "world")++      it "fails when node fails to parse" $ do+        let config = "foo 1.0"+            decodeFoo = proc () -> do+              KDL.arg @Text -< ()+            decoder = KDL.document $ proc () -> do+              KDL.nodeWith "foo" decodeFoo -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected text, got: 1.0"+            ]++    -- Most behaviors tested with `nodeWith`+    describe "nodeWith'" $ do+      it "decodes a node with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "(test)foo 1"+              decoder = KDL.document $ proc () -> do+                KDL.nodeWith' "foo" anns $ KDL.arg @Int -< ()+          KDL.decodeWith decoder config `shouldBe` Right 1++      it "decodes a node without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo 1"+              decoder = KDL.document $ proc () -> do+                KDL.nodeWith' "foo" anns $ KDL.arg @Int -< ()+          KDL.decodeWith decoder config `shouldBe` Right 1++      it "fails when node has unexpected annotation" $ do+        let config = "(test)foo 2"+            decoder = KDL.document $ proc () -> do+              KDL.nodeWith' "foo" ["FOO"] $ KDL.arg @Int -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected annotation to be one of [\"FOO\"], got: test"+            ]++    describe "remainingNodes" $ do+      it "returns all remaining nodes" $ do+        let config = "foo 1.0; foo 2.0; bar"+            decoder = KDL.document $ proc () -> do+              _ <- KDL.node @Node "foo" -< ()+              KDL.remainingNodes -< ()+            expected =+              Map.fromList+                [ ("foo", [fooNode2])+                , ("bar", [barNode])+                ]+            fooNode2 =+              Node+                { ann = Nothing+                , name = Identifier{value = "foo", format = Nothing}+                , entries =+                    [ Entry+                        { name = Nothing+                        , value = Value{ann = Nothing, data_ = Number 2.0, format = Nothing}+                        , format = Nothing+                        }+                    ]+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+            barNode =+              Node+                { ann = Nothing+                , name = Identifier{value = "bar", format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++    -- Most behaviors tested with `remainingNodes`+    describe "remainingNodesWith" $ do+      it "returns all remaining nodes" $ do+        let config = "foo 1.0; foo 2.0; bar"+            decodeNode = proc () -> do+              KDL.optional $ KDL.arg @Int -< ()+            decoder = KDL.document $ proc () -> do+              _ <- KDL.node @Node "foo" -< ()+              KDL.remainingNodesWith decodeNode -< ()+            expected =+              Map.fromList+                [ ("foo", [Just 2])+                , ("bar", [Nothing])+                ]+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "fails when node fails to parse" $ do+        let config = "foo 1; bar 1; bar \"hello\""+            decodeNode = proc () -> do+              KDL.arg @Int -< ()+            decoder = KDL.document $ proc () -> do+              KDL.remainingNodesWith decodeNode -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: bar #1 > arg #0"+            , "  Expected number, got: hello"+            ]++    -- Most behaviors tested with `remainingNodesWith`+    describe "remainingNodesWith'" $ do+      it "decodes a node with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "(test)foo 1"+              decoder = KDL.document $ proc () -> do+                KDL.remainingNodesWith' anns $ KDL.arg @Int -< ()+          KDL.decodeWith decoder config+            `shouldBe` (Right . Map.fromList) [("foo", [1])]++      it "decodes a node without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo 1"+              decoder = KDL.document $ proc () -> do+                KDL.remainingNodesWith' anns $ KDL.arg @Int -< ()+          KDL.decodeWith decoder config+            `shouldBe` (Right . Map.fromList) [("foo", [1])]++      it "fails when node has unexpected annotation" $ do+        let config = "(FOO)foo 1; (test)foo 2"+            decoder = KDL.document $ proc () -> do+              KDL.remainingNodesWith' ["FOO"] $ KDL.arg @Int -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #1"+            , "  Expected annotation to be one of [\"FOO\"], got: test"+            ]++    describe "argAt" $ do+      it "gets argument at a node" $ do+        let config = "foo \"bar\"; hello \"world\""+            decoder = KDL.document $ proc () -> do+              hello <- KDL.argAt @Text "hello" -< ()+              foo <- KDL.argAt @Text "foo" -< ()+              returnA -< (hello, foo)+        KDL.decodeWith decoder config `shouldBe` Right ("world", "bar")++      it "fails if no node" $ do+        let config = "other_node"+            decoder = KDL.document $ proc () -> do+              KDL.argAt @Int "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: <root>"+            , "  Expected node: foo"+            ]++      it "fails if node has no args" $ do+        let config = "foo"+            decoder = KDL.document $ proc () -> do+              KDL.argAt @Int "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected arg #0"+            ]++      it "fails if arg fails to parse" $ do+        let config = "foo 1"+            decoder = KDL.document $ proc () -> do+              KDL.argAt @Text "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected text, got: 1"+            ]++    -- Most behaviors tested with `argAt`+    describe "argAtWith" $ do+      it "gets argument at a node" $ do+        let config = "foo 1"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" $ show . (* 10) <$> KDL.number -< ()+        KDL.decodeWith decoder config `shouldBe` Right "10.0"++    -- Most behaviors tested with `argAtWith`+    describe "argAtWith'" $ do+      it "decodes argument with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo (test)a"+              decoder = KDL.document $ proc () -> do+                KDL.argAtWith' "foo" anns KDL.text -< ()+          KDL.decodeWith decoder config `shouldBe` Right "a"++      it "decodes argument without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a"+              decoder = KDL.document $ proc () -> do+                KDL.argAtWith' "foo" anns KDL.text -< ()+          KDL.decodeWith decoder config `shouldBe` Right "a"++      it "fails when argument has unexpected annotation" $ do+        let config = "foo (test)a"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith' "foo" ["VAL"] KDL.text -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "argsAt" $ do+      it "gets arguments at a node" $ do+        let config = "foo 1 2 3"+            decoder = KDL.document $ proc () -> do+              KDL.argsAt @Int "foo" -< ()+        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]++      it "returns empty list if no node" $ do+        let config = ""+            decoder = KDL.document $ proc () -> do+              KDL.argsAt @Int "foo" -< ()+        KDL.decodeWith decoder config `shouldBe` Right []++      it "returns empty list if node has no args" $ do+        let config = "foo"+            decoder = KDL.document $ proc () -> do+              KDL.argsAt @Int "foo" -< ()+        KDL.decodeWith decoder config `shouldBe` Right []++      it "fails if any arg fails to parse" $ do+        let config = "foo 1 \"asdf\""+            decoder = KDL.document $ proc () -> do+              KDL.argsAt @Int "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #1"+            , "  Expected number, got: asdf"+            ]++    -- Most behaviors tested with `argsAt`+    describe "argsAtWith" $ do+      it "gets arguments at a node" $ do+        let config = "foo 1 2"+            decoder = KDL.document $ proc () -> do+              KDL.argsAtWith "foo" $ show . (* 10) <$> KDL.number -< ()+        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]++    -- Most behaviors tested with `argsAtWith`+    describe "argsAtWith'" $ do+      it "decodes arguments with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo (test)a (test)b"+              decoder = KDL.document $ proc () -> do+                KDL.argsAtWith' "foo" anns KDL.text -< ()+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "decodes arguments without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a b"+              decoder = KDL.document $ proc () -> do+                KDL.argsAtWith' "foo" anns KDL.text -< ()+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "fails when argument has unexpected annotation" $ do+        let config = "foo (VAL)a (test)b"+            decoder = KDL.document $ proc () -> do+              KDL.argsAtWith' "foo" ["VAL"] KDL.text -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #1"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "dashChildrenAt" $ do+      it "gets dash children at a node" $ do+        let config = "foo { - 1; - 2; - 3; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashChildrenAt @Int "foo" -< ()+        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]++      it "returns empty list if no node" $ do+        let config = ""+            decoder = KDL.document $ proc () -> do+              KDL.dashChildrenAt @Int "foo" -< ()+        KDL.decodeWith decoder config `shouldBe` Right []++      it "returns empty list if node has no dash children" $ do+        forM_ ["foo", "foo {}"] $ \config -> do+          let decoder = KDL.document $ proc () -> do+                KDL.dashChildrenAt @Int "foo" -< ()+          KDL.decodeWith decoder config `shouldBe` Right []++      it "fails if dash children have multiple args" $ do+        let config = "foo { - 1 2; - 3 4; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashChildrenAt @Int "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #0"+            , "  Unexpected arg #1: 2"+            ]++      it "fails if node has non-dash children" $ do+        let config = "foo { - 1; bar 1 2 3; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashChildrenAt @Int "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected node: bar #0"+            ]++      it "fails if any child fails to parse" $ do+        let config = "foo { - 1; - \"asdf\"; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashChildrenAt @Int "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #1 > arg #0"+            , "  Expected number, got: asdf"+            ]++    -- Most behaviors tested with `dashChildrenAt`+    describe "dashChildrenAtWith" $ do+      it "gets dash children at a node" $ do+        let config = "foo { - 1; - 2; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashChildrenAtWith "foo" $ show . (* 10) <$> KDL.number -< ()+        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]++    -- Most behaviors tested with `dashChildrenAtWith`+    describe "dashChildrenAtWith'" $ do+      it "decodes dash children with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo { - (test)a; - (test)b; }"+              decoder = KDL.document $ proc () -> do+                KDL.dashChildrenAtWith' "foo" anns KDL.text -< ()+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "decodes dash children without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo { - a; - b; }"+              decoder = KDL.document $ proc () -> do+                KDL.dashChildrenAtWith' "foo" anns KDL.text -< ()+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "fails when child has unexpected annotation" $ do+        let config = "foo { - (test)a; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashChildrenAtWith' "foo" ["VAL"] KDL.text -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #0 > arg #0"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "dashNodesAt" $ do+      it "gets dash nodes at a node" $ do+        let config = "foo { - { bar; }; - { baz; }; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashNodesAt "foo" -< ()+            expected = [node "-" [node "bar" []], node "-" [node "baz" []]]+            node name children =+              Node+                { ann = Nothing+                , name = Identifier{value = name, format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = children, format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "returns empty list if no node" $ do+        let config = ""+            decoder = KDL.document $ proc () -> do+              KDL.dashNodesAt @Node "foo" -< ()+        KDL.decodeWith decoder config `shouldBe` Right []++      it "returns empty list if node has no dash nodes" $ do+        forM_ ["foo", "foo {}"] $ \config -> do+          let decoder = KDL.document $ proc () -> do+                KDL.dashNodesAt @Node "foo" -< ()+          KDL.decodeWith decoder config `shouldBe` Right []++      it "fails if node has non-dash nodes" $ do+        let config = "foo { - 1; bar 1 2 3; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashNodesAt @Node "foo" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected node: bar #0"+            ]++    -- Most behaviors tested with `dashNodesAt`+    describe "dashNodesAtWith" $ do+      it "gets dash nodes at a node" $ do+        let config = "foo { - 1 { bar \"hello\"; }; - 2 { bar \"world\"; }; }"+            decodeChild = proc () -> do+              arg <- KDL.arg @Int -< ()+              child <- KDL.children $ KDL.nodeWith "bar" $ KDL.arg @Text -< ()+              returnA -< (arg, child)+            decoder = KDL.document $ proc () -> do+              KDL.dashNodesAtWith "foo" decodeChild -< ()+        KDL.decodeWith decoder config `shouldBe` Right [(1, "hello"), (2, "world")]++      it "fails if any child fails to parse" $ do+        let config = "foo { - { bar 1; }; - { bar \"test\"; }; }"+            decoder = KDL.document $ proc () -> do+              KDL.dashNodesAtWith "foo" $ KDL.children $ KDL.argAt @Int "bar" -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #1 > bar #0 > arg #0"+            , "  Expected number, got: test"+            ]++  describe "NodeDecoder" $ do+    let decodeNode name decoder config =+          KDL.decodeWith+            ( KDL.document $ proc () -> do+                KDL.nodeWith name decoder -< ()+            )+            config++    describe "arg" $ do+      it "decodes an argument" $ do+        let config = "foo 1 \"bar\""+            decoder = proc () -> do+              arg1 <- KDL.arg @Int -< ()+              arg2 <- KDL.arg @Text -< ()+              returnA -< (arg1, arg2)+        decodeNode "foo" decoder config `shouldBe` Right (1, "bar")++      it "decodes multiple arguments" $ do+        let config = "foo 1 2 3"+            decoder = proc () -> do+              KDL.many $ KDL.arg @Int -< ()+        decodeNode "foo" decoder config `shouldBe` Right [1, 2, 3]++      it "fails if argument doesn't exist" $ do+        let config = "foo"+            decoder = proc () -> do+              KDL.arg @Int -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected arg #0"+            ]++      it "fails if argument fails to parse" $ do+        let config = "foo \"test\""+            decoder = proc () -> do+              KDL.arg @Int -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected number, got: test"+            ]++      it "fails if not all arguments are decoded" $ do+        let config = "foo 1 2 3"+            decoder = proc () -> do+              KDL.arg @Int -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected arg #1: 2"+            ]++    -- Most behaviors tested with `arg`+    describe "argWith" $ do+      it "decodes an argument" $ do+        let config = "foo \"bar\""+            decoder = proc () -> do+              KDL.argWith KDL.text -< ()+        decodeNode "foo" decoder config `shouldBe` Right "bar"++    -- Most behaviors tested with `argWith`+    describe "argWith'" $ do+      it "decodes argument with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo (test)a"+              decoder = proc () -> do+                KDL.argWith' anns KDL.text -< ()+          decodeNode "foo" decoder config `shouldBe` Right "a"++      it "decodes argument without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a"+              decoder = proc () -> do+                KDL.argWith' anns KDL.text -< ()+          decodeNode "foo" decoder config `shouldBe` Right "a"++      it "fails when argument has unexpected annotation" $ do+        let config = "foo (test)a"+            decoder = proc () -> do+              KDL.argWith' ["VAL"] KDL.text -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "prop" $ do+      it "decodes a prop" $ do+        let config = "foo test1=1 test2=\"hello\""+            decoder = proc () -> do+              prop1 <- KDL.prop @Text "test2" -< ()+              prop2 <- KDL.prop @Int "test1" -< ()+              returnA -< (prop1, prop2)+        decodeNode "foo" decoder config `shouldBe` Right ("hello", 1)++      it "can optionally decode a prop" $ do+        let config = "foo a=1"+            decoder = proc () -> do+              a <- KDL.optional $ KDL.prop @Int "a" -< ()+              b <- KDL.optional $ KDL.prop @Int "b" -< ()+              returnA -< (a, b)+        decodeNode "foo" decoder config `shouldBe` Right (Just 1, Nothing)++      it "decodes last prop" $ do+        let config = "foo test=1 test=2"+            decoder = proc () -> do+              KDL.prop @Int "test" -< ()+        decodeNode "foo" decoder config `shouldBe` Right 2++      it "fails if prop doesn't exist" $ do+        let config = "foo 123"+            decoder = proc () -> do+              KDL.prop @Int "test" -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected prop: test"+            ]++      it "fails if prop fails to parse" $ do+        let config = "foo hello=world"+            decoder = proc () -> do+              KDL.prop @Int "hello" -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop hello"+            , "  Expected number, got: world"+            ]++      it "fails if not all props are decoded" $ do+        let config = "foo a=1 b=2"+            decoder = proc () -> do+              KDL.prop @Int "a" -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected prop: b=2"+            ]++    -- Most behaviors tested with `prop`+    describe "propWith" $ do+      it "decodes a prop" $ do+        let config = "foo a=1"+            decoder = proc () -> do+              KDL.propWith "a" KDL.number -< ()+        decodeNode "foo" decoder config `shouldBe` Right 1++    -- Most behaviors tested with `propWith`+    describe "propWith'" $ do+      it "decodes prop with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=(test)1"+              decoder = proc () -> do+                KDL.propWith' "a" anns KDL.number -< ()+          decodeNode "foo" decoder config `shouldBe` Right 1++      it "decodes prop without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=1"+              decoder = proc () -> do+                KDL.propWith' "a" anns KDL.number -< ()+          decodeNode "foo" decoder config `shouldBe` Right 1++      it "fails when prop has unexpected annotation" $ do+        let config = "foo a=(test)1"+            decoder = proc () -> do+              KDL.propWith' "a" ["VAL"] KDL.number -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop a"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "remainingProps" $ do+      it "decodes remaining props" $ do+        let config = "foo a=1 b=2 c=3 b=4"+            decoder = proc () -> do+              _ <- KDL.prop @Int "a" -< ()+              KDL.remainingProps @Int -< ()+        decodeNode "foo" decoder config+          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]++      it "returns empty map if no props left" $ do+        let config = "foo a=1"+            decoder = proc () -> do+              _ <- KDL.prop @Int "a" -< ()+              KDL.remainingProps @Int -< ()+        decodeNode "foo" decoder config `shouldBe` Right Map.empty++      it "fails if prop fails to parse" $ do+        let config = "foo a=1 b=1 c=2 c=test"+            decoder = proc () -> do+              _ <- KDL.prop @Int "a" -< ()+              KDL.remainingProps @Int -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop c"+            , "  Expected number, got: test"+            ]++    -- Most behaviors tested with `remainingProps`+    describe "remainingPropsWith" $ do+      it "decodes remaining props" $ do+        let config = "foo a=1 b=2 c=3 b=4"+            decoder = proc () -> do+              _ <- KDL.prop @Int "a" -< ()+              KDL.remainingPropsWith KDL.number -< ()+        decodeNode "foo" decoder config+          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]++    -- Most behaviors tested with `remainingPropsWith`+    describe "remainingPropsWith'" $ do+      it "decodes props with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=(test)1 b=(test)2"+              decoder = proc () -> do+                KDL.remainingPropsWith' anns KDL.number -< ()+          decodeNode "foo" decoder config+            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]++      it "decodes props without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=1 b=2"+              decoder = proc () -> do+                KDL.remainingPropsWith' anns KDL.number -< ()+          decodeNode "foo" decoder config+            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]++      it "fails when prop has unexpected annotation" $ do+        let config = "foo a=(VAL)1 b=(test)2"+            decoder = proc () -> do+              KDL.remainingPropsWith' ["VAL"] KDL.number -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop b"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "children" $ do+      it "decodes children" $ do+        let config = "foo { bar test; }"+            decoder = proc () -> do+              KDL.children $ KDL.node @Node "bar" -< ()+            expected =+              Node+                { ann = Nothing+                , name = Identifier{value = "bar", format = Nothing}+                , entries =+                    [ Entry+                        { name = Nothing+                        , value = Value{ann = Nothing, data_ = Text "test", format = Nothing}+                        , format = Nothing+                        }+                    ]+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        decodeNode "foo" decoder config `shouldBe` Right expected++      it "can be re-entered" $ do+        let config = "foo { bar a; baz b; }"+            decoder = proc () -> do+              arg1 <- KDL.children $ KDL.argAt @Text "bar" -< ()+              arg2 <- KDL.children $ KDL.argAt @Text "baz" -< ()+              returnA -< (arg1, arg2)+        decodeNode "foo" decoder config `shouldBe` Right ("a", "b")++      it "fails if not all children are decoded" $ do+        let config = "foo { asdf; bar; }"+            decoder = proc () -> do+              KDL.children $ KDL.node @Node "bar" -< ()+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected node: asdf #0"+            ]++  describe "ValueDecoder" $ do+    describe "any" $ do+      it "decodes any value" $ do+        let config = "foo 1.0 asdf #true"+            decoder = KDL.document $ proc () -> do+              KDL.argsAtWith "foo" KDL.any -< ()+            val data_ =+              Value+                { ann = Nothing+                , data_ = data_+                , format = Nothing+                }+        KDL.decodeWith decoder config+          `shouldBe` Right [val $ Number 1, val $ Text "asdf", val $ Bool True]++    describe "text" $ do+      it "decodes text value" $ do+        let config = "foo asdf"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.text -< ()+        KDL.decodeWith decoder config `shouldBe` Right "asdf"++      it "fails when value is not text" $ do+        let config = "foo 1"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.text -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected text, got: 1"+            ]++    describe "number" $ do+      it "decodes number value" $ do+        let config = "foo 1"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.number -< ()+        KDL.decodeWith decoder config `shouldBe` Right 1++      it "fails when value is not number" $ do+        let config = "foo asdf"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.number -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected number, got: asdf"+            ]++    describe "bool" $ do+      it "decodes bool value" $ do+        let config = "foo #true"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.bool -< ()+        KDL.decodeWith decoder config `shouldBe` Right True++      it "fails when value is not bool" $ do+        let config = "foo 1"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.bool -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected bool, got: 1"+            ]++    describe "null" $ do+      it "decodes null value" $ do+        let config = "foo #null"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.null -< ()+        KDL.decodeWith decoder config `shouldBe` Right ()++      it "fails when value is not null" $ do+        let config = "foo 1"+            decoder = KDL.document $ proc () -> do+              KDL.argAtWith "foo" KDL.null -< ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected null, got: 1"+            ]++  describe "Combinators" $ do+    describe "oneOf" $ do+      it "decodes one of the options" $ do+        let config = "foo 123 hello"+            decoder = KDL.document $ proc () -> do+              KDL.nodeWith "foo" . KDL.many . KDL.argWith $+                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.text]+                -<+                  ()+        KDL.decodeWith decoder config `shouldBe` Right [Left 123, Right "hello"]++      it "fails if none can be decoded" $ do+        let config = "foo 123 hello"+            decoder = KDL.document $ proc () -> do+              KDL.nodeWith "foo" . KDL.many . KDL.argWith $+                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.bool]+                -<+                  ()+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #1"+            , "  Expected bool, got: hello"+            , "  Expected number, got: hello"+            ]++    describe "option" $ do+      it "defaults to the given value" $ do+        let config = "foo"+            decoder = KDL.document $ proc () -> do+              KDL.nodeWith "foo" $ KDL.option 123 $ KDL.arg @Int -< ()+        KDL.decodeWith decoder config `shouldBe` Right 123++schemaSpec :: Spec+schemaSpec = do+  describe "documentSchema" $ do+    it "gets the schema of a decoder" $ do+      let decoder = KDL.document $ proc () -> do+            x <- KDL.nodeWith "foo" $ show <$> KDL.argWith decodeFoo -< ()+            ys <- KDL.many $ KDL.nodeWith "bar" $ KDL.arg @String -< ()++            zType <- KDL.argAt @Text "baz_type" -< ()+            z <- KDL.argAtWith "baz" decodeBaz -< zType++            returnA -< (x, ys, z)+          decodeFoo =+            KDL.oneOf+              [ Left <$> KDL.valueDecoder @Bool+              , Right <$> KDL.valueDecoder @Text+              ]+          decodeBaz = proc zType -> do+            case zType of+              "int" -> KDL.valueDecoder @Int64 -< ()+              "bool" -> (\b -> if b then 1 else 0) <$> KDL.valueDecoder @Bool -< ()+              _ -> KDL.fail -< "Invalid type: " <> zType+          expected =+            KDL.SchemaAnd+              [ KDL.SchemaOne . KDL.NodeNamed "foo" $+                  KDL.TypedNodeSchema+                    { typeHint = typeRep $ Proxy @String+                    , validTypeAnns = []+                    , nodeSchema =+                        KDL.SchemaOne . KDL.NodeArg $+                          KDL.TypedValueSchema+                            { typeHint = typeRep $ Proxy @(Either Bool Text)+                            , validTypeAnns = []+                            , dataSchema =+                                KDL.SchemaOr+                                  [ KDL.SchemaOne KDL.BoolSchema+                                  , KDL.SchemaOne KDL.TextSchema+                                  ]+                            }+                    }+              , KDL.SchemaOr+                  [ KDL.SchemaSome . KDL.SchemaOne . KDL.NodeNamed "bar" $+                      KDL.TypedNodeSchema+                        { typeHint = typeRep $ Proxy @String+                        , validTypeAnns = []+                        , nodeSchema =+                            KDL.SchemaOne . KDL.NodeArg $+                              KDL.TypedValueSchema+                                { typeHint = typeRep $ Proxy @String+                                , validTypeAnns = ["string"]+                                , dataSchema = KDL.SchemaOne KDL.TextSchema+                                }+                        }+                  , KDL.SchemaAnd []+                  ]+              , KDL.SchemaOne . KDL.NodeNamed "baz_type" $+                  KDL.TypedNodeSchema+                    { typeHint = typeRep $ Proxy @Text+                    , validTypeAnns = []+                    , nodeSchema =+                        KDL.SchemaOne . KDL.NodeArg $+                          KDL.TypedValueSchema+                            { typeHint = typeRep $ Proxy @Text+                            , validTypeAnns = ["text"]+                            , dataSchema = KDL.SchemaOne KDL.TextSchema+                            }+                    }+              , KDL.SchemaOne . KDL.NodeNamed "baz" $+                  KDL.TypedNodeSchema+                    { typeHint = typeRep $ Proxy @Int64+                    , validTypeAnns = []+                    , nodeSchema =+                        KDL.SchemaOne . KDL.NodeArg $+                          KDL.TypedValueSchema+                            { typeHint = typeRep $ Proxy @Int64+                            , validTypeAnns = []+                            , dataSchema =+                                KDL.SchemaOr+                                  [ KDL.SchemaOne KDL.NumberSchema+                                  , KDL.SchemaOne KDL.BoolSchema+                                  ]+                            }+                    }+              ]+      KDL.documentSchema decoder `shouldBe` expected++newtype MyNode = MyNode Int+  deriving (Eq)++instance KDL.DecodeNode MyNode where+  validNodeTypeAnns _ = ["MyNode"]+  nodeDecoder = proc () -> do+    x <- KDL.arg -< ()+    if not (0 < x && x < 10)+      then KDL.fail -< "Invalid argument: " <> (Text.pack . show) x+      else returnA -< ()+    returnA -< MyNode x++decodeNodeSpec :: Spec+decodeNodeSpec = do+  describe "DecodeNode" $ do+    it "decodes a custom node" $ do+      let config = "foo 1"+          decoder = KDL.document $ proc () -> do+            KDL.node "foo" -< ()+      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)++    it "throws user-specified error" $ do+      let config = "foo 100"+          decoder = KDL.document $ proc () -> do+            KDL.node @MyNode "foo" -< ()+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0"+          , "  Invalid argument: 100"+          ]++    it "decodes valid type ann" $ do+      let config = "(MyNode)foo 1"+          decoder = KDL.document $ proc () -> do+            KDL.node "foo" -< ()+      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)++    it "fails on invalid type ann" $ do+      let config = "(bad)foo 1"+          decoder = KDL.document $ proc () -> do+            KDL.node @MyNode "foo" -< ()+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0"+          , "  Expected annotation to be one of [\"MyNode\"], got: bad"+          ]++newtype MyVal = MyVal Double+  deriving (Eq)++instance KDL.DecodeValue MyVal where+  validValueTypeAnns _ = ["MyVal"]+  valueDecoder = KDL.withDecoder KDL.number $ \x -> do+    unless (0 < x && x < 10) $ do+      KDL.failM $ "Invalid value: " <> (Text.pack . show) x+    pure $ MyVal (realToFrac x)++decodeValueSpec :: Spec+decodeValueSpec = do+  describe "DecodeValue" $ do+    it "decodes a custom value" $ do+      let config = "foo 1"+          decoder = KDL.document $ proc () -> do+            KDL.argAt "foo" -< ()+      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)++    it "throws user-specified error" $ do+      let config = "foo 100.0"+          decoder = KDL.document $ proc () -> do+            KDL.argAt @MyVal "foo" -< ()+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0 > arg #0"+          , "  Invalid value: 100.0"+          ]++    it "decodes valid type ann" $ do+      let config = "foo (MyVal)1"+          decoder = KDL.document $ proc () -> do+            KDL.argAt "foo" -< ()+      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)++    it "fails on invalid type ann" $ do+      let config = "foo (bad)1"+          decoder = KDL.document $ proc () -> do+            KDL.argAt @MyVal "foo" -< ()+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0 > arg #0"+          , "  Expected annotation to be one of [\"MyVal\"], got: bad"+          ]
+ test/KDL/Decoder/MonadSpec.hs view
@@ -0,0 +1,1122 @@+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE OverloadedStrings #-}++module KDL.Decoder.MonadSpec (spec) where++import Control.Monad (forM_, unless, when)+import Data.Map qualified as Map+import Data.Text (Text)+import Data.Text qualified as Text+import KDL qualified+import KDL.Types (+  Entry (..),+  Identifier (..),+  Node (..),+  NodeList (..),+  Value (..),+  ValueData (..),+ )+import Skeletest+import Skeletest.Predicate qualified as P++decodeErrorMsg :: [Text] -> Predicate IO (Either KDL.DecodeError a)+decodeErrorMsg msgs = P.left (KDL.renderDecodeError P.>>> P.eq msg)+ where+  msg = Text.intercalate "\n" msgs++spec :: Spec+spec = do+  apiSpec+  decodeNodeSpec+  decodeValueSpec++apiSpec :: Spec+apiSpec = do+  describe "decodeWith" $ do+    it "fails with helpful error if parsing fails" $ do+      let config = "foo hello= 123"+          decoder = KDL.document $ KDL.node @Node "foo"+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: <root>"+          , "  1:10:"+          , "    |"+          , "  1 | foo hello= 123"+          , "    |          ^^"+          , "  unexpected \"= \""+          , "  expecting Node Child, Node Space, or Node Terminator"+          ]++    it "fails with user-defined error" $ do+      let config = "foo -1"+          decoder =+            KDL.document . KDL.argAtWith "foo" $+              KDL.withDecoder KDL.number $ \x -> do+                when (x < 0) $ do+                  KDL.failM $ "Got negative number: " <> (Text.pack . show) x+                pure x+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0 > arg #0"+          , "  Got negative number: -1.0"+          ]++    it "shows context in deeply nested error" $ do+      let config = "foo; foo { bar { baz; baz; baz; baz a=1; }; }"+          decoder =+            KDL.document+              . (KDL.many . KDL.nodeWith "foo" . KDL.children)+              . (KDL.many . KDL.nodeWith "bar" . KDL.children)+              . (KDL.many . KDL.nodeWith "baz")+              $ KDL.optional (KDL.prop @Text "a")+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #1 > bar #0 > baz #3 > prop a"+          , "  Expected text, got: 1"+          ]++  describe "NodeListDecoder" $ do+    describe "node" $ do+      it "decodes a node" $ do+        let config = "foo 1.0"+            decoder = KDL.document $ do+              KDL.node "foo"+            expected =+              Node+                { ann = Nothing+                , name = Identifier{value = "foo", format = Nothing}+                , entries =+                    [ Entry+                        { name = Nothing+                        , value = Value{ann = Nothing, data_ = Number 1.0, format = Nothing}+                        , format = Nothing+                        }+                    ]+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "decodes multiple nodes" $ do+        let config = "foo; foo"+            decoder = KDL.document $ do+              KDL.many $ KDL.node "foo"+            expected = [fooNode, fooNode]+            fooNode =+              Node+                { ann = Nothing+                , name = Identifier{value = "foo", format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "decodes nodes in any order" $ do+        let config = "foo; bar"+            decoder = KDL.document $ do+              bar <- KDL.node "bar"+              foo <- KDL.node "foo"+              pure (bar, foo)+            expected = (node "bar", node "foo")+            node name =+              Node+                { ann = Nothing+                , name = Identifier{value = name, format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "fails when not enough nodes" $ do+        let config = "foo"+            decoder = KDL.document $ do+              foo1 <- KDL.node @Node "foo"+              foo2 <- KDL.node @Node "foo"+              pure (foo1, foo2)+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: <root>"+            , "  Expected another node: foo"+            ]++    -- Most behaviors tested with `node`+    describe "nodeWith" $ do+      it "decodes a node" $ do+        let config = "foo 1.0 { hello \"world\"; }"+            decodeFoo = do+              arg <- KDL.arg @Int+              child <- KDL.children $ KDL.argAt @Text "hello"+              pure (arg, child)+            decoder = KDL.document $ do+              KDL.nodeWith "foo" decodeFoo+        KDL.decodeWith decoder config `shouldBe` Right (1, "world")++      it "fails when node fails to parse" $ do+        let config = "foo 1.0"+            decodeFoo = do+              KDL.arg @Text+            decoder = KDL.document $ do+              KDL.nodeWith "foo" decodeFoo+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected text, got: 1.0"+            ]++    -- Most behaviors tested with `nodeWith`+    describe "nodeWith'" $ do+      it "decodes a node with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "(test)foo 1"+              decoder = KDL.document $ do+                KDL.nodeWith' "foo" anns $ KDL.arg @Int+          KDL.decodeWith decoder config `shouldBe` Right 1++      it "decodes a node without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo 1"+              decoder = KDL.document $ do+                KDL.nodeWith' "foo" anns $ KDL.arg @Int+          KDL.decodeWith decoder config `shouldBe` Right 1++      it "fails when node has unexpected annotation" $ do+        let config = "(test)foo 2"+            decoder = KDL.document $ do+              KDL.nodeWith' "foo" ["FOO"] $ KDL.arg @Int+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected annotation to be one of [\"FOO\"], got: test"+            ]++    describe "remainingNodes" $ do+      it "returns all remaining nodes" $ do+        let config = "foo 1.0; foo 2.0; bar"+            decoder = KDL.document $ do+              _ <- KDL.node @Node "foo"+              KDL.remainingNodes+            expected =+              Map.fromList+                [ ("foo", [fooNode2])+                , ("bar", [barNode])+                ]+            fooNode2 =+              Node+                { ann = Nothing+                , name = Identifier{value = "foo", format = Nothing}+                , entries =+                    [ Entry+                        { name = Nothing+                        , value = Value{ann = Nothing, data_ = Number 2.0, format = Nothing}+                        , format = Nothing+                        }+                    ]+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+            barNode =+              Node+                { ann = Nothing+                , name = Identifier{value = "bar", format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++    -- Most behaviors tested with `remainingNodes`+    describe "remainingNodesWith" $ do+      it "returns all remaining nodes" $ do+        let config = "foo 1.0; foo 2.0; bar"+            decodeNode = do+              KDL.optional $ KDL.arg @Int+            decoder = KDL.document $ do+              _ <- KDL.node @Node "foo"+              KDL.remainingNodesWith decodeNode+            expected =+              Map.fromList+                [ ("foo", [Just 2])+                , ("bar", [Nothing])+                ]+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "fails when node fails to parse" $ do+        let config = "foo 1; bar 1; bar \"hello\""+            decodeNode = do+              KDL.arg @Int+            decoder = KDL.document $ do+              KDL.remainingNodesWith decodeNode+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: bar #1 > arg #0"+            , "  Expected number, got: hello"+            ]++    -- Most behaviors tested with `remainingNodesWith`+    describe "remainingNodesWith'" $ do+      it "decodes a node with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "(test)foo 1"+              decoder = KDL.document $ do+                KDL.remainingNodesWith' anns $ KDL.arg @Int+          KDL.decodeWith decoder config+            `shouldBe` (Right . Map.fromList) [("foo", [1])]++      it "decodes a node without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo 1"+              decoder = KDL.document $ do+                KDL.remainingNodesWith' anns $ KDL.arg @Int+          KDL.decodeWith decoder config+            `shouldBe` (Right . Map.fromList) [("foo", [1])]++      it "fails when node has unexpected annotation" $ do+        let config = "(FOO)foo 1; (test)foo 2"+            decoder = KDL.document $ do+              KDL.remainingNodesWith' ["FOO"] $ KDL.arg @Int+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #1"+            , "  Expected annotation to be one of [\"FOO\"], got: test"+            ]++    describe "argAt" $ do+      it "gets argument at a node" $ do+        let config = "foo \"bar\"; hello \"world\""+            decoder = KDL.document $ do+              hello <- KDL.argAt @Text "hello"+              foo <- KDL.argAt @Text "foo"+              pure (hello, foo)+        KDL.decodeWith decoder config `shouldBe` Right ("world", "bar")++      it "fails if no node" $ do+        let config = "other_node"+            decoder = KDL.document $ do+              KDL.argAt @Int "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: <root>"+            , "  Expected node: foo"+            ]++      it "fails if node has no args" $ do+        let config = "foo"+            decoder = KDL.document $ do+              KDL.argAt @Int "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected arg #0"+            ]++      it "fails if arg fails to parse" $ do+        let config = "foo 1"+            decoder = KDL.document $ do+              KDL.argAt @Text "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected text, got: 1"+            ]++    -- Most behaviors tested with `argAt`+    describe "argAtWith" $ do+      it "gets argument at a node" $ do+        let config = "foo 1"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" $ show . (* 10) <$> KDL.number+        KDL.decodeWith decoder config `shouldBe` Right "10.0"++    -- Most behaviors tested with `argAtWith`+    describe "argAtWith'" $ do+      it "decodes argument with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo (test)a"+              decoder = KDL.document $ do+                KDL.argAtWith' "foo" anns KDL.text+          KDL.decodeWith decoder config `shouldBe` Right "a"++      it "decodes argument without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a"+              decoder = KDL.document $ do+                KDL.argAtWith' "foo" anns KDL.text+          KDL.decodeWith decoder config `shouldBe` Right "a"++      it "fails when argument has unexpected annotation" $ do+        let config = "foo (test)a"+            decoder = KDL.document $ do+              KDL.argAtWith' "foo" ["VAL"] KDL.text+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "argsAt" $ do+      it "gets arguments at a node" $ do+        let config = "foo 1 2 3"+            decoder = KDL.document $ do+              KDL.argsAt @Int "foo"+        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]++      it "returns empty list if no node" $ do+        let config = ""+            decoder = KDL.document $ do+              KDL.argsAt @Int "foo"+        KDL.decodeWith decoder config `shouldBe` Right []++      it "returns empty list if node has no args" $ do+        let config = "foo"+            decoder = KDL.document $ do+              KDL.argsAt @Int "foo"+        KDL.decodeWith decoder config `shouldBe` Right []++      it "fails if any arg fails to parse" $ do+        let config = "foo 1 \"asdf\""+            decoder = KDL.document $ do+              KDL.argsAt @Int "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #1"+            , "  Expected number, got: asdf"+            ]++    -- Most behaviors tested with `argsAt`+    describe "argsAtWith" $ do+      it "gets arguments at a node" $ do+        let config = "foo 1 2"+            decoder = KDL.document $ do+              KDL.argsAtWith "foo" $ show . (* 10) <$> KDL.number+        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]++    -- Most behaviors tested with `argsAtWith`+    describe "argsAtWith'" $ do+      it "decodes arguments with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo (test)a (test)b"+              decoder = KDL.document $ do+                KDL.argsAtWith' "foo" anns KDL.text+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "decodes arguments without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a b"+              decoder = KDL.document $ do+                KDL.argsAtWith' "foo" anns KDL.text+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "fails when argument has unexpected annotation" $ do+        let config = "foo (VAL)a (test)b"+            decoder = KDL.document $ do+              KDL.argsAtWith' "foo" ["VAL"] KDL.text+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #1"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "dashChildrenAt" $ do+      it "gets dash children at a node" $ do+        let config = "foo { - 1; - 2; - 3; }"+            decoder = KDL.document $ do+              KDL.dashChildrenAt @Int "foo"+        KDL.decodeWith decoder config `shouldBe` Right [1, 2, 3]++      it "returns empty list if no node" $ do+        let config = ""+            decoder = KDL.document $ do+              KDL.dashChildrenAt @Int "foo"+        KDL.decodeWith decoder config `shouldBe` Right []++      it "returns empty list if node has no dash children" $ do+        forM_ ["foo", "foo {}"] $ \config -> do+          let decoder = KDL.document $ do+                KDL.dashChildrenAt @Int "foo"+          KDL.decodeWith decoder config `shouldBe` Right []++      it "fails if dash children have multiple args" $ do+        let config = "foo { - 1 2; - 3 4; }"+            decoder = KDL.document $ do+              KDL.dashChildrenAt @Int "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #0"+            , "  Unexpected arg #1: 2"+            ]++      it "fails if node has non-dash children" $ do+        let config = "foo { - 1; bar 1 2 3; }"+            decoder = KDL.document $ do+              KDL.dashChildrenAt @Int "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected node: bar #0"+            ]++      it "fails if any child fails to parse" $ do+        let config = "foo { - 1; - \"asdf\"; }"+            decoder = KDL.document $ do+              KDL.dashChildrenAt @Int "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #1 > arg #0"+            , "  Expected number, got: asdf"+            ]++    -- Most behaviors tested with `dashChildrenAt`+    describe "dashChildrenAtWith" $ do+      it "gets dash children at a node" $ do+        let config = "foo { - 1; - 2; }"+            decoder = KDL.document $ do+              KDL.dashChildrenAtWith "foo" $ show . (* 10) <$> KDL.number+        KDL.decodeWith decoder config `shouldBe` Right ["10.0", "20.0"]++    -- Most behaviors tested with `dashChildrenAtWith`+    describe "dashChildrenAtWith'" $ do+      it "decodes dash children with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo { - (test)a; - (test)b; }"+              decoder = KDL.document $ do+                KDL.dashChildrenAtWith' "foo" anns KDL.text+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "decodes dash children without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo { - a; - b; }"+              decoder = KDL.document $ do+                KDL.dashChildrenAtWith' "foo" anns KDL.text+          KDL.decodeWith decoder config `shouldBe` Right ["a", "b"]++      it "fails when child has unexpected annotation" $ do+        let config = "foo { - (test)a; }"+            decoder = KDL.document $ do+              KDL.dashChildrenAtWith' "foo" ["VAL"] KDL.text+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #0 > arg #0"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "dashNodesAt" $ do+      it "gets dash nodes at a node" $ do+        let config = "foo { - { bar; }; - { baz; }; }"+            decoder = KDL.document $ do+              KDL.dashNodesAt "foo"+            expected = [node "-" [node "bar" []], node "-" [node "baz" []]]+            node name children =+              Node+                { ann = Nothing+                , name = Identifier{value = name, format = Nothing}+                , entries = []+                , children = Just NodeList{nodes = children, format = Nothing}+                , format = Nothing+                }+        KDL.decodeWith decoder config `shouldBe` Right expected++      it "returns empty list if no node" $ do+        let config = ""+            decoder = KDL.document $ do+              KDL.dashNodesAt @Node "foo"+        KDL.decodeWith decoder config `shouldBe` Right []++      it "returns empty list if node has no dash nodes" $ do+        forM_ ["foo", "foo {}"] $ \config -> do+          let decoder = KDL.document $ do+                KDL.dashNodesAt @Node "foo"+          KDL.decodeWith decoder config `shouldBe` Right []++      it "fails if node has non-dash nodes" $ do+        let config = "foo { - 1; bar 1 2 3; }"+            decoder = KDL.document $ do+              KDL.dashNodesAt @Node "foo"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected node: bar #0"+            ]++    -- Most behaviors tested with `dashNodesAt`+    describe "dashNodesAtWith" $ do+      it "gets dash nodes at a node" $ do+        let config = "foo { - 1 { bar \"hello\"; }; - 2 { bar \"world\"; }; }"+            decodeChild = do+              arg <- KDL.arg @Int+              child <- KDL.children $ KDL.nodeWith "bar" $ KDL.arg @Text+              pure (arg, child)+            decoder = KDL.document $ do+              KDL.dashNodesAtWith "foo" decodeChild+        KDL.decodeWith decoder config `shouldBe` Right [(1, "hello"), (2, "world")]++      it "fails if any child fails to parse" $ do+        let config = "foo { - { bar 1; }; - { bar \"test\"; }; }"+            decoder = KDL.document $ do+              KDL.dashNodesAtWith "foo" $ KDL.children $ KDL.argAt @Int "bar"+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > - #1 > bar #0 > arg #0"+            , "  Expected number, got: test"+            ]++  describe "NodeDecoder" $ do+    let decodeNode name decoder config =+          KDL.decodeWith+            ( KDL.document $ do+                KDL.nodeWith name decoder+            )+            config++    describe "arg" $ do+      it "decodes an argument" $ do+        let config = "foo 1 \"bar\""+            decoder = do+              arg1 <- KDL.arg @Int+              arg2 <- KDL.arg @Text+              pure (arg1, arg2)+        decodeNode "foo" decoder config `shouldBe` Right (1, "bar")++      it "decodes multiple arguments" $ do+        let config = "foo 1 2 3"+            decoder = do+              KDL.many $ KDL.arg @Int+        decodeNode "foo" decoder config `shouldBe` Right [1, 2, 3]++      it "fails if argument doesn't exist" $ do+        let config = "foo"+            decoder = do+              KDL.arg @Int+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected arg #0"+            ]++      it "fails if argument fails to parse" $ do+        let config = "foo \"test\""+            decoder = do+              KDL.arg @Int+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected number, got: test"+            ]++      it "fails if not all arguments are decoded" $ do+        let config = "foo 1 2 3"+            decoder = do+              KDL.arg @Int+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected arg #1: 2"+            ]++    -- Most behaviors tested with `arg`+    describe "argWith" $ do+      it "decodes an argument" $ do+        let config = "foo \"bar\""+            decoder = do+              KDL.argWith KDL.text+        decodeNode "foo" decoder config `shouldBe` Right "bar"++    -- Most behaviors tested with `argWith`+    describe "argWith'" $ do+      it "decodes argument with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo (test)a"+              decoder = do+                KDL.argWith' anns KDL.text+          decodeNode "foo" decoder config `shouldBe` Right "a"++      it "decodes argument without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a"+              decoder = do+                KDL.argWith' anns KDL.text+          decodeNode "foo" decoder config `shouldBe` Right "a"++      it "fails when argument has unexpected annotation" $ do+        let config = "foo (test)a"+            decoder = do+              KDL.argWith' ["VAL"] KDL.text+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "prop" $ do+      it "decodes a prop" $ do+        let config = "foo test1=1 test2=\"hello\""+            decoder = do+              prop1 <- KDL.prop @Text "test2"+              prop2 <- KDL.prop @Int "test1"+              pure (prop1, prop2)+        decodeNode "foo" decoder config `shouldBe` Right ("hello", 1)++      it "can optionally decode a prop" $ do+        let config = "foo a=1"+            decoder = do+              a <- KDL.optional $ KDL.prop @Int "a"+              b <- KDL.optional $ KDL.prop @Int "b"+              pure (a, b)+        decodeNode "foo" decoder config `shouldBe` Right (Just 1, Nothing)++      it "decodes last prop" $ do+        let config = "foo test=1 test=2"+            decoder = do+              KDL.prop @Int "test"+        decodeNode "foo" decoder config `shouldBe` Right 2++      it "fails if prop doesn't exist" $ do+        let config = "foo 123"+            decoder = do+              KDL.prop @Int "test"+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Expected prop: test"+            ]++      it "fails if prop fails to parse" $ do+        let config = "foo hello=world"+            decoder = do+              KDL.prop @Int "hello"+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop hello"+            , "  Expected number, got: world"+            ]++      it "fails if not all props are decoded" $ do+        let config = "foo a=1 b=2"+            decoder = do+              KDL.prop @Int "a"+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected prop: b=2"+            ]++    -- Most behaviors tested with `prop`+    describe "propWith" $ do+      it "decodes a prop" $ do+        let config = "foo a=1"+            decoder = do+              KDL.propWith "a" KDL.number+        decodeNode "foo" decoder config `shouldBe` Right 1++    -- Most behaviors tested with `propWith`+    describe "propWith'" $ do+      it "decodes prop with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=(test)1"+              decoder = do+                KDL.propWith' "a" anns KDL.number+          decodeNode "foo" decoder config `shouldBe` Right 1++      it "decodes prop without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=1"+              decoder = do+                KDL.propWith' "a" anns KDL.number+          decodeNode "foo" decoder config `shouldBe` Right 1++      it "fails when prop has unexpected annotation" $ do+        let config = "foo a=(test)1"+            decoder = do+              KDL.propWith' "a" ["VAL"] KDL.number+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop a"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "remainingProps" $ do+      it "decodes remaining props" $ do+        let config = "foo a=1 b=2 c=3 b=4"+            decoder = do+              _ <- KDL.prop @Int "a"+              KDL.remainingProps @Int+        decodeNode "foo" decoder config+          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]++      it "returns empty map if no props left" $ do+        let config = "foo a=1"+            decoder = do+              _ <- KDL.prop @Int "a"+              KDL.remainingProps @Int+        decodeNode "foo" decoder config `shouldBe` Right Map.empty++      it "fails if prop fails to parse" $ do+        let config = "foo a=1 b=1 c=2 c=test"+            decoder = do+              _ <- KDL.prop @Int "a"+              KDL.remainingProps @Int+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop c"+            , "  Expected number, got: test"+            ]++    -- Most behaviors tested with `remainingProps`+    describe "remainingPropsWith" $ do+      it "decodes remaining props" $ do+        let config = "foo a=1 b=2 c=3 b=4"+            decoder = do+              _ <- KDL.prop @Int "a"+              KDL.remainingPropsWith KDL.number+        decodeNode "foo" decoder config+          `shouldBe` (Right . Map.fromList) [("b", 4), ("c", 3)]++    -- Most behaviors tested with `remainingPropsWith`+    describe "remainingPropsWith'" $ do+      it "decodes props with an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=(test)1 b=(test)2"+              decoder = do+                KDL.remainingPropsWith' anns KDL.number+          decodeNode "foo" decoder config+            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]++      it "decodes props without an annotation" $ do+        let testCases =+              [ []+              , ["test"]+              , ["test", "other"]+              ]+        forM_ testCases $ \anns -> do+          let config = "foo a=1 b=2"+              decoder = do+                KDL.remainingPropsWith' anns KDL.number+          decodeNode "foo" decoder config+            `shouldBe` (Right . Map.fromList) [("a", 1), ("b", 2)]++      it "fails when prop has unexpected annotation" $ do+        let config = "foo a=(VAL)1 b=(test)2"+            decoder = do+              KDL.remainingPropsWith' ["VAL"] KDL.number+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > prop b"+            , "  Expected annotation to be one of [\"VAL\"], got: test"+            ]++    describe "children" $ do+      it "decodes children" $ do+        let config = "foo { bar test; }"+            decoder = do+              KDL.children $ KDL.node @Node "bar"+            expected =+              Node+                { ann = Nothing+                , name = Identifier{value = "bar", format = Nothing}+                , entries =+                    [ Entry+                        { name = Nothing+                        , value = Value{ann = Nothing, data_ = Text "test", format = Nothing}+                        , format = Nothing+                        }+                    ]+                , children = Just NodeList{nodes = [], format = Nothing}+                , format = Nothing+                }+        decodeNode "foo" decoder config `shouldBe` Right expected++      it "can be re-entered" $ do+        let config = "foo { bar a; baz b; }"+            decoder = do+              arg1 <- KDL.children $ KDL.argAt @Text "bar"+              arg2 <- KDL.children $ KDL.argAt @Text "baz"+              pure (arg1, arg2)+        decodeNode "foo" decoder config `shouldBe` Right ("a", "b")++      it "fails if not all children are decoded" $ do+        let config = "foo { asdf; bar; }"+            decoder = do+              KDL.children $ KDL.node @Node "bar"+        decodeNode "foo" decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0"+            , "  Unexpected node: asdf #0"+            ]++  describe "ValueDecoder" $ do+    describe "any" $ do+      it "decodes any value" $ do+        let config = "foo 1.0 asdf #true"+            decoder = KDL.document $ do+              KDL.argsAtWith "foo" KDL.any+            val data_ =+              Value+                { ann = Nothing+                , data_ = data_+                , format = Nothing+                }+        KDL.decodeWith decoder config+          `shouldBe` Right [val $ Number 1, val $ Text "asdf", val $ Bool True]++    describe "text" $ do+      it "decodes text value" $ do+        let config = "foo asdf"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.text+        KDL.decodeWith decoder config `shouldBe` Right "asdf"++      it "fails when value is not text" $ do+        let config = "foo 1"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.text+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected text, got: 1"+            ]++    describe "number" $ do+      it "decodes number value" $ do+        let config = "foo 1"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.number+        KDL.decodeWith decoder config `shouldBe` Right 1++      it "fails when value is not number" $ do+        let config = "foo asdf"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.number+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected number, got: asdf"+            ]++    describe "bool" $ do+      it "decodes bool value" $ do+        let config = "foo #true"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.bool+        KDL.decodeWith decoder config `shouldBe` Right True++      it "fails when value is not bool" $ do+        let config = "foo 1"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.bool+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected bool, got: 1"+            ]++    describe "null" $ do+      it "decodes null value" $ do+        let config = "foo #null"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.null+        KDL.decodeWith decoder config `shouldBe` Right ()++      it "fails when value is not null" $ do+        let config = "foo 1"+            decoder = KDL.document $ do+              KDL.argAtWith "foo" KDL.null+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #0"+            , "  Expected null, got: 1"+            ]++  describe "Combinators" $ do+    describe "oneOf" $ do+      it "decodes one of the options" $ do+        let config = "foo 123 hello"+            decoder = KDL.document $ do+              KDL.nodeWith "foo" . KDL.many . KDL.argWith $+                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.text]+        KDL.decodeWith decoder config `shouldBe` Right [Left 123, Right "hello"]++      it "fails if none can be decoded" $ do+        let config = "foo 123 hello"+            decoder = KDL.document $ do+              KDL.nodeWith "foo" . KDL.many . KDL.argWith $+                KDL.oneOf [Left <$> KDL.number, Right <$> KDL.bool]+        KDL.decodeWith decoder config+          `shouldSatisfy` decodeErrorMsg+            [ "At: foo #0 > arg #1"+            , "  Expected bool, got: hello"+            , "  Expected number, got: hello"+            ]++    describe "option" $ do+      it "defaults to the given value" $ do+        let config = "foo"+            decoder = KDL.document $ do+              KDL.nodeWith "foo" $ KDL.option 123 $ KDL.arg @Int+        KDL.decodeWith decoder config `shouldBe` Right 123++newtype MyNode = MyNode Int+  deriving (Eq)++instance KDL.DecodeNode MyNode where+  validNodeTypeAnns _ = ["MyNode"]+  nodeDecoder = do+    x <- KDL.arg+    unless (0 < x && x < 10) $ do+      KDL.fail $ "Invalid argument: " <> (Text.pack . show) x+    pure $ MyNode x++decodeNodeSpec :: Spec+decodeNodeSpec = do+  describe "DecodeNode" $ do+    it "decodes a custom node" $ do+      let config = "foo 1"+          decoder = KDL.document $ do+            KDL.node "foo"+      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)++    it "throws user-specified error" $ do+      let config = "foo 100"+          decoder = KDL.document $ do+            KDL.node @MyNode "foo"+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0"+          , "  Invalid argument: 100"+          ]++    it "decodes valid type ann" $ do+      let config = "(MyNode)foo 1"+          decoder = KDL.document $ do+            KDL.node "foo"+      KDL.decodeWith decoder config `shouldBe` Right (MyNode 1)++    it "fails on invalid type ann" $ do+      let config = "(bad)foo 1"+          decoder = KDL.document $ do+            KDL.node @MyNode "foo"+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0"+          , "  Expected annotation to be one of [\"MyNode\"], got: bad"+          ]++newtype MyVal = MyVal Double+  deriving (Eq)++instance KDL.DecodeValue MyVal where+  validValueTypeAnns _ = ["MyVal"]+  valueDecoder = KDL.withDecoder KDL.number $ \x -> do+    unless (0 < x && x < 10) $ do+      KDL.failM $ "Invalid value: " <> (Text.pack . show) x+    pure $ MyVal (realToFrac x)++decodeValueSpec :: Spec+decodeValueSpec = do+  describe "DecodeValue" $ do+    it "decodes a custom value" $ do+      let config = "foo 1"+          decoder = KDL.document $ do+            KDL.argAt "foo"+      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)++    it "throws user-specified error" $ do+      let config = "foo 100.0"+          decoder = KDL.document $ do+            KDL.argAt @MyVal "foo"+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0 > arg #0"+          , "  Invalid value: 100.0"+          ]++    it "decodes valid type ann" $ do+      let config = "foo (MyVal)1"+          decoder = KDL.document $ do+            KDL.argAt "foo"+      KDL.decodeWith decoder config `shouldBe` Right (MyVal 1)++    it "fails on invalid type ann" $ do+      let config = "foo (bad)1"+          decoder = KDL.document $ do+            KDL.argAt @MyVal "foo"+      KDL.decodeWith decoder config+        `shouldSatisfy` decodeErrorMsg+          [ "At: foo #0 > arg #0"+          , "  Expected annotation to be one of [\"MyVal\"], got: bad"+          ]
+ test/KDL/ParserSpec.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE OverloadedStrings #-}++module KDL.ParserSpec (spec) where++import Data.Text (Text)+import Data.Text qualified as Text+import KDL.Parser+import KDL.Types (+  Entry (..),+  Identifier (..),+  Node (..),+  NodeList (..),+  Value (..),+  ValueData (..),+ )+import Skeletest+import Skeletest.Predicate qualified as P+import System.IO.Temp (withSystemTempDirectory)++spec :: Spec+spec = do+  describe "parse" $ do+    it "parses a KDL document" $ do+      let expected =+            newNodeList+              [ newNode+                  "foo"+                  [ newArg $ Number 1.0+                  , newProp "hello" $ Text "world"+                  ]+                  ( Just+                      [ newNode "bar" [] (Just [])+                      ]+                  )+              ]+      parse "foo hello=world 1.0 { bar; }" `shouldBe` Right expected++    it "returns a textual error on parse failure" $ do+      let msg =+            Text.intercalate "\n" $+              [ "1:10:"+              , "  |"+              , "1 | foo hello= 123"+              , "  |          ^^"+              , "unexpected \"= \""+              , "expecting Node Child, Node Space, or Node Terminator"+              ]+      parse "foo hello= 123" `shouldBe` Left msg++  describe "parseFile" $ do+    it "parses a KDL document from a filepath" $ do+      withSystemTempDirectory "" $ \tmpdir -> do+        let file = tmpdir ++ "/test.kdl"+        writeFile file "foo hello=world 1.0 { bar; }"+        let expected =+              newNodeList+                [ newNode+                    "foo"+                    [ newArg $ Number 1.0+                    , newProp "hello" $ Text "world"+                    ]+                    ( Just+                        [ newNode "bar" [] (Just [])+                        ]+                    )+                ]+        parseFile file `shouldSatisfy` P.returns (P.right (P.eq expected))++{----- Helpers -----}++newNodeList :: [Node] -> NodeList+newNodeList nodes =+  NodeList+    { nodes = nodes+    , format = Nothing+    }++newNode :: Text -> [Entry] -> Maybe [Node] -> Node+newNode name entries children =+  Node+    { ann = Nothing+    , name = newIdentifier name+    , entries = entries+    , children = newNodeList <$> children+    , format = Nothing+    }++newArg :: ValueData -> Entry+newArg = newEntry Nothing++newProp :: Text -> ValueData -> Entry+newProp = newEntry . Just++newEntry :: Maybe Text -> ValueData -> Entry+newEntry mName data_ =+  Entry+    { name = newIdentifier <$> mName+    , value =+        Value+          { ann = Nothing+          , data_ = data_+          , format = Nothing+          }+    , format = Nothing+    }++newIdentifier :: Text -> Identifier+newIdentifier value =+  Identifier+    { value = value+    , format = Nothing+    }