Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ #206 ] Deriving FromField/ToField instances #207

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Manual: 2x slower
stevladimir committed Nov 3, 2022
commit d6c02e292dc961bfec23b074560d66d3cf5b9aa9
22 changes: 14 additions & 8 deletions src/Data/Csv/Conversion.hs
Original file line number Diff line number Diff line change
@@ -772,8 +772,9 @@ class FromField a where
genericParseField
:: forall a rep meta. (Generic a, GFromField rep, Rep a ~ D1 meta rep, Datatype meta)
=> Options -> Field -> Parser a
genericParseField opts field = fmap (to . M1) (gParseField opts field)
<|> fail ("Can't parseField for " <> datatypeName (Proxy :: Proxy meta d f))
genericParseField opts field = fmap (to . M1) (gParseField opts onFail field)
where
onFail _ = fail $ "Can't parseField of type " <> datatypeName (Proxy :: Proxy meta d f)
{-# INLINE genericParseField #-}

-- | A type that can be converted to a single CSV field.
@@ -1395,12 +1396,12 @@ instance (ToField a, Selector s) => GToRecord (M1 S s (K1 i a)) (B.ByteString, B
name = T.encodeUtf8 (T.pack (fieldLabelModifier opts (selName m)))

class GFromField f where
gParseField :: Options -> Field -> Parser (f p)
gParseField :: Options -> (Field -> Parser (f p)) -> Field -> Parser (f p)

-- Type with single nullary constructor
instance (Constructor c) => GFromField (C1 c U1) where
gParseField opts field = do
if field == expected then pure val else mempty
gParseField opts onFail field = do
if field == expected then pure val else onFail field
where
expected = encodeConstructor opts val
val :: C1 c U1 p
@@ -1409,13 +1410,18 @@ instance (Constructor c) => GFromField (C1 c U1) where

-- Type with single unary constructor
instance (FromField a) => GFromField (C1 c (S1 meta (K1 i a))) where
gParseField _ = fmap (M1 . M1 . K1) . parseField
gParseField _ onFail field =
fmap (M1 . M1 . K1) (parseField field) <|> onFail field
{-# INLINE gParseField #-}

-- Sum type
instance (GFromField c1, GFromField c2) => GFromField (c1 :+: c2) where
gParseField opts field =
fmap L1 (gParseField opts field) <|> fmap R1 (gParseField opts field)
gParseField opts onFail field =
case runParser $ gParseField opts mempty field of
Left _ -> case runParser $ gParseField opts mempty field of
Left _ -> onFail field
Right res -> pure $ R1 res
Right res -> pure $ L1 res
{-# INLINE gParseField #-}

class GToField f where
2 changes: 1 addition & 1 deletion tests/UnitTests.hs
Original file line number Diff line number Diff line change
@@ -504,7 +504,7 @@ genericFieldTests =
[ testCase "encoding" $ toField Foo @?= "Foo"
, testCase "decoding" $ runParser (parseField "Foo") @?= Right Foo ]
, testCase "decoding failure" $ runParser (parseField "foo")
@?= (Left "Expected \"Foo\"" :: Either String Foo)
@?= (Left "Can't parseField of type Foo" :: Either String Foo)
, testProperty "sum type roundtrip" (roundtripProp :: Bar -> Bool)
, testGroup "constructor modifier"
[ testCase "encoding" $ toField BazOne @?= "one"