{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

-- |
-- Copyright: © 2020-2021 IOHK
-- License: Apache-2.0
--
-- Conversion functions which don't have error handling. These are quite
-- convenient to use for jobs like testing, debugging, etc.
--
-- But these "unsafe" functions should not be used in application code, unless
-- it's certain that the error case will never happen.

module Cardano.Wallet.Unsafe
    ( unsafeRight
    , unsafeFromHex
    , unsafeFromHexText
    , unsafeFromBase64
    , unsafeFromHexFile
    , unsafeDecodeAddress
    , unsafeDecodeHex
    , unsafeFromText
    , unsafeRunExceptT
    , unsafeXPrv
    , unsafeXPub
    , unsafeDeserialiseCbor
    , unsafeBech32DecodeFile
    , unsafeBech32Decode
    , unsafeMkPercentage
    , unsafeIntToWord

    , someDummyMnemonic
    , unsafeMkMnemonic
    , unsafeMkEntropy
    , unsafeMkSomeMnemonicFromEntropy
    ) where

import Prelude

import Cardano.Crypto.Wallet
    ( XPrv, XPub )
import Cardano.Mnemonic
    ( ConsistentEntropy
    , Entropy
    , EntropySize
    , Mnemonic
    , MnemonicWords
    , SomeMnemonic (..)
    , ValidChecksumSize
    , ValidEntropySize
    , ValidMnemonicSentence
    , entropyToMnemonic
    , mkEntropy
    , mkMnemonic
    )
import Cardano.Wallet.Api.Types
    ( DecodeAddress (..) )
import Cardano.Wallet.Primitive.Types.Address
    ( Address )
import Cardano.Wallet.Util
    ( internalError )
import Control.Monad
    ( (>=>) )
import Control.Monad.Trans.Except
    ( ExceptT (..), runExceptT )
import Data.Binary.Get
    ( Get, runGet )
import Data.ByteArray
    ( ByteArray )
import Data.ByteArray.Encoding
    ( Base (..), convertFromBase )
import Data.ByteString
    ( ByteString )
import Data.Char
    ( isHexDigit )
import Data.Either
    ( fromRight )
import Data.Proxy
    ( Proxy (..) )
import Data.Quantity
    ( Percentage, mkPercentage )
import Data.Text
    ( Text )
import Data.Text.Class
    ( FromText (..) )
import Data.Typeable
    ( Typeable, typeRep )
import Fmt
    ( Buildable, Builder, build, (+||), (|+), (||+) )
import GHC.Stack
    ( HasCallStack )
import GHC.TypeLits
    ( natVal )

import qualified Cardano.Crypto.Wallet as CC
import qualified Codec.Binary.Bech32 as Bech32
import qualified Codec.CBOR.Decoding as CBOR
import qualified Codec.CBOR.Read as CBOR
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.IO as TIO

-- | Take the right side of an 'Either' value. Crash badly if it was a left.
unsafeRight :: (Buildable e, HasCallStack) => Either e a -> a
unsafeRight :: Either e a -> a
unsafeRight = (e -> a) -> (a -> a) -> Either e a -> a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Builder -> a
forall a. HasCallStack => Builder -> a
internalError (Builder -> a) -> (e -> Builder) -> e -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Builder
forall p. Buildable p => p -> Builder
build) a -> a
forall a. a -> a
id

-- | Decode an hex-encoded 'ByteString' into raw bytes, or fail.
unsafeFromHex :: forall b. (HasCallStack, ByteArray b) => ByteString -> b
unsafeFromHex :: ByteString -> b
unsafeFromHex = Either String b -> b
forall e a. (Buildable e, HasCallStack) => Either e a -> a
unsafeRight (Either String b -> b)
-> (ByteString -> Either String b) -> ByteString -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Base -> ByteString -> Either String b
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
Base -> bin -> Either String bout
convertFromBase @ByteString @b Base
Base16

-- | Decode hex-encoded 'Text' into a 'ByteString', or fail. This variant of
-- 'unsafeFromHex' may be easier to use because it's not polymorphic.
unsafeFromHexText :: HasCallStack => Text -> ByteString
unsafeFromHexText :: Text -> ByteString
unsafeFromHexText = ByteString -> ByteString
forall b. (HasCallStack, ByteArray b) => ByteString -> b
unsafeFromHex (ByteString -> ByteString)
-> (Text -> ByteString) -> Text -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
T.encodeUtf8

-- | Decode a base64-encoded 'ByteString' into raw bytes, or fail.
unsafeFromBase64 :: HasCallStack => ByteString -> ByteString
unsafeFromBase64 :: ByteString -> ByteString
unsafeFromBase64 = Either String ByteString -> ByteString
forall e a. (Buildable e, HasCallStack) => Either e a -> a
unsafeRight (Either String ByteString -> ByteString)
-> (ByteString -> Either String ByteString)
-> ByteString
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Base -> ByteString -> Either String ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
Base -> bin -> Either String bout
convertFromBase @ByteString @ByteString Base
Base64

-- | Load a hex string from file. Any non-hexadecimal characters are ignored.
unsafeFromHexFile :: HasCallStack => FilePath -> IO ByteString
unsafeFromHexFile :: String -> IO ByteString
unsafeFromHexFile = (ByteString -> ByteString) -> IO ByteString -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ByteString -> ByteString
forall b. (HasCallStack, ByteArray b) => ByteString -> b
unsafeFromHex (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ByteString -> ByteString
B8.filter Char -> Bool
isHexDigit) (IO ByteString -> IO ByteString)
-> (String -> IO ByteString) -> String -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO ByteString
B8.readFile

-- | Decode a bech32-encoded 'Text' into an 'Address', or fail.
unsafeDecodeAddress
    :: forall n. (HasCallStack, DecodeAddress n)
    => Text
    -> Address
unsafeDecodeAddress :: Text -> Address
unsafeDecodeAddress = Either TextDecodingError Address -> Address
forall e a. (Buildable e, HasCallStack) => Either e a -> a
unsafeRight (Either TextDecodingError Address -> Address)
-> (Text -> Either TextDecodingError Address) -> Text -> Address
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DecodeAddress n => Text -> Either TextDecodingError Address
forall (n :: NetworkDiscriminant).
DecodeAddress n =>
Text -> Either TextDecodingError Address
decodeAddress @n

-- | Run a decoder on a hex-encoded 'ByteString', or fail.
unsafeDecodeHex :: HasCallStack => Get a -> ByteString -> a
unsafeDecodeHex :: Get a -> ByteString -> a
unsafeDecodeHex Get a
get = Get a -> ByteString -> a
forall a. Get a -> ByteString -> a
runGet Get a
get (ByteString -> a) -> (ByteString -> ByteString) -> ByteString -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
BL.fromStrict (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
forall b. (HasCallStack, ByteArray b) => ByteString -> b
unsafeFromHex

-- | Decode the given data-type from a textual representation, or fail.
unsafeFromText :: (FromText a, HasCallStack) => Text -> a
unsafeFromText :: Text -> a
unsafeFromText = Either TextDecodingError a -> a
forall e a. (Buildable e, HasCallStack) => Either e a -> a
unsafeRight (Either TextDecodingError a -> a)
-> (Text -> Either TextDecodingError a) -> Text -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either TextDecodingError a
forall a. FromText a => Text -> Either TextDecodingError a
fromText

-- | Build a 'XPrv' from a bytestring
unsafeXPrv :: HasCallStack => ByteString -> XPrv
unsafeXPrv :: ByteString -> XPrv
unsafeXPrv ByteString
bytes =
    case ByteString -> Either String XPrv
forall bin. ByteArrayAccess bin => bin -> Either String XPrv
CC.xprv ByteString
bytes of
        Left String
e -> String -> XPrv
forall a. HasCallStack => String -> a
error (String -> XPrv) -> String -> XPrv
forall a b. (a -> b) -> a -> b
$ String
"unsafeXPrv: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
e
        Right XPrv
a -> XPrv
a

-- | Build a 'XPub' from a bytestring
unsafeXPub :: HasCallStack => ByteString -> XPub
unsafeXPub :: ByteString -> XPub
unsafeXPub ByteString
bytes =
    case ByteString -> Either String XPub
CC.xpub ByteString
bytes of
        Left String
e -> String -> XPub
forall a. HasCallStack => String -> a
error (String -> XPub) -> String -> XPub
forall a b. (a -> b) -> a -> b
$ String
"unsafeXPub: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
e
        Right XPub
a -> XPub
a

-- | Build 'Mnemonic' from literals
unsafeMkMnemonic
    :: forall mw n csz
    .  (ConsistentEntropy n mw csz, EntropySize mw ~ n, HasCallStack)
    => [Text]
    -> Mnemonic mw
unsafeMkMnemonic :: [Text] -> Mnemonic mw
unsafeMkMnemonic [Text]
m =
    case [Text] -> Either (MkMnemonicError csz) (Mnemonic mw)
forall (mw :: Nat) (ent :: Nat) (csz :: Nat).
(ConsistentEntropy ent mw csz, EntropySize mw ~ ent) =>
[Text] -> Either (MkMnemonicError csz) (Mnemonic mw)
mkMnemonic [Text]
m of
        Left MkMnemonicError csz
e -> String -> Mnemonic mw
forall a. HasCallStack => String -> a
error (String -> Mnemonic mw) -> String -> Mnemonic mw
forall a b. (a -> b) -> a -> b
$ String
"unsafeMnemonic: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> MkMnemonicError csz -> String
forall a. Show a => a -> String
show MkMnemonicError csz
e
        Right Mnemonic mw
a -> Mnemonic mw
a

-- | Run an 'ExceptT' and throws the error if any. This makes sense only if
-- called after checking for an invariant or, after ensuring that preconditions
-- for meeting the underlying error have been discarded.
unsafeRunExceptT :: (MonadFail m, Show e) => ExceptT e m a -> m a
unsafeRunExceptT :: ExceptT e m a -> m a
unsafeRunExceptT = ExceptT e m a -> m (Either e a)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT e m a -> m (Either e a))
-> (Either e a -> m a) -> ExceptT e m a -> m a
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> \case
    Left e
e ->
        String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> m a) -> String -> m a
forall a b. (a -> b) -> a -> b
$ String
"unexpected error: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> e -> String
forall a. Show a => a -> String
show e
e
    Right a
a ->
        a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return a
a

-- | CBOR deserialise without error handling - handy for prototypes or testing.
unsafeDeserialiseCbor
    :: HasCallStack
    => (forall s. CBOR.Decoder s a)
    -> BL.ByteString
    -> a
unsafeDeserialiseCbor :: (forall s. Decoder s a) -> ByteString -> a
unsafeDeserialiseCbor forall s. Decoder s a
decoder ByteString
bytes = (DeserialiseFailure -> a)
-> ((ByteString, a) -> a)
-> Either DeserialiseFailure (ByteString, a)
-> a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either
    (\DeserialiseFailure
e -> String -> a
forall a. HasCallStack => String -> a
error (String -> a) -> String -> a
forall a b. (a -> b) -> a -> b
$ String
"unsafeSerializeCbor: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> DeserialiseFailure -> String
forall a. Show a => a -> String
show DeserialiseFailure
e)
    (ByteString, a) -> a
forall a b. (a, b) -> b
snd
    ((forall s. Decoder s a)
-> ByteString -> Either DeserialiseFailure (ByteString, a)
forall a.
(forall s. Decoder s a)
-> ByteString -> Either DeserialiseFailure (ByteString, a)
CBOR.deserialiseFromBytes forall s. Decoder s a
decoder ByteString
bytes)

unsafeMkEntropy
    :: forall ent csz.
        ( HasCallStack
        , ValidEntropySize ent
        , ValidChecksumSize ent csz
        )
    => ByteString
    -> Entropy ent
unsafeMkEntropy :: ByteString -> Entropy ent
unsafeMkEntropy = (EntropyError csz -> Entropy ent)
-> (Entropy ent -> Entropy ent)
-> Either (EntropyError csz) (Entropy ent)
-> Entropy ent
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> Entropy ent
forall a. HasCallStack => String -> a
error (String -> Entropy ent)
-> (EntropyError csz -> String) -> EntropyError csz -> Entropy ent
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EntropyError csz -> String
forall a. Show a => a -> String
show) Entropy ent -> Entropy ent
forall a. a -> a
id (Either (EntropyError csz) (Entropy ent) -> Entropy ent)
-> (ByteString -> Either (EntropyError csz) (Entropy ent))
-> ByteString
-> Entropy ent
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ScrubbedBytes -> Either (EntropyError csz) (Entropy ent)
forall (ent :: Nat) (csz :: Nat).
(ValidEntropySize ent, ValidChecksumSize ent csz) =>
ScrubbedBytes -> Either (EntropyError csz) (Entropy ent)
mkEntropy (ScrubbedBytes -> Either (EntropyError csz) (Entropy ent))
-> (ByteString -> ScrubbedBytes)
-> ByteString
-> Either (EntropyError csz) (Entropy ent)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ScrubbedBytes
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
BA.convert

unsafeMkSomeMnemonicFromEntropy
    :: forall mw ent csz.
        ( HasCallStack
        , ValidEntropySize ent
        , ValidChecksumSize ent csz
        , ValidMnemonicSentence mw
        , ent ~ EntropySize mw
        , mw ~ MnemonicWords ent
        )
    => Proxy mw
    -> ByteString
    -> SomeMnemonic
unsafeMkSomeMnemonicFromEntropy :: Proxy mw -> ByteString -> SomeMnemonic
unsafeMkSomeMnemonicFromEntropy Proxy mw
_ = Mnemonic mw -> SomeMnemonic
forall (mw :: Nat). KnownNat mw => Mnemonic mw -> SomeMnemonic
SomeMnemonic
    (Mnemonic mw -> SomeMnemonic)
-> (ByteString -> Mnemonic mw) -> ByteString -> SomeMnemonic
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Entropy ent -> Mnemonic mw
forall (mw :: Nat) (ent :: Nat) (csz :: Nat).
(ValidMnemonicSentence mw, ValidEntropySize ent,
 ValidChecksumSize ent csz, ent ~ EntropySize mw,
 mw ~ MnemonicWords ent) =>
Entropy ent -> Mnemonic mw
entropyToMnemonic
    (Entropy ent -> Mnemonic mw)
-> (ByteString -> Entropy ent) -> ByteString -> Mnemonic mw
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (csz :: Nat).
(HasCallStack, ValidEntropySize ent, ValidChecksumSize ent csz) =>
ByteString -> Entropy ent
forall (ent :: Nat) (csz :: Nat).
(HasCallStack, ValidEntropySize ent, ValidChecksumSize ent csz) =>
ByteString -> Entropy ent
unsafeMkEntropy @ent

-- | A dummy @SomeMnemonic@ for testing.
--
-- Could have been named @dummySomeMnemonic@, but this way it sounds more like
-- valid english.
someDummyMnemonic
    :: forall mw ent csz.
        ( HasCallStack
        , ValidEntropySize ent
        , ValidChecksumSize ent csz
        , ValidMnemonicSentence mw
        , ent ~ EntropySize mw
        , mw ~ MnemonicWords ent
        )
    => Proxy mw
    -> SomeMnemonic
someDummyMnemonic :: Proxy mw -> SomeMnemonic
someDummyMnemonic Proxy mw
proxy =
    let
        n :: Int
n = Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Int) -> Integer -> Int
forall a b. (a -> b) -> a -> b
$ Proxy ent -> Integer
forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal (Proxy ent
forall k (t :: k). Proxy t
Proxy @ent) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
8
        entropy :: ByteString
entropy = Int -> Word8 -> ByteString
BS.replicate Int
n Word8
0
    in
        Proxy mw -> ByteString -> SomeMnemonic
forall (mw :: Nat) (ent :: Nat) (csz :: Nat).
(HasCallStack, ValidEntropySize ent, ValidChecksumSize ent csz,
 ValidMnemonicSentence mw, ent ~ EntropySize mw,
 mw ~ MnemonicWords ent) =>
Proxy mw -> ByteString -> SomeMnemonic
unsafeMkSomeMnemonicFromEntropy Proxy mw
proxy ByteString
entropy

-- | Load the data part of a bech32-encoded string from file. These files often
-- come from @jcli@. Only the first line of the file is read.
unsafeBech32DecodeFile :: HasCallStack => FilePath -> IO BL.ByteString
unsafeBech32DecodeFile :: String -> IO ByteString
unsafeBech32DecodeFile = (Text -> ByteString) -> IO Text -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (HasCallStack => Text -> ByteString
Text -> ByteString
unsafeBech32Decode (Text -> ByteString) -> (Text -> Text) -> Text -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
firstLine) (IO Text -> IO ByteString)
-> (String -> IO Text) -> String -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO Text
TIO.readFile
  where
    firstLine :: Text -> Text
firstLine = (Char -> Bool) -> Text -> Text
T.takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\n')

-- | Get the data part of a bech32-encoded string, ignoring the human-readable part.
unsafeBech32Decode :: HasCallStack => Text -> BL.ByteString
unsafeBech32Decode :: Text -> ByteString
unsafeBech32Decode Text
txt = case Text -> Either DecodingError (HumanReadablePart, DataPart)
Bech32.decodeLenient Text
txt of
    Right (HumanReadablePart
_hrp, DataPart
dp) -> ByteString
-> (ByteString -> ByteString) -> Maybe ByteString -> ByteString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> ByteString
bomb String
"missing data part")
        ByteString -> ByteString
BL.fromStrict (DataPart -> Maybe ByteString
Bech32.dataPartToBytes DataPart
dp)
    Left DecodingError
e -> String -> ByteString
bomb (DecodingError -> String
forall a. Show a => a -> String
show DecodingError
e)
  where
    bomb :: String -> ByteString
bomb String
msg = String -> ByteString
forall a. HasCallStack => String -> a
error (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ String
"Could not decode bech32 string " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
txt
        String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" because " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
msg

unsafeMkPercentage :: HasCallStack => Rational -> Percentage
unsafeMkPercentage :: Rational -> Percentage
unsafeMkPercentage Rational
r = Percentage -> Either MkPercentageError Percentage -> Percentage
forall b a. b -> Either a b -> b
fromRight Percentage
bomb (Either MkPercentageError Percentage -> Percentage)
-> Either MkPercentageError Percentage -> Percentage
forall a b. (a -> b) -> a -> b
$ Rational -> Either MkPercentageError Percentage
mkPercentage Rational
r
  where
    bomb :: Percentage
bomb = String -> Percentage
forall a. HasCallStack => String -> a
error (String -> Percentage) -> String -> Percentage
forall a b. (a -> b) -> a -> b
$ String
"unsafeMkPercentage: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Rational -> String
forall a. Show a => a -> String
show Rational
r String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" is out of bounds."

-- | Convert an integer type of any range to a machine word.
--
-- Only use it for values which have come from the ledger, and should fit in the
-- given type, according to the spec.
--
-- If this conversion would under/overflow, there is not much we can do except
-- to hastily exit.
unsafeIntToWord
    :: forall from to
     . ( HasCallStack
       , Integral from
       , Bounded to
       , Integral to
       , Typeable from
       , Typeable to
       , Show from)
    => from -> to
unsafeIntToWord :: from -> to
unsafeIntToWord from
n
    | from
n from -> from -> Bool
forall a. Ord a => a -> a -> Bool
< to -> from
forall a b. (Integral a, Num b) => a -> b
fromIntegral (to
forall a. Bounded a => a
minBound :: to) = Builder -> to
crash Builder
"underflow"
    | from
n from -> from -> Bool
forall a. Ord a => a -> a -> Bool
> to -> from
forall a b. (Integral a, Num b) => a -> b
fromIntegral (to
forall a. Bounded a => a
maxBound :: to) = Builder -> to
crash Builder
"overflow"
    | Bool
otherwise = from -> to
forall a b. (Integral a, Num b) => a -> b
fromIntegral from
n
  where
    crash :: Builder -> to
    crash :: Builder -> to
crash Builder
err = Builder -> to
forall a. HasCallStack => Builder -> a
internalError (Builder -> to) -> Builder -> to
forall a b. (a -> b) -> a -> b
$ Builder
err Builder -> Builder -> Builder
forall a b. (Buildable a, FromBuilder b) => a -> Builder -> b
|+Builder
" converting value "Builder -> Builder -> Builder
forall b. FromBuilder b => Builder -> Builder -> b
+|| from
n from -> Builder -> Builder
forall a b. (Show a, FromBuilder b) => a -> Builder -> b
||+
        Builder
" from " Builder -> Builder -> Builder
forall b. FromBuilder b => Builder -> Builder -> b
+|| Proxy from -> TypeRep
forall k (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (Proxy from
forall k (t :: k). Proxy t
Proxy @from) TypeRep -> Builder -> Builder
forall a b. (Show a, FromBuilder b) => a -> Builder -> b
||+
        Builder
" to "Builder -> Builder -> Builder
forall b. FromBuilder b => Builder -> Builder -> b
+|| Proxy to -> TypeRep
forall k (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (Proxy to
forall k (t :: k). Proxy t
Proxy @to) TypeRep -> Builder -> Builder
forall a b. (Show a, FromBuilder b) => a -> Builder -> b
||+Builder
"!"