{- |
Module      : Naming.Verify
Description : Pure retirement-binding verification (NOTE-024 item 3,
  NOTE-026)
License     : Apache-2.0

Pure predicates over already-resolved evidence for one retired
record. All inputs are plain bytes supplied by the caller (a reader
resolving them from transaction bodies); this module compares,
recomputes and routes — it parses nothing, trusts no constants, and
holds no keys.

The_EXE_ (retirement-verify) resolves every field below from retained
transaction CBOR plus public log mappings; the unit tests pin every
predicate here, including the wrong-policy\/same-name discrimination
(NOTE-026). Either side alone is insufficient: parsing without
predicates proves nothing, predicates without parsing check nothing.
-}
module Naming.Verify
    ( RetireEvidence (..)
    , CompleteEvidence (..)
    , positiveMintPolicy
    , verifyRetireEvidence
    , verifyCompletion
    , singleNamingToken
    ) where

import Data.ByteString (ByteString)

import Naming.Register (representativeName)

{- | Read one naming token from complete non-ADA value triples. The caller
supplies the deployment's allowed approval and representative policies.
-}
singleNamingToken
    :: [ByteString]
    -> [(ByteString, ByteString, Integer)]
    -> Either String (ByteString, Integer)
singleNamingToken :: [ByteString]
-> [(ByteString, ByteString, Integer)]
-> Either String (ByteString, Integer)
singleNamingToken [ByteString]
policies [(ByteString, ByteString, Integer)]
assets =
    case [(ByteString, ByteString, Integer)]
assets of
        [(ByteString
policy, ByteString
name, Integer
quantity)] | ByteString
policy ByteString -> [ByteString] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ByteString]
policies -> (ByteString, Integer) -> Either String (ByteString, Integer)
forall a b. b -> Either a b
Right (ByteString
name, Integer
quantity)
        [(ByteString, ByteString, Integer)]
_ -> String -> Either String (ByteString, Integer)
forall a b. a -> Either a b
Left String
"record-single-asset"

{- | One retired record, fully resolved to plain evidence. Every hash
is raw bytes as found in bodies or public lines — never a runner
constant, never a key.
-}
data RetireEvidence = RetireEvidence
    { RetireEvidence -> String
reRecord :: String
    -- ^ Record outref under audit (@txid#idx@, linkage display).
    , RetireEvidence -> String
reRetireTx :: String
    -- ^ Retire txid under audit.
    , RetireEvidence -> ByteString
reKey :: ByteString
    -- ^ Retire redeemer spelling (redeemer bytes).
    , RetireEvidence -> ByteString
reSpelling :: ByteString
    -- ^ Registry request key from the creation fold.
    , RetireEvidence -> ByteString
reCreationHash :: ByteString
    -- ^ Creation control hash (claim-tx datum bytes).
    , RetireEvidence -> ByteString
reCreationHashLog :: ByteString
    -- ^ Creation hash as published in public creation material.
    , RetireEvidence -> ByteString
reCurrentControl :: ByteString
    {- ^ Control of the record as retired (creation control for LT
    units, rotated destination for RR units — creating-datum bytes).
    -}
    , RetireEvidence -> [ByteString]
reQuorum :: [ByteString]
    -- ^ Quorum members of the record as retired (creating datum).
    , RetireEvidence -> ByteString
reStatePolicy :: ByteString
    {- ^ Registry STATE policy bytes (state output address bytes).
    The state policy forms the registry parameter, not the asset name.
    -}
    , RetireEvidence -> ByteString
reToken :: ByteString
    -- ^ Registry token name (request datum bytes).
    , RetireEvidence -> [(ByteString, ByteString, Integer)]
reCreationMint :: [(ByteString, ByteString, Integer)]
    -- ^ Creation-tx mint triples (policy, name, quantity).
    , RetireEvidence -> ByteString
reCustodyPolicy :: ByteString
    -- ^ Custody output asset policy (retire-tx bytes).
    , RetireEvidence -> ByteString
reCustodyName :: ByteString
    -- ^ Custody output asset name (retire-tx bytes).
    , RetireEvidence -> Integer
reCustodyQty :: Integer
    -- ^ Custody output asset quantity (retire-tx bytes).
    , RetireEvidence -> ByteString
reRepLog :: ByteString
    -- ^ Representative as published in public creation material.
    , RetireEvidence -> [ByteString]
reSigners :: [ByteString]
    -- ^ Retire-tx required signatories (body bytes).
    , RetireEvidence -> [ByteString]
reWitnesses :: [ByteString]
    -- ^ Retire-tx actual key witnesses (witness-set bytes).
    }
    deriving stock (RetireEvidence -> RetireEvidence -> Bool
(RetireEvidence -> RetireEvidence -> Bool)
-> (RetireEvidence -> RetireEvidence -> Bool) -> Eq RetireEvidence
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RetireEvidence -> RetireEvidence -> Bool
== :: RetireEvidence -> RetireEvidence -> Bool
$c/= :: RetireEvidence -> RetireEvidence -> Bool
/= :: RetireEvidence -> RetireEvidence -> Bool
Eq, Int -> RetireEvidence -> String -> String
[RetireEvidence] -> String -> String
RetireEvidence -> String
(Int -> RetireEvidence -> String -> String)
-> (RetireEvidence -> String)
-> ([RetireEvidence] -> String -> String)
-> Show RetireEvidence
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> RetireEvidence -> String -> String
showsPrec :: Int -> RetireEvidence -> String -> String
$cshow :: RetireEvidence -> String
show :: RetireEvidence -> String
$cshowList :: [RetireEvidence] -> String -> String
showList :: [RetireEvidence] -> String -> String
Show)

{- | The representative mint policy of a creation fold: exactly one
positive-quantity policy (the approval burn is negative). Anything
else — none, several — fails: the derivation admits no judgment
calls about which policy is \"the\" representative one.
-}
positiveMintPolicy
    :: [(ByteString, ByteString, Integer)] -> Either String ByteString
positiveMintPolicy :: [(ByteString, ByteString, Integer)] -> Either String ByteString
positiveMintPolicy [(ByteString, ByteString, Integer)]
triples = case [ByteString
p | (ByteString
p, ByteString
_, Integer
q) <- [(ByteString, ByteString, Integer)]
triples, Integer
q Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
0] of
    [ByteString
p] -> ByteString -> Either String ByteString
forall a b. b -> Either a b
Right ByteString
p
    [ByteString]
ps ->
        String -> Either String ByteString
forall a b. a -> Either a b
Left
            ( String
"expected exactly one positive mint policy, found "
                String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show ([ByteString] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [ByteString]
ps)
            )

{- | The full binding verdict. Every clause names its own failure;
@Right ()@ means the retirement's key, name, custody triple,
creation mint, signers and witnesses all check out.
-}
verifyRetireEvidence :: RetireEvidence -> Either String ()
verifyRetireEvidence :: RetireEvidence -> Either String ()
verifyRetireEvidence RetireEvidence
ev = do
    String -> ByteString -> ByteString -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"retire spelling is not the creation request key"
        (RetireEvidence -> ByteString
reKey RetireEvidence
ev)
        (RetireEvidence -> ByteString
reSpelling RetireEvidence
ev)
    String -> ByteString -> ByteString -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"claim-derived control differs from public creation material"
        (RetireEvidence -> ByteString
reCreationHash RetireEvidence
ev)
        (RetireEvidence -> ByteString
reCreationHashLog RetireEvidence
ev)
    -- Derive the policy from the creation mint, then verify custody agrees.
    repPolicy <- [(ByteString, ByteString, Integer)] -> Either String ByteString
positiveMintPolicy (RetireEvidence -> [(ByteString, ByteString, Integer)]
reCreationMint RetireEvidence
ev)
    let repComputed = ByteString -> ByteString
representativeName (RetireEvidence -> ByteString
reSpelling RetireEvidence
ev)
    -- Creation mint binds the recomputed name at +1 under that policy.
    unlessEq
        "creation mint does not carry the recomputed rep at +1"
        [(n, q) | (p, n, q) <- reCreationMint ev, p == repPolicy]
        [(repComputed, 1)]
    -- Custody triple binds policy, name and quantity together: a
    -- same-name token under a different policy is a different asset
    -- and refuses here (NOTE-026).
    unlessEq
        "custody policy is not the derived rep policy"
        (reCustodyPolicy ev)
        repPolicy
    unlessEq
        "custody name is not the recomputed rep"
        (reCustodyName ev)
        repComputed
    unlessEq "custody quantity is not 1" (reCustodyQty ev) 1
    unlessEq
        "recomputed rep differs from public creation material"
        repComputed
        (reRepLog ev)
    -- Route: singleton signers must be the current control
    -- (controller route, rotated or not); larger sets must equal the
    -- quorum with the control absent (quorum route). Every expected
    -- signer must also have witnessed (declared signatories alone
    -- prove no authorization happened).
    case reSigners ev of
        [ByteString
s] -> do
            String -> ByteString -> ByteString -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
                String
"singleton signer is not the record control"
                ByteString
s
                (RetireEvidence -> ByteString
reCurrentControl RetireEvidence
ev)
            RetireEvidence -> [ByteString] -> Either String ()
forall {t :: * -> *} {a}.
(Foldable t, IsString a) =>
RetireEvidence -> t ByteString -> Either a ()
unlessWitnessed RetireEvidence
ev [ByteString
s]
        [ByteString]
ss -> do
            String -> Int -> Int -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
                String
"signer count matches neither route"
                ([ByteString] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [ByteString]
ss)
                ([ByteString] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (RetireEvidence -> [ByteString]
reQuorum RetireEvidence
ev))
            String -> [ByteString] -> [ByteString] -> Either String ()
forall {t :: * -> *} {t :: * -> *} {a} {a}.
(Foldable t, Foldable t, Eq a) =>
a -> t a -> t a -> Either a ()
unlessSubset String
"quorum-route signer outside the quorum" [ByteString]
ss (RetireEvidence -> [ByteString]
reQuorum RetireEvidence
ev)
            String -> ByteString -> [ByteString] -> Either String ()
forall {t :: * -> *} {a} {a}.
(Foldable t, Eq a) =>
a -> a -> t a -> Either a ()
unlessAbsent
                String
"quorum route signed by the controller"
                (RetireEvidence -> ByteString
reCurrentControl RetireEvidence
ev)
                [ByteString]
ss
            RetireEvidence -> [ByteString] -> Either String ()
forall {t :: * -> *} {a}.
(Foldable t, IsString a) =>
RetireEvidence -> t ByteString -> Either a ()
unlessWitnessed RetireEvidence
ev [ByteString]
ss
  where
    unlessEq :: a -> a -> a -> Either a ()
unlessEq a
_ a
a a
b | a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
b = () -> Either a ()
forall a b. b -> Either a b
Right ()
    unlessEq a
msg a
_ a
_ = a -> Either a ()
forall a b. a -> Either a b
Left a
msg
    unlessSubset :: a -> t a -> t a -> Either a ()
unlessSubset a
_ t a
xs t a
ys | (a -> Bool) -> t a -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (a -> t a -> Bool
forall a. Eq a => a -> t a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t a
ys) t a
xs = () -> Either a ()
forall a b. b -> Either a b
Right ()
    unlessSubset a
msg t a
_ t a
_ = a -> Either a ()
forall a b. a -> Either a b
Left a
msg
    unlessAbsent :: a -> a -> t a -> Either a ()
unlessAbsent a
_ a
x t a
ys | a
x a -> t a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` t a
ys = () -> Either a ()
forall a b. b -> Either a b
Right ()
    unlessAbsent a
msg a
_ t a
_ = a -> Either a ()
forall a b. a -> Either a b
Left a
msg
    unlessWitnessed :: RetireEvidence -> t ByteString -> Either a ()
unlessWitnessed RetireEvidence
e t ByteString
ss =
        a -> t ByteString -> [ByteString] -> Either a ()
forall {t :: * -> *} {t :: * -> *} {a} {a}.
(Foldable t, Foldable t, Eq a) =>
a -> t a -> t a -> Either a ()
unlessSubset
            a
"expected signer has no witness bytes"
            t ByteString
ss
            (RetireEvidence -> [ByteString]
reWitnesses RetireEvidence
e)

{- | One permissionless completion, fully resolved to plain evidence.
The representative pair comes from an already-verified retirement
(never re-derived here); every other field is resolved from the
completion transaction's own bytes.
-}
data CompleteEvidence = CompleteEvidence
    { CompleteEvidence -> String
ceRetireTx :: String
    -- ^ Retire txid this completion closes (linkage display).
    , CompleteEvidence -> String
ceCompleteTx :: String
    -- ^ Completion txid under audit.
    , CompleteEvidence -> String
ceCustodyTxid :: String
    -- ^ Creator txid of the spent custody input (body bytes).
    , CompleteEvidence -> String
ceRequestTxid :: String
    {- ^ Creator txid of the folded request input (body bytes).
    Co-creation (NOTE-029): these two must be equal — the folded
    request is the retire's own, not one queued anywhere else.
    -}
    , CompleteEvidence -> ByteString
ceReqKey :: ByteString
    {- ^ Folded request's registry key (request datum bytes): must be
    the burned asset's own name (#157 token-name-is-registry-key, the asset name IS the
    key).
    -}
    , CompleteEvidence -> Integer
ceReqEdge :: Integer
    {- ^ Folded request's seven-admitted-edges row index (request datum bytes): must be
    the retirement, edge 3 `updateTerminal` (#183). The request states
    no values any more, so the move it asks for is the tag itself, and
    that tag is what the cage reads and the scripts check on ledger.
    -}
    , CompleteEvidence -> ByteString
ceRepPolicy :: ByteString
    -- ^ Verified representative policy (from the retire unit).
    , CompleteEvidence -> ByteString
ceRepName :: ByteString
    -- ^ Verified representative name (from the retire unit).
    , CompleteEvidence -> ByteString
ceCustodyPolicy :: ByteString
    -- ^ Spent custody input's asset policy (body bytes).
    , CompleteEvidence -> ByteString
ceCustodyName :: ByteString
    -- ^ Spent custody input's asset name (body bytes).
    , CompleteEvidence -> Integer
ceCustodyQty :: Integer
    -- ^ Spent custody input's asset quantity (body bytes).
    , CompleteEvidence -> [(ByteString, ByteString, Integer)]
ceMint :: [(ByteString, ByteString, Integer)]
    -- ^ Completion mint triples (body bytes): exactly the burn.
    , CompleteEvidence -> Bool
ceBurnRedeemerOk :: Bool
    {- ^ Mint redeemer at the mint purpose is the burn shape
    (resolved by the caller; this module checks no redeemers).
    -}
    , CompleteEvidence -> Bool
ceIsModify :: Bool
    -- ^ State-spend redeemer decodes as a `Modify` (caller-resolved).
    , CompleteEvidence -> Int
ceActionCount :: Int
    {- ^ Number of actions in that `Modify` (caller-resolved): exactly
    one. With a changed root below, singleton-ness is exactly one
    genuine `UpdateAction` (`Rejected` preserves the root).
    -}
    , CompleteEvidence -> ByteString
ceRootBefore :: ByteString
    -- ^ Spent state root (state datum bytes).
    , CompleteEvidence -> ByteString
ceRootAfter :: ByteString
    {- ^ Continuation state root (state datum bytes): must differ
    (a genuine transition, not Rejected-preserved).
    -}
    , CompleteEvidence -> [ByteString]
ceReqSigners :: [ByteString]
    {- ^ Required signatories (body bytes): must be empty
    (permissionless — no approval by anybody).
    -}
    , CompleteEvidence -> [ByteString]
ceWitnesses :: [ByteString]
    -- ^ Actual key witnesses (witness-set bytes).
    , CompleteEvidence -> ByteString
ceFeeOwner :: ByteString
    {- ^ Payment hash owning the fee input (body bytes): the sole
    witness must be exactly this key (fee ownership, never
    authorization — see the Q-file on the no-signature criterion).
    -}
    , CompleteEvidence -> [ByteString]
ceRouteKeys :: [ByteString]
    -- ^ Route keys that must not witness (control plus quorum).
    }
    deriving stock (CompleteEvidence -> CompleteEvidence -> Bool
(CompleteEvidence -> CompleteEvidence -> Bool)
-> (CompleteEvidence -> CompleteEvidence -> Bool)
-> Eq CompleteEvidence
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CompleteEvidence -> CompleteEvidence -> Bool
== :: CompleteEvidence -> CompleteEvidence -> Bool
$c/= :: CompleteEvidence -> CompleteEvidence -> Bool
/= :: CompleteEvidence -> CompleteEvidence -> Bool
Eq, Int -> CompleteEvidence -> String -> String
[CompleteEvidence] -> String -> String
CompleteEvidence -> String
(Int -> CompleteEvidence -> String -> String)
-> (CompleteEvidence -> String)
-> ([CompleteEvidence] -> String -> String)
-> Show CompleteEvidence
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> CompleteEvidence -> String -> String
showsPrec :: Int -> CompleteEvidence -> String -> String
$cshow :: CompleteEvidence -> String
show :: CompleteEvidence -> String
$cshowList :: [CompleteEvidence] -> String -> String
showList :: [CompleteEvidence] -> String -> String
Show)

{- | The full completion verdict: co-created pair, exact burn,
singleton-`Modify`, genuine root change, and permissionless
authorization (empty required signers, fee-owner-only witness
outside every route).
-}
verifyCompletion :: CompleteEvidence -> Either String ()
verifyCompletion :: CompleteEvidence -> Either String ()
verifyCompletion CompleteEvidence
ev = do
    -- Co-creation: the folded request comes from the same creator
    -- transaction as the spent custody (a request queued anywhere
    -- else, or no request at all, refuses).
    String -> String -> String -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"folded request is not the retire's own (creator mismatch)"
        (CompleteEvidence -> String
ceRequestTxid CompleteEvidence
ev)
        (CompleteEvidence -> String
ceCustodyTxid CompleteEvidence
ev)
    -- Edge relation (#183): the folded request retires the burned
    -- asset's own key, and the move it names is the retirement. The
    -- request carries no values, so this is the whole of what it
    -- states about the move.
    String -> ByteString -> ByteString -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"folded request is not for the burned asset's key"
        (CompleteEvidence -> ByteString
ceReqKey CompleteEvidence
ev)
        (CompleteEvidence -> ByteString
ceRepName CompleteEvidence
ev)
    String -> Integer -> Integer -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"folded request does not name the retirement edge"
        (CompleteEvidence -> Integer
ceReqEdge CompleteEvidence
ev)
        Integer
3
    -- Burn: the spent custody triple is the verified pair, and the
    -- mint field burns exactly it once.
    String -> ByteString -> ByteString -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"spent custody policy is not the verified rep policy"
        (CompleteEvidence -> ByteString
ceCustodyPolicy CompleteEvidence
ev)
        (CompleteEvidence -> ByteString
ceRepPolicy CompleteEvidence
ev)
    String -> ByteString -> ByteString -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"spent custody name is not the verified rep"
        (CompleteEvidence -> ByteString
ceCustodyName CompleteEvidence
ev)
        (CompleteEvidence -> ByteString
ceRepName CompleteEvidence
ev)
    String -> Integer -> Integer -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq String
"spent custody quantity is not 1" (CompleteEvidence -> Integer
ceCustodyQty CompleteEvidence
ev) Integer
1
    String
-> [(ByteString, ByteString, Integer)]
-> [(ByteString, ByteString, Integer)]
-> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"completion mint is not exactly the burn"
        (CompleteEvidence -> [(ByteString, ByteString, Integer)]
ceMint CompleteEvidence
ev)
        [(CompleteEvidence -> ByteString
ceRepPolicy CompleteEvidence
ev, CompleteEvidence -> ByteString
ceRepName CompleteEvidence
ev, -Integer
1)]
    String -> Bool -> Either String ()
forall {a}. a -> Bool -> Either a ()
unlessOk String
"mint redeemer is not the burn shape" (CompleteEvidence -> Bool
ceBurnRedeemerOk CompleteEvidence
ev)
    -- Transition: singleton `Modify` plus a changed root (a
    -- `Rejected` action preserves the root and refuses here).
    String -> Bool -> Either String ()
forall {a}. a -> Bool -> Either a ()
unlessOk String
"state spend is not a Modify" (CompleteEvidence -> Bool
ceIsModify CompleteEvidence
ev)
    String -> Int -> Int -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq String
"Modify action is not a singleton" (CompleteEvidence -> Int
ceActionCount CompleteEvidence
ev) Int
1
    String -> ByteString -> ByteString -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessNeq
        String
"registry root unchanged (no genuine transition)"
        (CompleteEvidence -> ByteString
ceRootBefore CompleteEvidence
ev)
        (CompleteEvidence -> ByteString
ceRootAfter CompleteEvidence
ev)
    -- Permissionless: nobody's approval rides the transaction; the
    -- sole witness owns the fee input and stands outside every route.
    String -> [ByteString] -> [ByteString] -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"required signers not empty (not permissionless)"
        (CompleteEvidence -> [ByteString]
ceReqSigners CompleteEvidence
ev)
        []
    String -> [ByteString] -> [ByteString] -> Either String ()
forall {a} {a}. Eq a => a -> a -> a -> Either a ()
unlessEq
        String
"witness is not exactly the fee owner"
        (CompleteEvidence -> [ByteString]
ceWitnesses CompleteEvidence
ev)
        [CompleteEvidence -> ByteString
ceFeeOwner CompleteEvidence
ev]
    String -> [ByteString] -> [ByteString] -> Either String ()
forall {t :: * -> *} {t :: * -> *} {a} {a}.
(Foldable t, Foldable t, Eq a) =>
a -> t a -> t a -> Either a ()
unlessAbsent
        String
"route key witnessed the completion"
        (CompleteEvidence -> [ByteString]
ceWitnesses CompleteEvidence
ev)
        (CompleteEvidence -> [ByteString]
ceRouteKeys CompleteEvidence
ev)
  where
    unlessEq :: a -> a -> a -> Either a ()
unlessEq a
_ a
a a
b | a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
b = () -> Either a ()
forall a b. b -> Either a b
Right ()
    unlessEq a
msg a
_ a
_ = a -> Either a ()
forall a b. a -> Either a b
Left a
msg
    unlessNeq :: a -> a -> a -> Either a ()
unlessNeq a
_ a
a a
b | a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
b = () -> Either a ()
forall a b. b -> Either a b
Right ()
    unlessNeq a
msg a
_ a
_ = a -> Either a ()
forall a b. a -> Either a b
Left a
msg
    unlessOk :: a -> Bool -> Either a ()
unlessOk a
_ Bool
True = () -> Either a ()
forall a b. b -> Either a b
Right ()
    unlessOk a
msg Bool
_ = a -> Either a ()
forall a b. a -> Either a b
Left a
msg
    unlessAbsent :: a -> t a -> t a -> Either a ()
unlessAbsent a
_ t a
xs t a
ys | (a -> Bool) -> t a -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (a -> t a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` t a
ys) t a
xs = () -> Either a ()
forall a b. b -> Either a b
Right ()
    unlessAbsent a
msg t a
_ t a
_ = a -> Either a ()
forall a b. a -> Either a b
Left a
msg