packages feed

keiki-0.2.0.0: test/Keiki/Fixtures/BrokenTailCoverage.hs

-- | Deliberately defective EP-73 fixture. Its command coverage is split
-- across two events, so the first event cannot reconstruct the command.
-- This module exists only to prove the round-trip harness and validator catch
-- EP-71's head-recoverability defect class. It is not an authoring example.
module Keiki.Fixtures.BrokenTailCoverage
  ( ProvisionData (..),
    BrokenCommand (..),
    BrokenEvent (..),
    BrokenVertex (..),
    BrokenRegs,
    brokenTailCoverage,
  )
where

import Data.Proxy (Proxy (..))
import Data.Text (Text)
import Keiki.Core

data ProvisionData = ProvisionData
  { owner :: Text,
    quota :: Int
  }
  deriving stock (Eq, Show)

data BrokenCommand = Provision ProvisionData
  deriving stock (Eq, Show)

newtype OwnerRecordedData = OwnerRecordedData {owner :: Text}
  deriving stock (Eq, Show)

newtype QuotaAssignedData = QuotaAssignedData {quota :: Int}
  deriving stock (Eq, Show)

data BrokenEvent
  = OwnerRecorded OwnerRecordedData
  | QuotaAssigned QuotaAssignedData
  deriving stock (Eq, Show)

data BrokenVertex = BtcIdle | BtcProvisioned
  deriving stock (Eq, Ord, Show, Enum, Bounded)

type BrokenRegs =
  '[ '("owner", Text),
     '("quota", Int)
   ]

type ProvisionFields =
  '[ '("owner", Text),
     '("quota", Int)
   ]

inCtorProvision :: InCtor BrokenCommand ProvisionFields
inCtorProvision =
  InCtor
    { icName = "Provision",
      icMatch = \case
        Provision ProvisionData {owner, quota} ->
          Just $
            RCons (Proxy @"owner") owner $
              RCons (Proxy @"quota") quota RNil,
      icBuild = \(RCons _ owner (RCons _ quota RNil)) ->
        Provision ProvisionData {owner, quota}
    }

wireOwnerRecorded :: WireCtor BrokenEvent (Text, ())
wireOwnerRecorded =
  WireCtor
    { wcName = "OwnerRecorded",
      wcMatch = \case
        OwnerRecorded OwnerRecordedData {owner} -> Just (owner, ())
        _ -> Nothing,
      wcBuild = \(owner, ()) -> OwnerRecorded OwnerRecordedData {owner}
    }

wireQuotaAssigned :: WireCtor BrokenEvent (Int, ())
wireQuotaAssigned =
  WireCtor
    { wcName = "QuotaAssigned",
      wcMatch = \case
        QuotaAssigned QuotaAssignedData {quota} -> Just (quota, ())
        _ -> Nothing,
      wcBuild = \(quota, ()) -> QuotaAssigned QuotaAssignedData {quota}
    }

provisionOwner :: Term BrokenRegs BrokenCommand ProvisionFields Text
provisionOwner = TInpCtorField inCtorProvision (#owner :: Index ProvisionFields Text)

provisionQuota :: Term BrokenRegs BrokenCommand ProvisionFields Int
provisionQuota = TInpCtorField inCtorProvision (#quota :: Index ProvisionFields Int)

brokenTailCoverage ::
  SymTransducer
    (HsPred BrokenRegs BrokenCommand)
    BrokenRegs
    BrokenVertex
    BrokenCommand
    BrokenEvent
brokenTailCoverage =
  SymTransducer
    { initial = BtcIdle,
      initialRegs =
        RCons (Proxy @"owner") "" $
          RCons (Proxy @"quota") 0 RNil,
      isFinal = (== BtcProvisioned),
      edgesOut = \case
        BtcIdle ->
          [ Edge
              { guard = matchInCtor inCtorProvision,
                update =
                  USet
                    (#owner :: IndexN "owner" BrokenRegs Text)
                    provisionOwner
                    `combine` USet
                      (#quota :: IndexN "quota" BrokenRegs Int)
                      provisionQuota,
                output =
                  [ pack
                      inCtorProvision
                      wireOwnerRecorded
                      (provisionOwner *: oNil),
                    pack
                      inCtorProvision
                      wireQuotaAssigned
                      (provisionQuota *: oNil)
                  ],
                target = BtcProvisioned
              }
          ]
        BtcProvisioned -> []
    }