ron-hs-0.4.0: src/Data/Ron/Class.hs
{-# LANGUAGE DefaultSignatures, FlexibleContexts, FlexibleInstances #-}
{-# LANGUAGE EmptyCase #-}
-- | Definition for ron conversion classes, and for using 'GHC.Generics' to
-- encode any datatype into RON.
module Data.Ron.Class
( ToRon (..), FromRon (..)
, ParseResult
-- * Settings for generic encoding
, RonSettings (..)
, RonFlags (..)
, strictRonSettings, laxRonSettings
-- * Generic encoding
, toRonGeneric
, fromRonGeneric
, GToRon, GFromRon
) where
import Control.Applicative (liftA2)
import Control.Arrow ((***))
import Data.ByteString (ByteString)
import Data.Complex (Complex ((:+)))
import Data.Foldable (toList)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Map.Strict (Map)
import Data.Proxy (Proxy (..))
import Data.Ron.Class.Internal (productSize, ProductSize)
import Data.Scientific (fromFloatDigits, toRealFloat, Scientific)
import Data.Text (Text, pack, unpack)
import Data.Text.Encoding (encodeUtf8)
import Data.Vector (Vector)
import Data.Word (Word8, Word16, Word32, Word64)
import GHC.Generics
( Generic (Rep, from, to), V1, U1 (..), (:+:)(..), (:*:)(..)
, K1 (..), M1 (..)
, C, S, D, R
, Constructor (conName, conIsRecord), Selector (selName), Datatype (datatypeName)
)
import qualified Data.ByteString as BS
import qualified Data.Map.Strict as Map
import qualified Data.Sequence as Seq
import qualified Data.Set as Set
import qualified Data.Vector as Vector
import Data.Ron.Value
import Prelude hiding (fail)
-- | When decoding from ron, this type is used to indicate decode failure.
-- During decoding, multiple failures may be concatenated with a semicolon, and
-- for some functions the string will be thrown with
-- 'Data.Ron.Deserialize.DecodeError'. When implementing your own decoding
-- functions, you should put short one-sentence error descriptions.
--
-- For the next major release, we plan to replace it with a better mechanism
-- that reports error locations and supports long-form content.
type ParseResult = Either String
fail :: String -> ParseResult a
fail = Left
guard :: String -> Bool -> ParseResult ()
guard _ True = pure ()
guard msg False = fail msg
-- | Like 'Control.Applicative.(<|>)' but collect errors from both sides
(<<|>>) :: ParseResult a -> ParseResult a -> ParseResult a
pa <<|>> pb = case pa of
Right a -> pure a
Left ea -> case pb of
Right b -> pure b
Left eb -> fail $ ea <> "; "<> eb
-- | A class of values that can be encoded to RON format.
--
-- There are several ways to define an instance:
--
-- 1. By producing a 'Value' by hand
-- 2. By using 'toRonGeneric'
-- 3. By @DerivingVia@ extension and using 'Data.Ron.Class.Deriving.RonWith'
--
-- When using the second option, the encoding parameters are specified with
-- 'RonSettings'. With the third option, the same parameters are specified by a
-- list of settings found in 'Data.Ron.Class.Deriving'.
--
-- The default implementation uses generic encoding with 'laxRonSettings'. You
-- can use other settings like this:
--
-- @
-- instance ToRon MyType where
-- toRon = toRonGeneric strictRonSettings
-- { encodeFlags = RonFlags
-- { implicitSome = True
-- , skipSingleConstructor = True
-- }
-- }
-- @
--
-- Or like this:
--
-- @
-- deriving via (RonWith '[UseStrict, EncodeWith SkipSingleConstructor, EncodeWith ImplicitSome])
-- instance ToRon MyType
-- @
class ToRon a where
toRon :: a -> Value
default toRon :: (Generic a, GToRon (Rep a)) => a -> Value
toRon = toRonDefault
-- | A class of values that can be restored from RON format
--
-- There are several ways to define an instance:
--
-- 1. By deconstructing a 'Value' by hand and producing a value of your type
-- 2. By using 'fromRonGeneric'
-- 3. By @DerivingVia@ extension and using 'Data.Ron.Class.Deriving.RonWith'
--
-- The default implementation uses generic decoding with 'laxRonSettings'. You
-- can use other settings like this:
--
-- @
-- instance FromRon MyType where
-- fromRon = fromRonGeneric strictRonSettings
-- { decodeFlags = RonFlags
-- { implicitSome = True
-- , skipSingleConstructor = True
-- }
-- }
-- @
--
-- Or like this:
--
-- @
-- deriving via (RonWith '[UseStrict, DecodeWith SkipSingleConstructor, DecodeWith ImplicitSome])
-- instance FromRon MyType
-- @
class FromRon a where
fromRon :: Value -> ParseResult a
default fromRon :: (Generic a, GFromRon (Rep a)) => Value -> ParseResult a
fromRon = fromRonDefault
intInRange :: forall a. (Bounded a, Num a, Integral a) => Integer -> ParseResult a
intInRange i
| i >= minVal && i <= maxVal = pure . fromInteger $ i
| otherwise = fail $ "Value " <> show i <> " is not in valid range"
where
minVal = toInteger (minBound @a)
maxVal = toInteger (maxBound @a)
instance ToRon Int where
toRon = Integral . toInteger
instance FromRon Int where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Int8 where
toRon = Integral . toInteger
instance FromRon Int8 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Int16 where
toRon = Integral . toInteger
instance FromRon Int16 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Int32 where
toRon = Integral . toInteger
instance FromRon Int32 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Int64 where
toRon = Integral . toInteger
instance FromRon Int64 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Word where
toRon = Integral . toInteger
instance FromRon Word where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Word8 where
toRon = Integral . toInteger
instance FromRon Word8 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Word16 where
toRon = Integral . toInteger
instance FromRon Word16 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Word32 where
toRon = Integral . toInteger
instance FromRon Word32 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Word64 where
toRon = Integral . toInteger
instance FromRon Word64 where
fromRon (Integral i) = intInRange i
fromRon _ = fail "Not an integer"
instance ToRon Integer where
toRon = Integral
instance FromRon Integer where
fromRon (Integral i) = pure i
fromRon _ = fail "Not an integer"
instance ToRon Scientific where
toRon = Floating
instance FromRon Scientific where
fromRon (Integral x) = pure . fromInteger $ x
fromRon (Floating x) = pure x
fromRon _ = fail "Not a floating"
instance ToRon Double where
toRon x
| isNaN x = Unit "NaN"
| isInfinite x = Unit "inf"
| otherwise = Floating . fromFloatDigits $ x
instance FromRon Double where
fromRon (Integral x) = pure . fromInteger $ x
fromRon (Floating x) = pure . toRealFloat $ x
fromRon (Unit "inf") = pure $! read "Infinity"
fromRon (Unit "NaN") = pure $! read "NaN"
fromRon _ = fail "Not a floating"
instance ToRon Float where
toRon x
| isNaN x = Unit "NaN"
| isInfinite x = Unit "inf"
| otherwise = Floating . fromFloatDigits $ x
instance FromRon Float where
fromRon (Integral x) = pure . fromInteger $ x
fromRon (Floating x) = pure . toRealFloat $ x
fromRon (Unit "inf") = pure $! read "Infinity"
fromRon (Unit "NaN") = pure $! read "NaN"
fromRon _ = fail "Not a floating"
instance (ToRon a) => ToRon (Complex a) where
toRon (r :+ im) = Tuple "Complex" (Vector.fromList [toRon r, toRon im])
instance (Num a, FromRon a) => FromRon (Complex a) where
fromRon v@(Integral _) = (:+ 0) <$> fromRon v
fromRon v@(Floating _) = (:+ 0) <$> fromRon v
fromRon (Tuple name vals) = do
guard "Incorrect Complex tuple name" $ name == "" || name == "Complex"
case Vector.toList vals of
[r, im] -> liftA2 (:+) (fromRon r) (fromRon im)
_ -> fail "Incorrect amount of values in complex coordinates"
fromRon _ = fail "Incorrect Complex value"
instance ToRon Char where
toRon = Char
instance FromRon Char where
fromRon (Char x) = pure x
fromRon _ = fail "Expected Char"
instance ToRon Text where
toRon = String
instance FromRon Text where
fromRon (String x) = pure x
fromRon _ = fail "Not text"
instance ToRon ByteString where
toRon = List . Vector.fromList . map (Integral . toInteger) . BS.unpack
instance FromRon ByteString where
fromRon (List bytes) = fmap BS.pack . traverse asWord8 . Vector.toList $ bytes
where
asWord8 (Integral x) = intInRange x
asWord8 _ = fail "Not a byte in list"
fromRon (String s) = pure . encodeUtf8 $ s
fromRon _ = fail "Not a bytestring"
instance ToRon Bool where
toRon True = Unit "true"
toRon False = Unit "false"
instance FromRon Bool where
fromRon (Unit name)
| name == "True" || name == "true" = pure True
| name == "False" || name == "false" = pure False
| otherwise = fail $ "Invalid enum value: " <> show name
fromRon _ = fail "Not a boolean"
instance (ToRon a) => ToRon (Vector.Vector a) where
toRon = List . Vector.map toRon
instance (FromRon a) => FromRon (Vector.Vector a) where
fromRon (List xs) = traverse fromRon xs
fromRon _ = fail "Not a list"
instance (ToRon a) => ToRon [a] where
toRon = List . Vector.fromList . map toRon
instance (FromRon a) => FromRon [a] where
fromRon (List xs) = Vector.toList <$> traverse fromRon xs
fromRon _ = fail "Not a list"
instance {-# OVERLAPPING #-} ToRon [Char] where
toRon = String . pack
instance {-# OVERLAPPING #-} FromRon [Char] where
fromRon = fmap unpack . fromRon
instance (ToRon a) => ToRon (NonEmpty a) where
toRon (x:|xs) = toRon $ x:xs
instance (FromRon a) => FromRon (NonEmpty a) where
fromRon v = fromRon v >>= \case
[] -> fail "Expected non-empty list"
x:xs -> pure $ x:|xs
instance (ToRon k, ToRon v) => ToRon (Map.Map k v) where
toRon = Map . Map.fromList . map (toRon *** toRon) . Map.toAscList
instance (FromRon k, FromRon v, Ord k) => FromRon (Map.Map k v) where
fromRon (Map xs) = Map.fromList <$>
traverse tupleFromRon (Map.toAscList xs)
where tupleFromRon (a, b) = liftA2 (,) (fromRon a) (fromRon b)
fromRon _ = fail "Not a map"
instance (ToRon a) => ToRon (Set.Set a) where
toRon = toRon . Set.toAscList
instance (FromRon a, Ord a) => FromRon (Set.Set a) where
fromRon = fmap Set.fromList . fromRon
instance (ToRon a) => ToRon (Seq.Seq a) where
toRon = toRon . toList
instance (FromRon a) => FromRon (Seq.Seq a) where
fromRon = fmap Seq.fromList . fromRon
instance ToRon () where
toRon () = Unit ""
instance FromRon () where
fromRon (Unit name)
| name == "" = pure ()
| otherwise = fail "Not a unit enum value"
fromRon _ = fail "Not a unit"
instance (ToRon a) => ToRon (Maybe a) where
toRon (Just x) = Tuple "Some" (Vector.singleton $ toRon x)
toRon Nothing = Unit "None"
instance (FromRon a) => FromRon (Maybe a) where
fromRon (Tuple name xs)
| (name == "Just" || name == "Some") && Vector.length xs == 1
= Just <$> fromRon (Vector.unsafeHead xs)
| otherwise = fail "Not a Maybe enum"
fromRon (Unit name)
| name == "Nothing" || name == "None" = pure Nothing
| otherwise = fail $ "Incorrect Maybe name: " <> show name
fromRon _ = fail "Not a named tuple or unit"
instance (ToRon a, ToRon b) => ToRon (Either a b) where
toRon (Left x) = Tuple "Left" (Vector.singleton $ toRon x)
toRon (Right x) = Tuple "Right" (Vector.singleton $ toRon x)
instance (FromRon a, FromRon b) => FromRon (Either a b) where
fromRon (Tuple name xs)
| Vector.length xs == 1 =
if | name == "Left" -> Left <$> fromRon (Vector.unsafeHead xs)
| name == "Right" -> Right <$> fromRon (Vector.unsafeHead xs)
| otherwise -> fail "Not an Either enum"
| otherwise = fail "Incorrect amount of arguments to Either"
fromRon _ = fail "Not an Either value"
instance (ToRon a1, ToRon a2) => ToRon (a1, a2) where
toRon (a1, a2) = Tuple "" . Vector.fromList $ [toRon a1, toRon a2]
instance (FromRon a1, FromRon a2) => FromRon (a1, a2) where
fromRon (Tuple name xs)
| name == "" = case Vector.toList xs of
[a1, a2] -> liftA2 (,) (fromRon a1) (fromRon a2)
_ -> fail "Incorrect amount of tuple values"
| otherwise = fail "Incorrect tuple name"
fromRon _ = fail "Not a tuple"
--- Generic instance
-- | Part of 'RonSettings' that applies to both encoding and decoding, and
-- separately
data RonFlags = RonFlags
{ implicitSome :: !Bool
-- ^ Like ron-rs's @implicit_some@. When set to True, 'Nothing' in record
-- fields is represented by omission of the field
, skipSingleConstructor :: !Bool
-- ^ When a datatype has a single constructor, encoding will omit it and
-- decoding will ignore it missing, turning the representation into a tuple
-- or an anonymous record.
} deriving (Eq, Show)
-- | Settings for use with 'Generic' RON encoding/decoding
data RonSettings = RonSettings
{ fieldModifier :: !(String -> String)
-- ^ Every field will be renamed using the supplied function
, constructorModifier :: !(String -> String)
-- ^ Every constructor will be renamed using the supplied function
, decodeFlags :: !RonFlags
-- ^ Options that apply to encoding separately from decoding
, encodeFlags :: !RonFlags
-- ^ Options that apply to decoding separately from encoding
}
data SumContext = SumContext
{ isSingleConstructor :: Bool
, sumTypeName :: String
} deriving (Eq, Show)
data ProductContext = ProductContext
{ prodTypeName :: String
, constructorName :: String
} deriving (Eq, Show)
-- | Encode ron using 'Generic' instance and provided 'RonSettings'.
--
-- With generic encoding sums are turned into sums, records into records, and
-- multi-param constructors into tuples.
toRonGeneric :: (Generic a, GToRon (Rep a)) => RonSettings -> a -> Value
toRonGeneric conf = toRonG conf . from
-- | Values are expected to exactly conform: all fields should have the same
-- name, all constructors should be present, no @Some@ omission
strictRonSettings :: RonSettings
strictRonSettings = RonSettings
{ fieldModifier = id
, constructorModifier = id
, decodeFlags = RonFlags
{ implicitSome = False
, skipSingleConstructor = False
}
, encodeFlags = RonFlags
{ implicitSome = False
, skipSingleConstructor = False
}
}
-- | Relaxes 'strictRonSettings' on constructor omission and implicitSome when
-- decoding, but encodes in the same strict way
laxRonSettings :: RonSettings
laxRonSettings = RonSettings
{ fieldModifier = id
, constructorModifier = id
, decodeFlags = RonFlags
{ implicitSome = True
, skipSingleConstructor = True
}
, encodeFlags = RonFlags
{ implicitSome = False
, skipSingleConstructor = False
}
}
toRonDefault :: (Generic a, GToRon (Rep a)) => a -> Value
toRonDefault = toRonGeneric laxRonSettings
-- | Internal class for converting to Ron. You might need it if you're writing
-- you own generic combinators
class GToRon f where
toRonG :: RonSettings -> f a -> Value
class GToRonSum f where
toRonSum :: RonSettings -> SumContext -> f a -> Value
class GToRonProduct f where
toRonProduct
:: RonSettings -> ProductContext -> f a -> Either (Vector Value) (Map Text Value)
class GToRonRec f where
toRonRec :: RonSettings -> f a -> Value
instance (Datatype d, GToRonSum f) => GToRon (M1 D d f) where
toRonG conf (M1 x) = toRonSum conf cont x
where cont = SumContext
{ isSingleConstructor = True
, sumTypeName = datatypeName (undefined :: t d f a)
}
instance GToRonSum V1 where
toRonSum _ _ x = case x of {}
instance (Constructor c, GToRonProduct f) => GToRonSum (M1 C c f) where
toRonSum conf cont (M1 x) =
let con = undefined :: t c f a
name = if isSingleConstructor cont
&& skipSingleConstructor (encodeFlags conf)
then ""
else pack . conName $ con
cont' = ProductContext
{ prodTypeName = sumTypeName cont
, constructorName = unpack name
}
xs = toRonProduct conf cont' x
in case (xs, conIsRecord con) of
(Right xs', True) -> Record name xs'
(Left xs', False) -> Tuple name xs'
(Right xs', _) | null xs' -> Unit name
(Left xs', _) | Vector.null xs' -> Unit name
_ -> error $ "Bad product: " <> take 128 (show xs)
instance (GToRonSum fl, GToRonSum fr) => GToRonSum (fl :+: fr) where
toRonSum conf cont (L1 x) = toRonSum conf cont' x
where cont' = cont
{isSingleConstructor = False}
toRonSum conf cont (R1 x) = toRonSum conf cont' x
where cont' = cont
{isSingleConstructor = False}
instance GToRonProduct U1 where
toRonProduct _ _ U1 = Left Vector.empty
instance {-# OVERLAPPING #-} (Selector s, ToRon c)
=> GToRonProduct (M1 S s (K1 R (Maybe c))) where
toRonProduct conf _cont (M1 (K1 x)) =
let field = selName (undefined :: t s (K1 R (Maybe c)) a)
in case x of
Nothing | implicitSome . encodeFlags $ conf ->
if null field
then Left . Vector.singleton . toRon $ Nothing @()
else Right Map.empty
Just x' | implicitSome . encodeFlags $ conf ->
if null field
then Left . Vector.singleton . toRon $ x'
else
let field' = pack $ fieldModifier conf field
value = toRon x'
in Right $ Map.singleton field' value
x'
| null field -> Left . Vector.singleton . toRon $ x'
| otherwise ->
let field' = pack $ fieldModifier conf field
in Right . Map.singleton field' $ toRon x'
instance (Selector s, GToRonRec f) => GToRonProduct (M1 S s f) where
toRonProduct conf _cont (M1 x) =
let field = selName (undefined :: t s f a)
value = toRonRec conf x
in case field of
"" -> Left . Vector.singleton $ value
_field ->
let field' = pack $ fieldModifier conf field
in Right $ Map.singleton field' value
instance (GToRonProduct pl, GToRonProduct pr)
=> GToRonProduct (pl :*: pr) where
toRonProduct conf cont (x :*: y) =
case (toRonProduct conf cont x, toRonProduct conf cont y) of
(Left xs, Left ys) -> Left $ xs <> ys
(Right xs, Right ys) -> Right $ Map.union xs ys
_ -> error "Incompatible product branches"
instance ToRon c => GToRonRec (K1 R c) where
toRonRec _ (K1 x) = toRon x
-- | Decode ron using 'Generic' instance and provided 'RonSettings'
--
-- With generic encoding sums are turned into sums, records into records, and
-- multi-param constructors into tuples.
fromRonGeneric
:: (Generic a, GFromRon (Rep a)) => RonSettings -> Value -> ParseResult a
fromRonGeneric conf = fmap to . fromRonG conf
fromRonDefault :: (Generic a, GFromRon (Rep a)) => Value -> ParseResult a
fromRonDefault = fromRonGeneric laxRonSettings
-- | Internal class for converting from Ron. You might need it if you're
-- writing you own generic combinators
class GFromRon f where
fromRonG :: RonSettings -> Value -> ParseResult (f a)
class GFromRonSum f where
fromRonSum :: RonSettings -> SumContext -> Value -> ParseResult (f a)
class GFromRonProduct f where
fromRonProduct
:: RonSettings
-> ProductContext
-> Either (Vector Value) (Map Text Value)
-> ParseResult (f a)
class GFromRonRec f where
fromRonRec :: RonSettings -> Value -> ParseResult (f a)
instance (Datatype d, GFromRonSum f) => GFromRon (M1 D d f) where
fromRonG conf x = M1 <$> fromRonSum conf cont x
where cont = SumContext
{ isSingleConstructor = True
, sumTypeName = datatypeName (undefined :: t d f a)
}
instance (Constructor c, GFromRonProduct f) => GFromRonSum (M1 C c f) where
fromRonSum conf cont x =
M1 <$> case x of
Unit n | name `matches` n
-> fromRonProduct conf cont' $ Left Vector.empty
| otherwise -> fail incorrectConstructor
Tuple n xs | name `matches` n -> fromRonProduct conf cont' $ Left xs
| otherwise -> fail incorrectConstructor
Record n xs | name `matches` n -> fromRonProduct conf cont' $ Right xs
| otherwise -> fail incorrectConstructor
_ -> fail $ "Incorrect value for type " <> sumTypeName cont
where
con = undefined :: t c f a
name = pack . conName $ con
matches target ron =
ron == target
|| ron == ""
&& isSingleConstructor cont
&& skipSingleConstructor (decodeFlags conf)
incorrectConstructor = "Incorrect constructor " <> constructorName <> " for type " <> sumTypeName cont
constructorName = case unpack name of
"" -> "<UNNAMED>"
n -> n
cont' = ProductContext
{ prodTypeName = sumTypeName cont
, constructorName
}
instance (GFromRonSum fl, GFromRonSum fr) => GFromRonSum (fl :+: fr) where
fromRonSum conf cont x =
(L1 <$> fromRonSum conf cont' x) <<|>> (R1 <$> fromRonSum conf cont' x)
where cont' = cont
{ isSingleConstructor = False }
instance GFromRonProduct U1 where
fromRonProduct _ _ (Left xs) | Vector.null xs = pure U1
fromRonProduct _ _ (Right xs) | Map.null xs = pure U1
fromRonProduct _ c _ = fail $ constructorName c <> ": expected empty structure"
instance {-# OVERLAPPING #-} (Selector s, FromRon c)
=> GFromRonProduct (M1 S s (K1 R (Maybe c))) where
fromRonProduct conf cont xs =
let field =
pack . fieldModifier conf $
selName (undefined :: t s (K1 R (Maybe c)) a)
ProductContext {constructorName} = cont
in case xs of
Left xs' -> case Vector.uncons xs' of
Nothing -> fail $ "Not enough elements in tuple " <> constructorName
Just (x, xs'')
| Vector.null xs'' -> M1 <$>
let tryUnwrapped
| implicitSome . decodeFlags $ conf =
K1 . Just <$> fromRon x
| otherwise =
fail $ constructorName <> ": not using decodeImplicitSome"
in fromRonRec conf x <<|>> tryUnwrapped
| otherwise -> fail $ "Trailing members in tuple " <> constructorName
Right xs'
| implicitSome . decodeFlags $ conf ->
case Map.lookup field xs' of
Nothing -> pure . M1 . K1 $ Nothing
Just x ->
let unwrapped = M1 . K1 . Just <$> fromRon x
wrapped = M1 <$> fromRonRec conf x
in wrapped <<|>> unwrapped
| otherwise -> case Map.lookup field xs' of
Nothing -> fail $ "Field " <> unpack field <> " not present in record " <> constructorName
Just x -> M1 <$> fromRonRec conf x
instance (Selector s, GFromRonRec f) => GFromRonProduct (M1 S s f) where
fromRonProduct conf cont xs =
let field = pack . fieldModifier conf $ selName (undefined :: t s f a)
ProductContext {constructorName} = cont
in case xs of
Left xs' -> case Vector.uncons xs' of
Nothing -> fail $ "Not enough elements in tuple " <> constructorName
Just (x, xs'') | Vector.null xs'' -> M1 <$> fromRonRec conf x
| otherwise -> fail $ "Trailing members in tuple " <> constructorName
Right xs' -> case Map.lookup field xs' of
Nothing -> fail $ "Field " <> unpack field <> " not present in record " <> constructorName
Just x -> M1 <$> fromRonRec conf x
instance (ProductSize pl, GFromRonProduct pl, GFromRonProduct pr)
=> GFromRonProduct (pl :*: pr) where
fromRonProduct conf cont (Left xs) =
let sizel = productSize (Proxy @pl)
(xsl, xsr) = Vector.splitAt sizel xs
in (:*:)
<$> fromRonProduct conf cont (Left xsl)
<*> fromRonProduct conf cont (Left xsr)
fromRonProduct conf cont xs = (:*:)
<$> fromRonProduct conf cont xs
<*> fromRonProduct conf cont xs
instance FromRon c => GFromRonRec (K1 R c) where
fromRonRec _ x = K1 <$> fromRon x