{-# LANGUAGE DerivingStrategies #-} -- | Advanced errors. module Lorentz.Errors ( IsError , LorentzUserError , unLorentzUserError , UserFailInstr , userFailWith ) where import Data.Singletons (SingI) import Data.Vinyl.Derived (Label) import GHC.TypeLits (KnownSymbol, symbolVal) import Lorentz.ADT import Lorentz.Base import Lorentz.Coercions import Lorentz.Constraints import Lorentz.Instr import Lorentz.Value import Michelson.Text import Michelson.Typed.Haskell -- | Constraints on an object you can fail with. type IsError err = ( IsoValue err , KnownValue err , NoOperation err , NoBigMap err ) -- | A unique error identifier. newtype ErrorTag = ErrorTag MText deriving newtype (Show, Eq, Ord, IsString, IsoValue) -- | An error indicating a normal failure caused by such user input. type LorentzUserError e = (ErrorTag, e) -- | Pseudo-getter for error within 'LorentzUserError'. unLorentzUserError :: LorentzUserError e -> e unLorentzUserError = snd -- | Signature of 'userFailWith'. type UserFailInstr e name s s' = (InstrWrapC e name, KnownSymbol name) => Label name -> AppendCtorField (GetCtorField e name) s :-> s' -- | Fail with given error, picking argument for error from the top -- of the stack if any required. Error will be wrapped into 'LorentzUserError' -- (i.e. an error tag will be attached to the error data). -- -- Consider the following practice: once error datatype for your contract -- is defined, create a specialization of this function to the error type. userFailWith :: forall err name s s'. (Typeable (ToT err), SingI (ToT err)) => UserFailInstr err name s s' userFailWith label = wrap_ @err @_ @s label # push (mkMTextUnsafe . toText $ symbolVal (Proxy @name)) # pair # coerce_ @_ @(LorentzUserError err) # failWith