{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Hercules.Agent.Bag ( ParseBag (..), parseBag, part, whenKind, ) where import Data.Aeson import Data.Aeson.KeyMap qualified as AK import Data.Aeson.Types import Data.Functor.Compose import Data.Functor.Partitioner hiding ( part, ) import Protolude -- TODO: Use a Validation instead of Either to return all errors at once -- | Partitioning and validation for heterogeneous JSON maps newtype ParseBag a b = ParseBag {getReadBag :: Compose (Partitioner (WithKey Text a)) Parser b} deriving newtype (Functor, Applicative) -- | Text argument: Map key, a: thing you're parsing. Return 'Nothing' to skip the object and let another part handle it. part :: (Text -> a -> Maybe (Parser b)) -> ParseBag a (Map Text b) part f = ParseBag $ Compose $ traversePartWithKey f parseBag :: ParseBag Value a -> Value -> Parser a parseBag f v = do m <- parseJSON v partitionMap (getCompose $ getReadBag f) m -- | Ignore if the value is not an object or if it doesn't have a "kind" field -- set to the provided kind. whenKind :: Text -> (Value -> Maybe a) -> Value -> Maybe a whenKind expectedKind f v@(Object o) = ( do x <- AK.lookup "kind" o guard (x == String expectedKind) ) *> f v whenKind _ _ _ = Nothing