packages feed

keiro-dsl-0.17.0.0: test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Codec.hs

-- @generated by keiro-dsl 0.17.0.0 (language keiro-dsl 4) from aggregate ArtifactCatalog; do not edit.
module Generated.StructuralConformance.ArtifactCatalog.Codec (
    artifactCatalogCodec,
    parseArtifactCatalogEvent,
    encodeArtifactCatalogEvent,
    encodeArtifactInfoMapped,
    decodeArtifactInfoMapped,
    encodeArtifactKindMapped,
    decodeArtifactKindMapped,
    encodeArtifactLocationMapped,
    decodeArtifactLocationMapped,
    encodeArtifactMetadataMapped,
    decodeArtifactMetadataMapped,
) where

import Generated.StructuralConformance.ArtifactCatalog.Domain
import Control.Monad (unless)
import Data.Aeson (Value (..), object, parseJSON, toJSON, withObject, withText, (.:), (.=))
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Aeson.Types (Parser, JSONPathElement (..), (<?>), explicitParseField, parseEither)
import Data.List.NonEmpty (NonEmpty (..))
import Data.List.NonEmpty qualified as NonEmpty
import Data.Text (Text)
import qualified Data.Text as T
import Keiro.Codec.Structural (bindingFromShape, bindingToShape)
import Keiro.Codec (Codec (..), EventType (..))

import Conformance.Structural.Domain ()
import Conformance.Structural.Bindings qualified as Bindings
import Conformance.Structural.Domain (ArtifactInfo, ArtifactKind, ArtifactLocation, ArtifactMetadata)
import Generated.StructuralConformance.Structural.Shape.ArtifactInfo qualified as ShapeArtifactInfo
import Generated.StructuralConformance.Structural.Shape.ArtifactKind qualified as ShapeArtifactKind
import Generated.StructuralConformance.Structural.Shape.ArtifactLocation qualified as ShapeArtifactLocation
import Generated.StructuralConformance.Structural.Shape.ArtifactMetadata qualified as ShapeArtifactMetadata



encodeArtifactInfoMapped :: ArtifactInfo -> Value
encodeArtifactInfoMapped = encodeArtifactInfoShape . bindingToShape Bindings.artifactInfoBinding

parseArtifactInfoMapped :: Value -> Parser ArtifactInfo
parseArtifactInfoMapped value = bindingFromShape Bindings.artifactInfoBinding <$> parseArtifactInfoShape value

decodeArtifactInfoMapped :: Value -> Either Text ArtifactInfo
decodeArtifactInfoMapped = mapLeftText . parseEither parseArtifactInfoMapped

encodeArtifactInfoShape :: ShapeArtifactInfo.ArtifactInfoShape -> Value
encodeArtifactInfoShape shape =
  object
      [ "artifact_key" .= toJSON (shape.artifactKey)
      , "display_name" .= toJSON (shape.displayName)
      , "artifact_hash" .= maybe Null (\item0 -> toJSON (item0)) (shape.artifactHash)
      , "artifact_kind" .= encodeArtifactKindShape (shape.artifactKind)
      , "location" .= encodeArtifactLocationShape (shape.location)
      , "metadata" .= encodeArtifactMetadataShape (shape.metadata)
      , "active" .= toJSON (shape.active)
      , "tags" .= toJSON (map (\item0 -> toJSON (item0)) (shape.tags))
      ]

parseArtifactInfoShape :: Value -> Parser ShapeArtifactInfo.ArtifactInfoShape
parseArtifactInfoShape = withObject "ArtifactInfoShape" $ \objectValue -> do
  rejectUnknownFields "ArtifactInfo" ["artifact_key", "display_name", "artifact_hash", "artifact_kind", "location", "metadata", "active", "tags"] objectValue
  ShapeArtifactInfo.ArtifactInfo
    <$> explicitParseField (parseJSON) objectValue "artifact_key"
    <*> explicitParseField (parseJSON) objectValue "display_name"
    <*> parseOptionalField (pure Nothing) (\value0 -> case value0 of Null -> pure Nothing; other0 -> Just <$> (parseJSON) other0) objectValue "artifact_hash"
    <*> parseOptionalField (pure ShapeArtifactKind.Guide) (parseArtifactKindShape) objectValue "artifact_kind"
    <*> explicitParseField (parseArtifactLocationShape) objectValue "location"
    <*> explicitParseField (parseArtifactMetadataShape) objectValue "metadata"
    <*> parseOptionalField (pure False) (parseJSON) objectValue "active"
    <*> parseOptionalField (pure []) (\value0 -> do items0 <- (parseJSON value0 :: Parser [Value]); traverse (\(index0, item0) -> (parseJSON) item0 <?> Index index0) (zip [0..] items0)) objectValue "tags"

encodeArtifactKindMapped :: ArtifactKind -> Value
encodeArtifactKindMapped = encodeArtifactKindShape . bindingToShape Bindings.artifactKindBinding

parseArtifactKindMapped :: Value -> Parser ArtifactKind
parseArtifactKindMapped value = bindingFromShape Bindings.artifactKindBinding <$> parseArtifactKindShape value

decodeArtifactKindMapped :: Value -> Either Text ArtifactKind
decodeArtifactKindMapped = mapLeftText . parseEither parseArtifactKindMapped

encodeArtifactKindShape :: ShapeArtifactKind.ArtifactKindShape -> Value
encodeArtifactKindShape = \case
  ShapeArtifactKind.Guide -> String "guide"
  ShapeArtifactKind.Reference -> String "reference"

parseArtifactKindShape :: Value -> Parser ShapeArtifactKind.ArtifactKindShape
parseArtifactKindShape = withText "ArtifactKindShape" $ \tag -> case tag of
  "guide" -> pure ShapeArtifactKind.Guide
  "reference" -> pure ShapeArtifactKind.Reference
  unknownTag -> fail ("unknown ArtifactKind wire value " <> show unknownTag <> "; expected one of: guide, reference")

encodeArtifactLocationMapped :: ArtifactLocation -> Value
encodeArtifactLocationMapped = encodeArtifactLocationShape . bindingToShape Bindings.artifactLocationBinding

parseArtifactLocationMapped :: Value -> Parser ArtifactLocation
parseArtifactLocationMapped value = bindingFromShape Bindings.artifactLocationBinding <$> parseArtifactLocationShape value

decodeArtifactLocationMapped :: Value -> Either Text ArtifactLocation
decodeArtifactLocationMapped = mapLeftText . parseEither parseArtifactLocationMapped

encodeArtifactLocationShape :: ShapeArtifactLocation.ArtifactLocationShape -> Value
encodeArtifactLocationShape = \case
  ShapeArtifactLocation.LocalFile payload ->
    object
      [ "tag" .= ("local_file" :: Text)
      , "contents" .= toJSON (payload)
      ]
  ShapeArtifactLocation.LocalDir payload ->
    object
      [ "tag" .= ("local_dir" :: Text)
      , "contents" .= toJSON (payload)
      ]
  ShapeArtifactLocation.RepoPath payload ->
    object
      [ "tag" .= ("repo_path" :: Text)
      , "contents" .= toJSON (payload)
      ]
  ShapeArtifactLocation.LocUrl payload ->
    object
      [ "tag" .= ("url" :: Text)
      , "contents" .= toJSON (payload)
      ]
  ShapeArtifactLocation.Canonical ->
    object
      [ "tag" .= ("canonical" :: Text)
      ]

parseArtifactLocationShape :: Value -> Parser ShapeArtifactLocation.ArtifactLocationShape
parseArtifactLocationShape = withObject "ArtifactLocationShape" $ \objectValue -> do
  tag <- explicitParseField (withText "ArtifactLocation tag" validateArtifactLocationTag) objectValue "tag"
  case tag of
    "local_file" -> do
      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
      ShapeArtifactLocation.LocalFile <$> explicitParseField (parseJSON) objectValue "contents"
    "local_dir" -> do
      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
      ShapeArtifactLocation.LocalDir <$> explicitParseField (parseJSON) objectValue "contents"
    "repo_path" -> do
      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
      ShapeArtifactLocation.RepoPath <$> explicitParseField (parseJSON) objectValue "contents"
    "url" -> do
      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
      ShapeArtifactLocation.LocUrl <$> explicitParseField (parseJSON) objectValue "contents"
    "canonical" -> do
      rejectUnknownFields "ArtifactLocation" ["tag"] objectValue
      pure ShapeArtifactLocation.Canonical
    _ -> fail "validated union tag was not handled"

validateArtifactLocationTag :: Text -> Parser Text
validateArtifactLocationTag tag
  | tag `elem` ["local_file", "local_dir", "repo_path", "url", "canonical"] = pure tag
  | otherwise = fail ("unknown ArtifactLocation union tag " <> show tag <> "; expected one of: local_file, local_dir, repo_path, url, canonical")

encodeArtifactMetadataMapped :: ArtifactMetadata -> Value
encodeArtifactMetadataMapped = encodeArtifactMetadataShape . bindingToShape Bindings.artifactMetadataBinding

parseArtifactMetadataMapped :: Value -> Parser ArtifactMetadata
parseArtifactMetadataMapped value = bindingFromShape Bindings.artifactMetadataBinding <$> parseArtifactMetadataShape value

decodeArtifactMetadataMapped :: Value -> Either Text ArtifactMetadata
decodeArtifactMetadataMapped = mapLeftText . parseEither parseArtifactMetadataMapped

encodeArtifactMetadataShape :: ShapeArtifactMetadata.ArtifactMetadataShape -> Value
encodeArtifactMetadataShape shape =
  object
      [ "note" .= maybe Null (\item0 -> toJSON (item0)) (shape.note)
      ]

parseArtifactMetadataShape :: Value -> Parser ShapeArtifactMetadata.ArtifactMetadataShape
parseArtifactMetadataShape = withObject "ArtifactMetadataShape" $ \objectValue -> do
  ShapeArtifactMetadata.ArtifactMetadata
    <$> explicitParseField (\value0 -> case value0 of Null -> pure Nothing; other0 -> Just <$> (parseJSON) other0) objectValue "note"

artifactCatalogEventTypes :: NonEmpty EventType
artifactCatalogEventTypes = EventType "ArtifactRecorded" :| [EventType "ArtifactAccepted"]

artifactCatalogCodec :: Codec ArtifactCatalogEvent
artifactCatalogCodec =
  Codec
    { eventTypes = artifactCatalogEventTypes
    , eventType = \case
        ArtifactRecorded{} -> EventType "ArtifactRecorded"
        ArtifactAccepted{} -> EventType "ArtifactAccepted"
    , schemaVersion = 1
    , encode = encodeArtifactCatalogEvent
    , decode = parseArtifactCatalogEvent
    , upcasters = []
    }

encodeArtifactCatalogEvent :: ArtifactCatalogEvent -> Value
encodeArtifactCatalogEvent = \case
  ArtifactRecorded payload ->
    object
      [ "kind" .= ("ArtifactRecorded" :: Text)
      , "artifact" .= encodeArtifactInfoMapped payload.artifact
      , "geometry" .= toJSON payload.geometry
      , "accepted" .= payload.accepted
      ]
  ArtifactAccepted payload ->
    object
      [ "kind" .= ("ArtifactAccepted" :: Text)
      , "accepted" .= payload.accepted
      ]

parseArtifactCatalogEvent :: EventType -> Value -> Either Text ArtifactCatalogEvent
parseArtifactCatalogEvent (EventType tag) = mapLeftText . parseEither (withObject "ArtifactCatalogEvent" go)
  where
    go o = do
      case tag of
        "ArtifactRecorded" ->
          ArtifactRecorded
            <$> ( ArtifactRecordedData
                    <$> explicitParseField parseArtifactInfoMapped o "artifact"
                    <*> o .: "geometry"
                    <*> o .: "accepted"
                )
        "ArtifactAccepted" ->
          ArtifactAccepted
            <$> ( ArtifactAcceptedData
                    <$> o .: "accepted"
                )
        _ -> fail ("unknown event type " <> show tag <> "; expected one of: " <> renderExpectedEventTypes artifactCatalogEventTypes)

mapLeftText :: Either String b -> Either Text b
mapLeftText = either (Left . T.pack) Right

renderExpectedEventTypes :: NonEmpty EventType -> String
renderExpectedEventTypes =
  T.unpack
    . T.intercalate ", "
    . map (\(EventType eventTypeName) -> eventTypeName)
    . NonEmpty.toList

parseOptionalField :: Parser fieldValue -> (Value -> Parser fieldValue) -> KeyMap.KeyMap Value -> Key.Key -> Parser fieldValue
parseOptionalField onMissing parseItem objectValue key =
  case KeyMap.lookup key objectValue of
    Nothing -> onMissing
    Just _ -> explicitParseField parseItem objectValue key

rejectUnknownFields :: String -> [Text] -> KeyMap.KeyMap Value -> Parser ()
rejectUnknownFields label allowed objectValue =
  unless (null extras) (fail (label <> " contains unknown fields: " <> show extras))
  where
    extras = filter (`notElem` allowed) (map Key.toText (KeyMap.keys objectValue))