forked from haskell-to-elm/haskell-to-elm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDerivingVia.hs
108 lines (94 loc) · 3.64 KB
/
DerivingVia.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
{-# language DataKinds #-}
{-# language DeriveAnyClass #-}
{-# language DeriveGeneric #-}
{-# language DerivingVia #-}
{-# language FlexibleContexts #-}
{-# language KindSignatures #-}
{-# language MultiParamTypeClasses #-}
{-# language OverloadedStrings #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language UndecidableInstances #-}
module DerivingVia where
import qualified Data.Aeson as Aeson
import Data.Foldable
import qualified Data.HashMap.Lazy as HashMap
import Data.Proxy
import Data.String (fromString)
import qualified Data.Text as Text
import Data.Text (Text)
import qualified Generics.SOP as SOP
import GHC.Generics (Generic, Rep)
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import qualified Language.Elm.Name as Name
import qualified Language.Elm.Pretty as Pretty
import qualified Language.Elm.Simplification as Simplification
import Language.Haskell.To.Elm
-------------------------------------------------------------------------------
-- A type to derive via, which should typically only be defined once per project.
newtype ElmType (name :: Symbol) a
= ElmType a
instance
(Generic a, Aeson.GToJSON Aeson.Zero (Rep a)) =>
Aeson.ToJSON (ElmType name a)
where
toJSON (ElmType a) =
Aeson.genericToJSON Aeson.defaultOptions {Aeson.fieldLabelModifier = dropWhile (== '_')} a
instance
(Generic a, Aeson.GFromJSON Aeson.Zero (Rep a)) =>
Aeson.FromJSON (ElmType name a)
where
parseJSON =
fmap ElmType . Aeson.genericParseJSON Aeson.defaultOptions {Aeson.fieldLabelModifier = dropWhile (== '_')}
instance
(SOP.HasDatatypeInfo a, SOP.All2 HasElmType (SOP.Code a), KnownSymbol name) =>
HasElmType (ElmType name a)
where
elmDefinition =
Just
$ deriveElmTypeDefinition @a defaultOptions {fieldLabelModifier = dropWhile (== '_')}
$ fromString $ symbolVal $ Proxy @name
instance
(SOP.HasDatatypeInfo a, HasElmType a, SOP.All2 (HasElmDecoder Aeson.Value) (SOP.Code a), HasElmType (ElmType name a), KnownSymbol name) =>
HasElmDecoder Aeson.Value (ElmType name a)
where
elmDecoderDefinition =
Just
$ deriveElmJSONDecoder
@a
defaultOptions {fieldLabelModifier = dropWhile (== '_')}
Aeson.defaultOptions {Aeson.fieldLabelModifier = dropWhile (== '_')}
$ Name.Qualified moduleName $ lowerName <> "Decoder"
where
Name.Qualified moduleName name = fromString $ symbolVal $ Proxy @name
lowerName = Text.toLower (Text.take 1 name) <> Text.drop 1 name
instance
(SOP.HasDatatypeInfo a, HasElmType a, SOP.All2 (HasElmEncoder Aeson.Value) (SOP.Code a), HasElmType (ElmType name a), KnownSymbol name) =>
HasElmEncoder Aeson.Value (ElmType name a)
where
elmEncoderDefinition =
Just
$ deriveElmJSONEncoder
@a
defaultOptions {fieldLabelModifier = dropWhile (== '_')}
Aeson.defaultOptions {Aeson.fieldLabelModifier = dropWhile (== '_')}
$ Name.Qualified moduleName $ lowerName <> "Encoder"
where
Name.Qualified moduleName name = fromString $ symbolVal $ Proxy @name
lowerName = Text.toLower (Text.take 1 name) <> Text.drop 1 name
-------------------------------------------------------------------------------
data User = User
{ _name :: Text
, _age :: Int
} deriving (Generic, SOP.Generic, SOP.HasDatatypeInfo)
deriving (Aeson.ToJSON, Aeson.FromJSON, HasElmType, HasElmDecoder Aeson.Value, HasElmEncoder Aeson.Value) via ElmType "Api.User.User" User
main :: IO ()
main = do
let
definitions =
Simplification.simplifyDefinition <$>
jsonDefinitions @User
modules =
Pretty.modules definitions
forM_ (HashMap.toList modules) $ \(_moduleName, contents) ->
print contents