packages feed

keiki-0.9.0.0: test/Keiki/Fixtures/RegisterEmission.hs

-- | Shared fixture, integration point 4 of
-- @docs/masterplans/16-harden-keiki-correctness-and-api-surfaces-surfaced-by-the-2026-07-architecture-review.md@:
-- consumed by EP-71 (validation alignment), EP-73 (round-trip property
-- harness), and EP-74 (composition semantics). Do not fold into a spec module.
module Keiki.Fixtures.RegisterEmission
  ( RegisterCmd (..),
    RegisterEvent (..),
    RegisterVertex (..),
    RegisterEmissionRegs,
    registerEmission,
    registerCommands,
  )
where

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

data RegisterCmd
  = Open Text
  | Add Int
  | Close
  deriving stock (Eq, Show)

data RegisterEvent
  = Opened Text
  | Added Int Text
  | Closed Text
  | Archived Text
  deriving stock (Eq, Show)

data RegisterVertex = Fresh | Active | Finished
  deriving stock (Eq, Ord, Show, Enum, Bounded)

type RegisterEmissionRegs =
  '[ '("owner", Text),
     '("total", Int)
   ]

inCtorOpen :: InCtor RegisterCmd '[ '("owner", Text)]
inCtorOpen =
  unavailableInCtor
    "Open"
    (\case Open owner -> Just (RCons (Proxy @"owner") owner RNil); _ -> Nothing)
    (\(RCons _ owner RNil) -> Open owner)

inCtorAdd :: InCtor RegisterCmd '[ '("amount", Int)]
inCtorAdd =
  unavailableInCtor
    "Add"
    (\case Add amount -> Just (RCons (Proxy @"amount") amount RNil); _ -> Nothing)
    (\(RCons _ amount RNil) -> Add amount)

inCtorClose :: InCtor RegisterCmd '[]
inCtorClose =
  unavailableInCtor "Close" (\case Close -> Just RNil; _ -> Nothing) (\RNil -> Close)

wireOpened :: WireCtor RegisterEvent (Text, ())
wireOpened =
  unavailableWireCtor
    "Opened"
    (\case Opened owner -> Just (owner, ()); _ -> Nothing)
    (\(owner, ()) -> Opened owner)

wireAdded :: WireCtor RegisterEvent (Int, (Text, ()))
wireAdded =
  unavailableWireCtor
    "Added"
    (\case Added amount owner -> Just (amount, (owner, ())); _ -> Nothing)
    (\(amount, (owner, ())) -> Added amount owner)

wireClosed :: WireCtor RegisterEvent (Text, ())
wireClosed =
  unavailableWireCtor
    "Closed"
    (\case Closed owner -> Just (owner, ()); _ -> Nothing)
    (\(owner, ()) -> Closed owner)

wireArchived :: WireCtor RegisterEvent (Text, ())
wireArchived =
  unavailableWireCtor
    "Archived"
    (\case Archived owner -> Just (owner, ()); _ -> Nothing)
    (\(owner, ()) -> Archived owner)

registerEmission :: SymTransducer (HsPred RegisterEmissionRegs RegisterCmd) RegisterEmissionRegs RegisterVertex RegisterCmd RegisterEvent
registerEmission =
  SymTransducer
    { edgesOut = \case
        Fresh ->
          [ Edge
              { guard = matchInCtor inCtorOpen,
                update = USet (#owner :: IndexN "owner" RegisterEmissionRegs Text) (TInpCtorField inCtorOpen (#owner :: Index '[ '("owner", Text)] Text)),
                output = [pack inCtorOpen wireOpened (TInpCtorField inCtorOpen (#owner :: Index '[ '("owner", Text)] Text) *: oNil)],
                target = Active,
                mode = Live
              }
          ]
        Active ->
          [ Edge
              { guard = matchInCtor inCtorAdd,
                update = USet (#total :: IndexN "total" RegisterEmissionRegs Int) (TInpCtorField inCtorAdd (#amount :: Index '[ '("amount", Int)] Int)),
                output =
                  [ pack
                      inCtorAdd
                      wireAdded
                      ( TInpCtorField inCtorAdd (#amount :: Index '[ '("amount", Int)] Int)
                          *: TReg (#owner :: Index RegisterEmissionRegs Text)
                          *: oNil
                      )
                  ],
                target = Active,
                mode = Live
              },
            Edge
              { guard = matchInCtor inCtorClose,
                update = UKeep,
                output =
                  [ pack inCtorClose wireClosed (TReg (#owner :: Index RegisterEmissionRegs Text) *: oNil),
                    pack inCtorClose wireArchived (TReg (#owner :: Index RegisterEmissionRegs Text) *: oNil)
                  ],
                target = Finished,
                mode = Live
              }
          ]
        Finished -> [],
      initial = Fresh,
      initialRegs = RCons (Proxy @"owner") "" (RCons (Proxy @"total") 0 RNil),
      isFinal = (== Finished)
    }

registerCommands :: [RegisterCmd]
registerCommands = [Open "alice", Add 7, Close]