From c5fa5d5ca3decff1ff86864771c599c3277aad6b Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 6 Mar 2021 14:11:52 -0800 Subject: [PATCH 01/13] override foreign-generic and argonaut-aeson-generic --- example/packages.dhall | 33 +++++++++++++++++++++------------ example/src/Main.purs | 5 +++-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/example/packages.dhall b/example/packages.dhall index c9a3dd33..3f334823 100644 --- a/example/packages.dhall +++ b/example/packages.dhall @@ -1,13 +1,5 @@ let upstream = - https://github.com/purescript/package-sets/releases/download/psc-0.13.8-20210118/packages.dhall sha256:a59c5c93a68d5d066f3815a89f398bcf00e130a51cb185b2da29b20e2d8ae115 - -let overrides = - { argonaut-generic = upstream.argonaut-generic ⫽ { version = "v5.0.0" } - , argonaut = upstream.argonaut ⫽ { version = "v6.0.0" } - , argonaut-codecs = upstream.argonaut-codecs ⫽ { version = "v6.0.2" } - , argonaut-traversals = - upstream.argonaut-traversals ⫽ { version = "v6.0.0" } - } + https://github.com/purescript/package-sets/releases/download/psc-0.14.0-20210304/packages.dhall sha256:c88151fe7c05f05290224c9c1ae4a22905060424fb01071b691d3fe2e5bad4ca let additions = { argonaut-aeson-generic = @@ -21,9 +13,26 @@ let additions = , "psci-support" , "test-unit" ] - , repo = "git://github.com/coot/purescript-argonaut-aeson-generic.git" - , version = "2201093f39d58befe7e4ae9e2f587e340ee54a28" + , repo = + "git://github.com/peterbecich/purescript-argonaut-aeson-generic.git" + , version = "2c8c5ee2381ddb786af7fb79a73e3b83001d68e8" + } + , foreign-generic = + { dependencies = + [ "console" + , "effect" + , "psci-support" + , "prelude" + , "tuples" + , "bifunctors" + , "foreign" + , "foreign-object" + , "assert" + , "record" + ] + , repo = "git://github.com/peterbecich/purescript-foreign-generic.git" + , version = "56bc2056ef706ded4ef4406aced01a23d39af7cf" } } -in upstream ⫽ overrides ⫽ additions +in upstream // additions diff --git a/example/src/Main.purs b/example/src/Main.purs index 09f128e2..6115ddaf 100644 --- a/example/src/Main.purs +++ b/example/src/Main.purs @@ -2,6 +2,7 @@ module Main where import Prelude +import Data.Argonaut.Decode.Error (JsonDecodeError, printJsonDecodeError) import Data.Argonaut.Aeson.Decode.Generic (genericDecodeAeson) import Data.Argonaut.Aeson.Encode.Generic (genericEncodeAeson) import Data.Argonaut.Aeson.Options (defaultOptions) @@ -29,10 +30,10 @@ main = log "Hello, Purescript!" *> launchAff_ do fooResponse <- get json "/foo" for_ fooResponse \fooPayload -> do let - efoo :: Either String Foo + efoo :: Either JsonDecodeError Foo efoo = genericDecodeAeson defaultOptions fooPayload.body case efoo of - Left e -> liftEffect $ log $ "Error decoding Foo: " <> e + Left e -> liftEffect $ log $ "Error decoding Foo: " <> printJsonDecodeError e Right _ -> pure unit for_ efoo \foo -> do liftEffect do From 137d4242100e8e4a7d9893c6cf30570b95b6b284 Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 6 Mar 2021 17:08:36 -0800 Subject: [PATCH 02/13] bridge for Maps with String keys, and example --- example/example.cabal | 1 + example/spago.dhall | 1 + example/src/Main.purs | 12 ++++++++++-- example/src/MyLib.hs | 10 ++++++++-- example/src/Types.hs | 2 ++ example/src/Types.purs | 7 ++++++- src/Language/PureScript/Bridge.hs | 1 + src/Language/PureScript/Bridge/PSTypes.hs | 5 +++++ src/Language/PureScript/Bridge/Primitives.hs | 3 +++ 9 files changed, 37 insertions(+), 5 deletions(-) diff --git a/example/example.cabal b/example/example.cabal index e0c97172..61cd2406 100644 --- a/example/example.cabal +++ b/example/example.cabal @@ -15,6 +15,7 @@ library , aeson >= 1.5.5.0 , aeson-pretty , bytestring + , containers , lens , transformers , servant >=0.18.0 diff --git a/example/spago.dhall b/example/spago.dhall index 4fcf1b70..1c9cb542 100644 --- a/example/spago.dhall +++ b/example/spago.dhall @@ -12,6 +12,7 @@ You can edit this file as you like. , "aff" , "affjax" , "argonaut-aeson-generic" + , "ordered-collections" ] , packages = ./packages.dhall , sources = [ "src/**/*.purs", "test/**/*.purs" ] diff --git a/example/src/Main.purs b/example/src/Main.purs index 6115ddaf..b0932ec8 100644 --- a/example/src/Main.purs +++ b/example/src/Main.purs @@ -18,8 +18,14 @@ import Effect.Aff (launchAff_) import Affjax (get, post_) import Affjax.ResponseFormat (json) import Affjax.RequestBody as RequestBody - import Types (Foo, fooMessage, fooNumber, fooList) +import Data.Argonaut.Decode.Error (JsonDecodeError) +import Data.Argonaut.Decode.Generic (genericDecodeJson) +import Data.Argonaut.Encode.Generic (genericEncodeJson) +import Types (Foo, fooMessage, fooNumber, fooList, fooMap) +import Data.Map as Map + +import Foreign.Object as Object main :: Effect Unit main = log "Hello, Purescript!" *> launchAff_ do @@ -41,12 +47,14 @@ main = log "Hello, Purescript!" *> launchAff_ do <> "\t Foo number: " <> (show $ view fooNumber foo) <> "\t Foo list length: " <> (show (length $ view fooList foo :: Int)) + <> "\t Foo map size: " + <> (show (Object.size $ view fooMap foo :: Int)) let -- modify the Foo received and send it back foo' = set fooMessage "Hola" $ over fooNumber (_+1) $ over fooList (\l -> l <> l) + $ over fooMap (\o -> Object.insert "abc" 123 o) $ foo response = Just $ RequestBody.json $ genericEncodeAeson defaultOptions foo' post_ "/foo" response - diff --git a/example/src/MyLib.hs b/example/src/MyLib.hs index a0dc806f..ae275fa9 100644 --- a/example/src/MyLib.hs +++ b/example/src/MyLib.hs @@ -17,9 +17,10 @@ import GHC.TypeLits import Network.Wai.Handler.Warp import Servant import System.Environment (lookupEnv) +import qualified Data.Map.Lazy as Map import Types - (Foo (Foo), fooMessage, fooNumber, fooList) + (Foo (Foo), fooMessage, fooNumber, fooList, fooMap) type FooServer = "foo" :> (Get '[JSON] Foo @@ -27,7 +28,11 @@ type FooServer ) foo :: Foo -foo = Foo (pack "Hello") 123 [10..20] +foo = Foo + (pack "Hello") + 123 + [10..20] + (Map.fromList [(pack "foo", 2), (pack "bar", 3), (pack "baz", 3)]) fooServer :: Server FooServer fooServer = getFoo :<|> postFoo @@ -38,6 +43,7 @@ fooServer = getFoo :<|> postFoo logMsg = "Foo message: " <> (unpack $ view fooMessage foo) <> "\t Foo number: " <> (show (view fooNumber foo)) <> "\t Foo list length: " <> (show . length $ view fooList foo) + <> "\t Foo Map length: " <> (show . length $ view fooMap foo) liftIO . putStrLn $ logMsg return NoContent diff --git a/example/src/Types.hs b/example/src/Types.hs index 180699c9..d8eaaf0d 100644 --- a/example/src/Types.hs +++ b/example/src/Types.hs @@ -14,11 +14,13 @@ import Data.Text import GHC.Generics import Language.PureScript.Bridge import Language.PureScript.Bridge.PSTypes +import qualified Data.Map.Lazy as Map data Foo = Foo { _fooMessage :: Text , _fooNumber :: Int , _fooList :: [Int] + , _fooMap :: Map.Map Text Int } deriving (Generic, ToJSON, FromJSON) makeLenses ''Foo diff --git a/example/src/Types.purs b/example/src/Types.purs index 7c1d5a3e..18200fae 100644 --- a/example/src/Types.purs +++ b/example/src/Types.purs @@ -10,6 +10,7 @@ import Data.Newtype (class Newtype) import Data.Symbol (SProxy(SProxy)) import Foreign.Class (class Decode, class Encode) import Foreign.Generic (defaultOptions, genericDecode, genericEncode) +import Foreign.Object (Object) import Prim (Array, Int, String) import Prelude @@ -19,6 +20,7 @@ newtype Foo = _fooMessage :: String , _fooNumber :: Int , _fooList :: Array Int + , _fooMap :: Object Int } instance encodeFoo :: Encode Foo where @@ -29,7 +31,7 @@ derive instance genericFoo :: Generic Foo _ derive instance newtypeFoo :: Newtype Foo _ -------------------------------------------------------------------------------- -_Foo :: Iso' Foo { _fooMessage :: String, _fooNumber :: Int, _fooList :: Array Int} +_Foo :: Iso' Foo { _fooMessage :: String, _fooNumber :: Int, _fooList :: Array Int, _fooMap :: Object Int} _Foo = _Newtype fooMessage :: Lens' Foo String @@ -41,4 +43,7 @@ fooNumber = _Newtype <<< prop (SProxy :: SProxy "_fooNumber") fooList :: Lens' Foo (Array Int) fooList = _Newtype <<< prop (SProxy :: SProxy "_fooList") +fooMap :: Lens' Foo (Object Int) +fooMap = _Newtype <<< prop (SProxy :: SProxy "_fooMap") + -------------------------------------------------------------------------------- diff --git a/src/Language/PureScript/Bridge.hs b/src/Language/PureScript/Bridge.hs index 7d9239b0..2b21956e 100644 --- a/src/Language/PureScript/Bridge.hs +++ b/src/Language/PureScript/Bridge.hs @@ -130,6 +130,7 @@ defaultBridge = textBridge <|> listBridge <|> maybeBridge <|> eitherBridge + <|> strMapBridge <|> boolBridge <|> intBridge <|> doubleBridge diff --git a/src/Language/PureScript/Bridge/PSTypes.hs b/src/Language/PureScript/Bridge/PSTypes.hs index f54b5cd4..2888490b 100644 --- a/src/Language/PureScript/Bridge/PSTypes.hs +++ b/src/Language/PureScript/Bridge/PSTypes.hs @@ -32,6 +32,11 @@ psBool = TypeInfo { psEither :: MonadReader BridgeData m => m PSType psEither = TypeInfo "purescript-either" "Data.Either" "Either" <$> psTypeParameters +psObject :: MonadReader BridgeData m => m PSType +psObject = do + valueTypes <- tail <$> psTypeParameters + return $ TypeInfo "purescript-foreign-object" "Foreign.Object" "Object" valueTypes + psInt :: PSType psInt = TypeInfo { _typePackage = "" diff --git a/src/Language/PureScript/Bridge/Primitives.hs b/src/Language/PureScript/Bridge/Primitives.hs index a7d35221..51ab5987 100644 --- a/src/Language/PureScript/Bridge/Primitives.hs +++ b/src/Language/PureScript/Bridge/Primitives.hs @@ -19,6 +19,9 @@ boolBridge = typeName ^== "Bool" >> return psBool eitherBridge :: BridgePart eitherBridge = typeName ^== "Either" >> psEither +strMapBridge :: BridgePart +strMapBridge = typeName ^== "Map" >> psObject + -- | Dummy bridge, translates every type with 'clearPackageFixUp' dummyBridge :: MonadReader BridgeData m => m PSType dummyBridge = clearPackageFixUp From 78094e7edb6763973c01d591b194a6c94181d200 Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 6 Mar 2021 17:13:02 -0800 Subject: [PATCH 03/13] CI uses Purescript 0.14 --- .github/workflows/purescript.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/purescript.yml b/.github/workflows/purescript.yml index 35159643..606358f9 100644 --- a/.github/workflows/purescript.yml +++ b/.github/workflows/purescript.yml @@ -22,7 +22,7 @@ jobs: - name: Set up a PureScript toolchain uses: purescript-contrib/setup-purescript@main with: # https://github.com/purescript-contrib/setup-purescript#specify-versions - purescript: "0.13.8" + purescript: "0.14.0" - name: Cache PureScript dependencies uses: actions/cache@v2 From 1501e5650fed0529d563194607c40d979e7ebeac Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sun, 7 Mar 2021 21:23:22 -0800 Subject: [PATCH 04/13] nested types example --- example/src/MyLib.hs | 4 ++-- example/src/Types.hs | 13 ++++++++++++- example/src/Types.purs | 26 +++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/example/src/MyLib.hs b/example/src/MyLib.hs index ae275fa9..0e45a46d 100644 --- a/example/src/MyLib.hs +++ b/example/src/MyLib.hs @@ -20,7 +20,7 @@ import System.Environment (lookupEnv) import qualified Data.Map.Lazy as Map import Types - (Foo (Foo), fooMessage, fooNumber, fooList, fooMap) + (Foo (Foo), Baz (Baz), fooMessage, fooNumber, fooList, fooMap) type FooServer = "foo" :> (Get '[JSON] Foo @@ -32,7 +32,7 @@ foo = Foo (pack "Hello") 123 [10..20] - (Map.fromList [(pack "foo", 2), (pack "bar", 3), (pack "baz", 3)]) + (Map.fromList [(pack "foo", 2), (pack "bar", 3), (pack "baz", 3)]) (Baz $ pack "hello") fooServer :: Server FooServer fooServer = getFoo :<|> postFoo diff --git a/example/src/Types.hs b/example/src/Types.hs index d8eaaf0d..94feceb4 100644 --- a/example/src/Types.hs +++ b/example/src/Types.hs @@ -16,11 +16,21 @@ import Language.PureScript.Bridge import Language.PureScript.Bridge.PSTypes import qualified Data.Map.Lazy as Map +data Baz = Baz + { _bazMessage :: Text + } deriving (Generic, ToJSON, FromJSON) + +makeLenses ''Baz + +bazProxy :: Proxy Baz +bazProxy = Proxy + data Foo = Foo { _fooMessage :: Text , _fooNumber :: Int , _fooList :: [Int] , _fooMap :: Map.Map Text Int + , _fooBaz :: Baz } deriving (Generic, ToJSON, FromJSON) makeLenses ''Foo @@ -33,5 +43,6 @@ myBridge = defaultBridge myTypes :: [SumType 'Haskell] myTypes = - [ mkSumType (Proxy :: Proxy Foo) + [ mkSumType (Proxy :: Proxy Baz) + , mkSumType (Proxy :: Proxy Foo) ] diff --git a/example/src/Types.purs b/example/src/Types.purs index 18200fae..6b68599d 100644 --- a/example/src/Types.purs +++ b/example/src/Types.purs @@ -15,12 +15,33 @@ import Prim (Array, Int, String) import Prelude +newtype Baz = + Baz { + _bazMessage :: String + } + +instance encodeBaz :: Encode Baz where + encode = genericEncode $ defaultOptions { unwrapSingleConstructors = false , unwrapSingleArguments = false } +instance decodeBaz :: Decode Baz where + decode = genericDecode $ defaultOptions { unwrapSingleConstructors = false , unwrapSingleArguments = false } +derive instance genericBaz :: Generic Baz _ +derive instance newtypeBaz :: Newtype Baz _ + +-------------------------------------------------------------------------------- +_Baz :: Iso' Baz { _bazMessage :: String} +_Baz = _Newtype + +bazMessage :: Lens' Baz String +bazMessage = _Newtype <<< prop (SProxy :: SProxy "_bazMessage") + +-------------------------------------------------------------------------------- newtype Foo = Foo { _fooMessage :: String , _fooNumber :: Int , _fooList :: Array Int , _fooMap :: Object Int + , _fooBaz :: Baz } instance encodeFoo :: Encode Foo where @@ -31,7 +52,7 @@ derive instance genericFoo :: Generic Foo _ derive instance newtypeFoo :: Newtype Foo _ -------------------------------------------------------------------------------- -_Foo :: Iso' Foo { _fooMessage :: String, _fooNumber :: Int, _fooList :: Array Int, _fooMap :: Object Int} +_Foo :: Iso' Foo { _fooMessage :: String, _fooNumber :: Int, _fooList :: Array Int, _fooMap :: Object Int, _fooBaz :: Baz} _Foo = _Newtype fooMessage :: Lens' Foo String @@ -46,4 +67,7 @@ fooList = _Newtype <<< prop (SProxy :: SProxy "_fooList") fooMap :: Lens' Foo (Object Int) fooMap = _Newtype <<< prop (SProxy :: SProxy "_fooMap") +fooBaz :: Lens' Foo Baz +fooBaz = _Newtype <<< prop (SProxy :: SProxy "_fooBaz") + -------------------------------------------------------------------------------- From eec344ae713fe9874af77fdb25566247180beec2 Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Mon, 8 Mar 2021 00:47:07 -0800 Subject: [PATCH 05/13] generate Argonaut Codecs instances --- example/src/Types.purs | 13 +++ .../PureScript/Bridge/CodeGenSwitches.hs | 5 +- src/Language/PureScript/Bridge/Printer.hs | 88 +++++++++++++++---- src/Language/PureScript/Bridge/SumType.hs | 4 +- 4 files changed, 87 insertions(+), 23 deletions(-) diff --git a/example/src/Types.purs b/example/src/Types.purs index 6b68599d..294baf85 100644 --- a/example/src/Types.purs +++ b/example/src/Types.purs @@ -1,6 +1,11 @@ -- File auto generated by purescript-bridge! -- module Types where +import Data.Argonaut.Aeson.Decode.Generic (genericDecodeAeson) +import Data.Argonaut.Aeson.Encode.Generic (genericEncodeAeson) +import Data.Argonaut.Aeson.Options as Argonaut +import Data.Argonaut.Decode.Class (class DecodeJson, decodeJson) +import Data.Argonaut.Encode.Class (class EncodeJson, encodeJson) import Data.Generic.Rep (class Generic) import Data.Lens (Iso', Lens', Prism', lens, prism') import Data.Lens.Iso.Newtype (_Newtype) @@ -24,6 +29,10 @@ instance encodeBaz :: Encode Baz where encode = genericEncode $ defaultOptions { unwrapSingleConstructors = false , unwrapSingleArguments = false } instance decodeBaz :: Decode Baz where decode = genericDecode $ defaultOptions { unwrapSingleConstructors = false , unwrapSingleArguments = false } +instance encodeJsonBaz :: EncodeJson Baz where + encodeJson = genericEncodeAeson Argonaut.defaultOptions +instance decodeJsonBaz :: DecodeJson Baz where + decodeJson = genericDecodeAeson Argonaut.defaultOptions derive instance genericBaz :: Generic Baz _ derive instance newtypeBaz :: Newtype Baz _ @@ -48,6 +57,10 @@ instance encodeFoo :: Encode Foo where encode = genericEncode $ defaultOptions { unwrapSingleConstructors = false , unwrapSingleArguments = false } instance decodeFoo :: Decode Foo where decode = genericDecode $ defaultOptions { unwrapSingleConstructors = false , unwrapSingleArguments = false } +instance encodeJsonFoo :: EncodeJson Foo where + encodeJson = genericEncodeAeson Argonaut.defaultOptions +instance decodeJsonFoo :: DecodeJson Foo where + decodeJson = genericDecodeAeson Argonaut.defaultOptions derive instance genericFoo :: Generic Foo _ derive instance newtypeFoo :: Newtype Foo _ diff --git a/src/Language/PureScript/Bridge/CodeGenSwitches.hs b/src/Language/PureScript/Bridge/CodeGenSwitches.hs index 3a93ebef..cb569e3c 100644 --- a/src/Language/PureScript/Bridge/CodeGenSwitches.hs +++ b/src/Language/PureScript/Bridge/CodeGenSwitches.hs @@ -19,6 +19,7 @@ import Data.Monoid (Endo(..)) data Settings = Settings { generateLenses :: Bool -- ^use purescript-profunctor-lens for generated PS-types? , genericsGenRep :: Bool -- ^generate generics using purescript-generics-rep instead of purescript-generics + , generateArgonautCodecs :: Bool -- ^generate Data.Argonaut.Decode.Class EncodeJson and DecodeJson instances , generateForeign :: Maybe ForeignOptions -- ^generate Foreign.Generic Encode and Decode instances } deriving (Eq, Show) @@ -31,12 +32,12 @@ data ForeignOptions = ForeignOptions -- | Settings to generate Lenses defaultSettings :: Settings -defaultSettings = Settings True True Nothing +defaultSettings = Settings True True True Nothing -- |settings for purescript 0.11.x purs_0_11_settings :: Settings -purs_0_11_settings = Settings True False Nothing +purs_0_11_settings = Settings True False False Nothing -- | you can `mappend` switches to control the code generation diff --git a/src/Language/PureScript/Bridge/Printer.hs b/src/Language/PureScript/Bridge/Printer.hs index 45a73881..4e1972e6 100644 --- a/src/Language/PureScript/Bridge/Printer.hs +++ b/src/Language/PureScript/Bridge/Printer.hs @@ -36,6 +36,7 @@ type PSModule = Module 'PureScript data ImportLine = ImportLine { importModule :: !Text +, importAlias :: !(Maybe Text) , importTypes :: !(Set Text) } deriving Show @@ -69,45 +70,63 @@ moduleToText settings m = T.unlines $ ] <> map (sumTypeToText settings) (psTypes m) where - otherImports = importsFromList (_lensImports settings <> _genericsImports settings <> _foreignImports settings) + otherImports = importsFromList $ + _lensImports settings + <> _genericsImports settings + <> _argonautCodecsImports settings + <> _foreignImports settings allImports = Map.elems $ mergeImportLines otherImports (psImportLines m) _genericsImports :: Switches.Settings -> [ImportLine] _genericsImports settings | Switches.genericsGenRep settings = - [ ImportLine "Data.Generic.Rep" $ Set.fromList ["class Generic"] ] + [ ImportLine "Data.Generic.Rep" Nothing $ Set.fromList ["class Generic"] ] | otherwise = - [ ImportLine "Data.Generic" $ Set.fromList ["class Generic"] ] + [ ImportLine "Data.Generic" Nothing $ Set.fromList ["class Generic"] ] _lensImports :: Switches.Settings -> [ImportLine] _lensImports settings | Switches.generateLenses settings = - [ ImportLine "Data.Maybe" $ Set.fromList ["Maybe(..)"] - , ImportLine "Data.Lens" $ Set.fromList ["Iso'", "Prism'", "Lens'", "prism'", "lens"] - , ImportLine "Data.Lens.Record" $ Set.fromList ["prop"] - , ImportLine "Data.Lens.Iso.Newtype" $ Set.fromList ["_Newtype"] - , ImportLine "Data.Symbol" $ Set.fromList ["SProxy(SProxy)"] - , ImportLine "Data.Newtype" $ Set.fromList ["class Newtype"] + [ ImportLine "Data.Maybe" Nothing $ Set.fromList ["Maybe(..)"] + , ImportLine "Data.Lens" Nothing $ Set.fromList ["Iso'", "Prism'", "Lens'", "prism'", "lens"] + , ImportLine "Data.Lens.Record" Nothing $ Set.fromList ["prop"] + , ImportLine "Data.Lens.Iso.Newtype" Nothing $ Set.fromList ["_Newtype"] + , ImportLine "Data.Symbol" Nothing $ Set.fromList ["SProxy(SProxy)"] + , ImportLine "Data.Newtype" Nothing $ Set.fromList ["class Newtype"] ] | otherwise = - [ ImportLine "Data.Maybe" $ Set.fromList ["Maybe(..)"] - , ImportLine "Data.Newtype" $ Set.fromList ["class Newtype"] + [ ImportLine "Data.Maybe" Nothing $ Set.fromList ["Maybe(..)"] + , ImportLine "Data.Newtype" Nothing $ Set.fromList ["class Newtype"] + ] + +_argonautCodecsImports :: Switches.Settings -> [ImportLine] +_argonautCodecsImports settings + | Switches.generateArgonautCodecs settings = + [ ImportLine "Data.Argonaut.Decode.Class" Nothing $ Set.fromList [ "class DecodeJson", "decodeJson" ] + , ImportLine "Data.Argonaut.Encode.Class" Nothing $ Set.fromList [ "class EncodeJson", "encodeJson" ] + , ImportLine "Data.Argonaut.Aeson.Decode.Generic" Nothing $ Set.fromList [ "genericDecodeAeson" ] + , ImportLine "Data.Argonaut.Aeson.Encode.Generic" Nothing $ Set.fromList [ "genericEncodeAeson" ] + , ImportLine "Data.Argonaut.Aeson.Options" (Just "Argonaut") $ Set.fromList [ "defaultOptions" ] ] _foreignImports :: Switches.Settings -> [ImportLine] _foreignImports settings | (isJust . Switches.generateForeign) settings = - [ ImportLine "Foreign.Generic" $ Set.fromList ["defaultOptions", "genericDecode", "genericEncode"] - , ImportLine "Foreign.Class" $ Set.fromList ["class Decode", "class Encode"] + [ ImportLine "Foreign.Generic" Nothing $ Set.fromList ["defaultOptions", "genericDecode", "genericEncode"] + , ImportLine "Foreign.Class" Nothing $ Set.fromList ["class Decode", "class Encode"] ] | otherwise = [] importLineToText :: ImportLine -> Text -importLineToText l = "import " <> importModule l <> " (" <> typeList <> ")" +importLineToText = \case + ImportLine importModule Nothing importTypes -> + "import " <> importModule <> " (" <> typeList importTypes <> ")" + ImportLine importModule (Just importAlias) _ -> + "import " <> importModule <> " as " <> importAlias where - typeList = T.intercalate ", " (Set.toList (importTypes l)) + typeList s = T.intercalate ", " (Set.toList s) sumTypeToText :: Switches.Settings -> SumType 'PureScript -> Text sumTypeToText settings st = @@ -159,6 +178,18 @@ instances settings st@(SumType t _ is) = map go is constraintsInner = T.intercalate ", " $ map instances sumTypeParameters instances params = genericInstance settings params <> ", " <> encodeInstance params bracketWrap x = "(" <> x <> ")" + go EncodeJson = "instance encodeJson" <> _typeName t <> " :: " <> extras <> "EncodeJson " <> typeInfoToText False t <> " where\n" <> + " encodeJson = genericEncodeAeson Argonaut.defaultOptions" + where + encodeOpts = + foreignOptionsToPurescript $ Switches.generateForeign settings + stpLength = length sumTypeParameters + extras | stpLength == 0 = mempty + | otherwise = bracketWrap constraintsInner <> " => " + sumTypeParameters = filter (isTypeParam t) . Set.toList $ getUsedTypes st + constraintsInner = T.intercalate ", " $ map instances sumTypeParameters + instances params = genericInstance settings params <> ", " <> encodeInstance params + bracketWrap x = "(" <> x <> ")" go Decode = "instance decode" <> _typeName t <> " :: " <> extras <> "Decode " <> typeInfoToText False t <> " where\n" <> " decode = genericDecode $ defaultOptions" <> decodeOpts where @@ -171,6 +202,18 @@ instances settings st@(SumType t _ is) = map go is constraintsInner = T.intercalate ", " $ map instances sumTypeParameters instances params = genericInstance settings params <> ", " <> decodeInstance params bracketWrap x = "(" <> x <> ")" + go DecodeJson = "instance decodeJson" <> _typeName t <> " :: " <> extras <> "DecodeJson " <> typeInfoToText False t <> " where\n" <> + " decodeJson = genericDecodeAeson Argonaut.defaultOptions" + where + decodeOpts = + foreignOptionsToPurescript $ Switches.generateForeign settings + stpLength = length sumTypeParameters + extras | stpLength == 0 = mempty + | otherwise = bracketWrap constraintsInner <> " => " + sumTypeParameters = filter (isTypeParam t) . Set.toList $ getUsedTypes st + constraintsInner = T.intercalate ", " $ map instances sumTypeParameters + instances params = genericInstance settings params <> ", " <> decodeJsonInstance params + bracketWrap x = "(" <> x <> ")" go i = "derive instance " <> T.toLower c <> _typeName t <> " :: " <> extras i <> c <> " " <> typeInfoToText False t <> postfix i where c = T.pack $ show i extras Generic | stpLength == 0 = mempty @@ -193,9 +236,15 @@ isTypeParam t typ = _typeName typ `elem` map _typeName (_typeParameters t) encodeInstance :: PSType -> Text encodeInstance params = "Encode " <> typeInfoToText False params +encodeJsonInstance :: PSType -> Text +encodeJsonInstance params = "EncodeJson " <> typeInfoToText False params + decodeInstance :: PSType -> Text decodeInstance params = "Decode " <> typeInfoToText False params +decodeJsonInstance :: PSType -> Text +decodeJsonInstance params = "DecodeJson " <> typeInfoToText False params + genericInstance :: Switches.Settings -> PSType -> Text genericInstance settings params = if not (Switches.genericsGenRep settings) then @@ -369,20 +418,21 @@ typeToImportLines t ls = typesToImportLines (update ls) (Set.fromList (_typePara then Map.alter (Just . updateLine) (_typeModule t) else id - updateLine Nothing = ImportLine (_typeModule t) (Set.singleton (_typeName t)) - updateLine (Just (ImportLine m types)) = ImportLine m $ Set.insert (_typeName t) types + updateLine Nothing = ImportLine (_typeModule t) Nothing (Set.singleton (_typeName t)) + updateLine (Just (ImportLine m alias types)) = + ImportLine m alias (Set.insert (_typeName t) types) importsFromList :: [ImportLine] -> Map Text ImportLine importsFromList ls = let pairs = zip (map importModule ls) ls - merge a b = ImportLine (importModule a) (importTypes a `Set.union` importTypes b) + merge a b = ImportLine (importModule a) (importAlias a) (importTypes a `Set.union` importTypes b) in Map.fromListWith merge pairs mergeImportLines :: ImportLines -> ImportLines -> ImportLines mergeImportLines = Map.unionWith mergeLines where - mergeLines a b = ImportLine (importModule a) (importTypes a `Set.union` importTypes b) + mergeLines a b = ImportLine (importModule a) (importAlias a) (importTypes a `Set.union` importTypes b) unlessM :: Monad m => m Bool -> m () -> m () unlessM mbool action = mbool >>= flip unless action diff --git a/src/Language/PureScript/Bridge/SumType.hs b/src/Language/PureScript/Bridge/SumType.hs index 36e22ec3..a9e5c7f8 100644 --- a/src/Language/PureScript/Bridge/SumType.hs +++ b/src/Language/PureScript/Bridge/SumType.hs @@ -55,12 +55,12 @@ sumTypeConstructors inj (SumType info constrs is) = (\cs -> SumType info cs is) -- In order to get the type information we use a dummy variable of type 'Proxy' (YourType). mkSumType :: forall t. (Generic t, Typeable t, GDataConstructor (Rep t)) => Proxy t -> SumType 'Haskell -mkSumType p = SumType (mkTypeInfo p) constructors (Encode : Decode : Generic : maybeToList (nootype constructors)) +mkSumType p = SumType (mkTypeInfo p) constructors (Encode : Decode : EncodeJson : DecodeJson : Generic : maybeToList (nootype constructors)) where constructors = gToConstructors (from (undefined :: t)) -- | Purescript typeclass instances that can be generated for your Haskell types. -data Instance = Encode | Decode | Generic | Newtype | Eq | Ord deriving (Eq, Show) +data Instance = Encode | EncodeJson | Decode | DecodeJson | Generic | Newtype | Eq | Ord deriving (Eq, Show) -- | The Purescript typeclass `Newtype` might be derivable if the original -- Haskell type was a simple type wrapper. From 6d95574c299612b829c5b9a8925035ec4e9158f4 Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Fri, 2 Apr 2021 22:43:17 -0700 Subject: [PATCH 06/13] update purescript-foreign-generic --- example/packages.dhall | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/packages.dhall b/example/packages.dhall index 3f334823..02227072 100644 --- a/example/packages.dhall +++ b/example/packages.dhall @@ -30,9 +30,9 @@ let additions = , "assert" , "record" ] - , repo = "git://github.com/peterbecich/purescript-foreign-generic.git" - , version = "56bc2056ef706ded4ef4406aced01a23d39af7cf" + , repo = "git://github.com/paf31/purescript-foreign-generic.git" + , version = "3cddc5fe3e87e426e0f719465ba60b9df4c0c72d" } } -in upstream // additions +in upstream ⫽ additions From e5e380ec5d8786d69a9ad064ca0957307cac397f Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 3 Apr 2021 21:37:39 -0700 Subject: [PATCH 07/13] clean-up --- example/app/GeneratePurescript.hs | 11 ++++++----- example/src/MyLib.hs | 21 +++++++++++---------- example/src/Types.hs | 16 ++++++++-------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/example/app/GeneratePurescript.hs b/example/app/GeneratePurescript.hs index 3611ae75..410e4476 100644 --- a/example/app/GeneratePurescript.hs +++ b/example/app/GeneratePurescript.hs @@ -1,10 +1,11 @@ module Main where -import Control.Lens -import Data.Text (pack) -import Language.PureScript.Bridge -import Language.PureScript.Bridge.CodeGenSwitches - (ForeignOptions (ForeignOptions), genForeign, useGenRep) +import Control.Lens +import Data.Text (pack) +import Language.PureScript.Bridge +import Language.PureScript.Bridge.CodeGenSwitches (ForeignOptions (ForeignOptions), + genForeign, + useGenRep) import qualified MyLib import Types diff --git a/example/src/MyLib.hs b/example/src/MyLib.hs index 0e45a46d..c43fb57a 100644 --- a/example/src/MyLib.hs +++ b/example/src/MyLib.hs @@ -4,23 +4,23 @@ module MyLib (main) where -import Prelude +import Prelude -import Control.Lens (view) -import Control.Monad.IO.Class (liftIO) +import Control.Lens (view) +import Control.Monad.IO.Class (liftIO) import Data.Aeson -import qualified Data.Aeson.Encode.Pretty as AP +import qualified Data.Aeson.Encode.Pretty as AP import qualified Data.ByteString.Lazy.Char8 as Char8 -import Data.Text (pack, unpack) +import qualified Data.Map.Lazy as Map +import Data.Text (pack, unpack) import GHC.Generics import GHC.TypeLits import Network.Wai.Handler.Warp import Servant -import System.Environment (lookupEnv) -import qualified Data.Map.Lazy as Map +import System.Environment (lookupEnv) -import Types - (Foo (Foo), Baz (Baz), fooMessage, fooNumber, fooList, fooMap) +import Types (Baz (Baz), Foo (Foo), fooList, + fooMap, fooMessage, fooNumber) type FooServer = "foo" :> (Get '[JSON] Foo @@ -32,7 +32,8 @@ foo = Foo (pack "Hello") 123 [10..20] - (Map.fromList [(pack "foo", 2), (pack "bar", 3), (pack "baz", 3)]) (Baz $ pack "hello") + (Map.fromList [(pack "foo", 2), (pack "bar", 3), (pack "baz", 3)]) + (Baz $ pack "hello") fooServer :: Server FooServer fooServer = getFoo :<|> postFoo diff --git a/example/src/Types.hs b/example/src/Types.hs index 94feceb4..26a41d7e 100644 --- a/example/src/Types.hs +++ b/example/src/Types.hs @@ -7,14 +7,14 @@ module Types where -import Control.Lens.TH (makeLenses) -import Data.Aeson -import Data.Proxy -import Data.Text -import GHC.Generics -import Language.PureScript.Bridge -import Language.PureScript.Bridge.PSTypes -import qualified Data.Map.Lazy as Map +import Control.Lens.TH (makeLenses) +import Data.Aeson +import qualified Data.Map.Lazy as Map +import Data.Proxy +import Data.Text +import GHC.Generics +import Language.PureScript.Bridge +import Language.PureScript.Bridge.PSTypes data Baz = Baz { _bazMessage :: Text From 58144f59904999724922b92aab14d7a999003a9b Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Fri, 11 Jun 2021 22:59:22 -0700 Subject: [PATCH 08/13] fix Spago error --- example/packages.dhall | 4 ++-- example/spago.dhall | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/example/packages.dhall b/example/packages.dhall index 02227072..840e4238 100644 --- a/example/packages.dhall +++ b/example/packages.dhall @@ -1,5 +1,5 @@ let upstream = - https://github.com/purescript/package-sets/releases/download/psc-0.14.0-20210304/packages.dhall sha256:c88151fe7c05f05290224c9c1ae4a22905060424fb01071b691d3fe2e5bad4ca + https://github.com/purescript/package-sets/releases/download/psc-0.14.1-20210516/packages.dhall sha256:f5e978371d4cdc4b916add9011021509c8d869f4c3f6d0d2694c0e03a85046c8 let additions = { argonaut-aeson-generic = @@ -35,4 +35,4 @@ let additions = } } -in upstream ⫽ additions +in upstream // additions diff --git a/example/spago.dhall b/example/spago.dhall index 1c9cb542..352f6a6c 100644 --- a/example/spago.dhall +++ b/example/spago.dhall @@ -1,7 +1,3 @@ -{- -Welcome to a Spago project! -You can edit this file as you like. --} { name = "purescript-bridge-example" , dependencies = [ "console" @@ -13,6 +9,14 @@ You can edit this file as you like. , "affjax" , "argonaut-aeson-generic" , "ordered-collections" + , "argonaut-codecs" + , "argonaut-generic" + , "either" + , "foldable-traversable" + , "foreign-object" + , "maybe" + , "newtype" + , "prelude" ] , packages = ./packages.dhall , sources = [ "src/**/*.purs", "test/**/*.purs" ] From 9958155fa04a169ceec9e25505fa0c881513057d Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Fri, 11 Jun 2021 23:04:37 -0700 Subject: [PATCH 09/13] update Github Action for Haskell https://github.com/haskell/actions/tree/main/setup --- .github/workflows/haskell.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index d53148a1..2b8fb084 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -17,25 +17,19 @@ jobs: fail-fast: false matrix: versions: - - ghc: '8.0.2' - cabal: '3.2' - - ghc: '8.2.2' - cabal: '3.2' - - ghc: '8.4.4' - cabal: '3.2' - ghc: '8.6.5' - cabal: '3.2' + cabal: '3.4' - ghc: '8.8.4' - cabal: '3.2' - - ghc: '8.10.3' - cabal: '3.2' + cabal: '3.4' + - ghc: '8.10.4' + cabal: '3.4' steps: - uses: actions/checkout@v2 # need to install older cabal/ghc versions from ppa repository - name: Install recent cabal/ghc - uses: actions/setup-haskell@v1.1.3 + uses: haskell/actions/setup@v1 with: ghc-version: ${{ matrix.versions.ghc }} cabal-version: ${{ matrix.versions.cabal }} From 5fbc9c633c98962998aa30a7f19048714bf8f859 Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 12 Jun 2021 20:24:31 -0700 Subject: [PATCH 10/13] fix unit tests --- example/readme.md | 2 +- .../PureScript/Bridge/CodeGenSwitches.hs | 18 ++--- src/Language/PureScript/Bridge/Printer.hs | 52 +++++++------- src/Language/PureScript/Bridge/SumType.hs | 2 - test/Spec.hs | 68 +++++++++++++------ 5 files changed, 88 insertions(+), 54 deletions(-) diff --git a/example/readme.md b/example/readme.md index 01124855..97db3f30 100644 --- a/example/readme.md +++ b/example/readme.md @@ -12,7 +12,7 @@ In this directory: - `cabal run example` -- Open http://localhost:8080/index.html +- Open [http://localhost:8080/index.html](http://localhost:8080/index.html) - Open the browser's developer console and look for the message received: diff --git a/src/Language/PureScript/Bridge/CodeGenSwitches.hs b/src/Language/PureScript/Bridge/CodeGenSwitches.hs index cb569e3c..aefd6601 100644 --- a/src/Language/PureScript/Bridge/CodeGenSwitches.hs +++ b/src/Language/PureScript/Bridge/CodeGenSwitches.hs @@ -3,13 +3,13 @@ module Language.PureScript.Bridge.CodeGenSwitches ( Settings (..) , ForeignOptions(..) , defaultSettings - , purs_0_11_settings , Switch , getSettings , defaultSwitch , noLenses, genLenses , useGen, useGenRep - , genForeign, noForeign + , genForeign, noForeign, noArgonautCodecs + , genArgonautCodecs ) where @@ -34,12 +34,6 @@ data ForeignOptions = ForeignOptions defaultSettings :: Settings defaultSettings = Settings True True True Nothing - --- |settings for purescript 0.11.x -purs_0_11_settings :: Settings -purs_0_11_settings = Settings True False False Nothing - - -- | you can `mappend` switches to control the code generation type Switch = Endo Settings @@ -58,6 +52,10 @@ defaultSwitch = mempty noLenses :: Switch noLenses = Endo $ \settings -> settings { generateLenses = False } +-- | Switch off the generatation of argonaut-codecs +noArgonautCodecs :: Switch +noArgonautCodecs = Endo $ \settings -> + settings { generateArgonautCodecs = False } -- | Switch on the generatation of profunctor-lenses genLenses :: Switch @@ -76,5 +74,9 @@ useGen = Endo $ \settings -> settings { genericsGenRep = False } genForeign :: ForeignOptions -> Switch genForeign opts = Endo $ \settings -> settings { generateForeign = Just opts } +genArgonautCodecs :: Switch +genArgonautCodecs = Endo $ \settings -> + settings { generateArgonautCodecs = True } + noForeign :: Switch noForeign = Endo $ \settings -> settings { generateForeign = Nothing } diff --git a/src/Language/PureScript/Bridge/Printer.hs b/src/Language/PureScript/Bridge/Printer.hs index 4e1972e6..404cb7ac 100644 --- a/src/Language/PureScript/Bridge/Printer.hs +++ b/src/Language/PureScript/Bridge/Printer.hs @@ -20,7 +20,6 @@ import qualified Data.Text.IO as T import System.Directory import System.FilePath - import Language.PureScript.Bridge.SumType import Language.PureScript.Bridge.TypeInfo import qualified Language.PureScript.Bridge.CodeGenSwitches as Switches @@ -77,7 +76,6 @@ moduleToText settings m = T.unlines $ <> _foreignImports settings allImports = Map.elems $ mergeImportLines otherImports (psImportLines m) - _genericsImports :: Switches.Settings -> [ImportLine] _genericsImports settings | Switches.genericsGenRep settings = @@ -85,31 +83,32 @@ _genericsImports settings | otherwise = [ ImportLine "Data.Generic" Nothing $ Set.fromList ["class Generic"] ] - _lensImports :: Switches.Settings -> [ImportLine] _lensImports settings | Switches.generateLenses settings = - [ ImportLine "Data.Maybe" Nothing $ Set.fromList ["Maybe(..)"] - , ImportLine "Data.Lens" Nothing $ Set.fromList ["Iso'", "Prism'", "Lens'", "prism'", "lens"] + [ ImportLine "Data.Lens" Nothing $ Set.fromList ["Iso'", "Prism'", "Lens'", "prism'", "lens"] , ImportLine "Data.Lens.Record" Nothing $ Set.fromList ["prop"] , ImportLine "Data.Lens.Iso.Newtype" Nothing $ Set.fromList ["_Newtype"] - , ImportLine "Data.Symbol" Nothing $ Set.fromList ["SProxy(SProxy)"] - , ImportLine "Data.Newtype" Nothing $ Set.fromList ["class Newtype"] - ] - | otherwise = - [ ImportLine "Data.Maybe" Nothing $ Set.fromList ["Maybe(..)"] - , ImportLine "Data.Newtype" Nothing $ Set.fromList ["class Newtype"] + ] <> baseline <> + [ ImportLine "Data.Symbol" Nothing $ Set.fromList ["SProxy(SProxy)"] ] + | otherwise = baseline + where + baseline = + [ ImportLine "Data.Maybe" Nothing $ Set.fromList ["Maybe(..)"] + , ImportLine "Data.Newtype" Nothing $ Set.fromList ["class Newtype"] + ] _argonautCodecsImports :: Switches.Settings -> [ImportLine] _argonautCodecsImports settings | Switches.generateArgonautCodecs settings = - [ ImportLine "Data.Argonaut.Decode.Class" Nothing $ Set.fromList [ "class DecodeJson", "decodeJson" ] - , ImportLine "Data.Argonaut.Encode.Class" Nothing $ Set.fromList [ "class EncodeJson", "encodeJson" ] - , ImportLine "Data.Argonaut.Aeson.Decode.Generic" Nothing $ Set.fromList [ "genericDecodeAeson" ] + [ ImportLine "Data.Argonaut.Aeson.Decode.Generic" Nothing $ Set.fromList [ "genericDecodeAeson" ] , ImportLine "Data.Argonaut.Aeson.Encode.Generic" Nothing $ Set.fromList [ "genericEncodeAeson" ] , ImportLine "Data.Argonaut.Aeson.Options" (Just "Argonaut") $ Set.fromList [ "defaultOptions" ] + , ImportLine "Data.Argonaut.Decode.Class" Nothing $ Set.fromList [ "class DecodeJson", "decodeJson" ] + , ImportLine "Data.Argonaut.Encode.Class" Nothing $ Set.fromList [ "class EncodeJson", "encodeJson" ] ] + | otherwise = mempty _foreignImports :: Switches.Settings -> [ImportLine] _foreignImports settings @@ -117,7 +116,7 @@ _foreignImports settings [ ImportLine "Foreign.Generic" Nothing $ Set.fromList ["defaultOptions", "genericDecode", "genericEncode"] , ImportLine "Foreign.Class" Nothing $ Set.fromList ["class Decode", "class Encode"] ] - | otherwise = [] + | otherwise = mempty importLineToText :: ImportLine -> Text importLineToText = \case @@ -141,16 +140,26 @@ sumTypeToTypeDecls :: Switches.Settings -> SumType 'PureScript -> Text sumTypeToTypeDecls settings (SumType t cs is) = T.unlines $ dataOrNewtype <> " " <> typeInfoToText True t <> " =" : " " <> T.intercalate "\n | " (map (constructorToText 4) cs) <> "\n" - : instances settings (SumType t cs (filter genForeign is)) + : instances settings (SumType t cs (filter genForeign . filter genArgonautCodec $ is)) where dataOrNewtype = if isJust (nootype cs) then "newtype" else "data" - genForeign Encode = (isJust . Switches.generateForeign) settings - genForeign Decode = (isJust . Switches.generateForeign) settings - genForeign _ = True + genForeign :: Instance -> Bool + genForeign = \case + Encode -> check + Decode -> check + _ -> True + where check = (isJust . Switches.generateForeign) settings + + genArgonautCodec :: Instance -> Bool + genArgonautCodec = \case + EncodeJson -> check + DecodeJson -> check + _ -> True + where check = Switches.generateArgonautCodecs settings foreignOptionsToPurescript :: Maybe Switches.ForeignOptions -> Text foreignOptionsToPurescript = \case - Nothing -> "" + Nothing -> mempty Just (Switches.ForeignOptions{..}) -> " { unwrapSingleConstructors = " <> (T.toLower . T.pack . show $ unwrapSingleConstructors) @@ -205,8 +214,6 @@ instances settings st@(SumType t _ is) = map go is go DecodeJson = "instance decodeJson" <> _typeName t <> " :: " <> extras <> "DecodeJson " <> typeInfoToText False t <> " where\n" <> " decodeJson = genericDecodeAeson Argonaut.defaultOptions" where - decodeOpts = - foreignOptionsToPurescript $ Switches.generateForeign settings stpLength = length sumTypeParameters extras | stpLength == 0 = mempty | otherwise = bracketWrap constraintsInner <> " => " @@ -289,7 +296,6 @@ constructorToText indentation (DataConstructor n (Right rs)) = spaces :: Int -> Text spaces c = T.replicate c " " - typeNameAndForall :: TypeInfo 'PureScript -> (Text, Text) typeNameAndForall typeInfo = (typName, forAll) where diff --git a/src/Language/PureScript/Bridge/SumType.hs b/src/Language/PureScript/Bridge/SumType.hs index a9e5c7f8..10dccb55 100644 --- a/src/Language/PureScript/Bridge/SumType.hs +++ b/src/Language/PureScript/Bridge/SumType.hs @@ -85,7 +85,6 @@ data DataConstructor (lang :: Language) = , _sigValues :: !(Either [TypeInfo lang] [RecordEntry lang]) } deriving (Show, Eq) - data RecordEntry (lang :: Language) = RecordEntry { _recLabel :: !Text -- ^ e.g. `runState` for `State` , _recValue :: !(TypeInfo lang) @@ -118,7 +117,6 @@ instance (GRecordEntry a, GRecordEntry b) => GRecordEntry (a :*: b) where gToRecordEntries (_ :: (a :*: b) f) = gToRecordEntries (undefined :: a f) ++ gToRecordEntries (undefined :: b f) - instance GRecordEntry U1 where gToRecordEntries _ = [] diff --git a/test/Spec.hs b/test/Spec.hs index 31136128..0b0cba69 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -25,8 +25,7 @@ main = hspec allTests allTests :: Spec allTests = do - describe "buildBridge for purescript 0.12" $ do - let settings = purs_0_11_settings + describe "buildBridge for purescript 0.14" $ do it "tests with Int" $ let bst = buildBridge defaultBridge (mkTypeInfo (Proxy :: Proxy Int)) ti = TypeInfo { _typePackage = "" @@ -51,12 +50,12 @@ allTests = do ] } ] - [Eq, Ord, Encode, Decode, Generic] + [Eq, Ord, Encode, Decode, EncodeJson, DecodeJson, Generic] in bst `shouldBe` st it "tests generation of for custom type Foo" $ let prox = Proxy :: Proxy Foo recType = bridgeSumType (buildBridge defaultBridge) (order prox $ mkSumType prox) - recTypeText = sumTypeToText settings recType + recTypeText = sumTypeToText defaultSettings recType txt = T.stripEnd $ T.unlines [ "data Foo =" , " Foo" @@ -65,7 +64,11 @@ allTests = do , "" , "derive instance eqFoo :: Eq Foo" , "derive instance ordFoo :: Ord Foo" - , "derive instance genericFoo :: Generic Foo" + , "instance encodeJsonFoo :: EncodeJson Foo where" + , " encodeJson = genericEncodeAeson Argonaut.defaultOptions" + , "instance decodeJsonFoo :: DecodeJson Foo where" + , " decodeJson = genericDecodeAeson Argonaut.defaultOptions" + , "derive instance genericFoo :: Generic Foo _" , "" , "--------------------------------------------------------------------------------" , "_Foo :: Prism' Foo Unit" @@ -92,12 +95,17 @@ allTests = do it "tests the generation of a whole (dummy) module" $ let advanced = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (Bar A B M1 C))) modules = sumTypeToModule advanced Map.empty - m = head . map (moduleToText settings) . Map.elems $ modules + m = head . map (moduleToText defaultSettings) . Map.elems $ modules txt = T.unlines [ "-- File auto generated by purescript-bridge! --" , "module TestData where" , "" + , "import Data.Argonaut.Aeson.Decode.Generic (genericDecodeAeson)" + , "import Data.Argonaut.Aeson.Encode.Generic (genericEncodeAeson)" + , "import Data.Argonaut.Aeson.Options as Argonaut" + , "import Data.Argonaut.Decode.Class (class DecodeJson, decodeJson)" + , "import Data.Argonaut.Encode.Class (class EncodeJson, encodeJson)" , "import Data.Either (Either)" - , "import Data.Generic (class Generic)" + , "import Data.Generic.Rep (class Generic)" , "import Data.Lens (Iso', Lens', Prism', lens, prism')" , "import Data.Lens.Iso.Newtype (_Newtype)" , "import Data.Lens.Record (prop)" @@ -115,7 +123,11 @@ allTests = do , " myMonadicResult :: m b" , " }" , "" - , "derive instance genericBar :: (Generic a, Generic b, Generic (m b)) => Generic (Bar a b m c)" + , "instance encodeJsonBar :: (Generic a ra, Encode a, Generic b rb, Encode b, Generic (m b) rmb, Encode (m b)) => EncodeJson (Bar a b m c) where" + , " encodeJson = genericEncodeAeson Argonaut.defaultOptions" + , "instance decodeJsonBar :: (Generic a ra, DecodeJson a, Generic b rb, DecodeJson b, Generic (m b) rmb, DecodeJson (m b)) => DecodeJson (Bar a b m c) where" + , " decodeJson = genericDecodeAeson Argonaut.defaultOptions" + , "derive instance genericBar :: (Generic a ra, Generic b rb, Generic (m b) rmb) => Generic (Bar a b m c) _" , "" , "--------------------------------------------------------------------------------" , "_Bar1 :: forall a b m c. Prism' (Bar a b m c) (Maybe a)" @@ -211,7 +223,7 @@ allTests = do in (barOptics <> recTypeOptics) `shouldBe` txt it "tests generation of newtypes for record data type" $ let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (SingleRecord A B))) - recTypeText = sumTypeToText settings recType + recTypeText = sumTypeToText defaultSettings recType txt = T.stripEnd $ T.unlines [ "newtype SingleRecord a b =" , " SingleRecord {" @@ -220,7 +232,11 @@ allTests = do , " , c :: String" , " }" , "" - , "derive instance genericSingleRecord :: (Generic a, Generic b) => Generic (SingleRecord a b)" + , "instance encodeJsonSingleRecord :: (Generic a ra, Encode a, Generic b rb, Encode b) => EncodeJson (SingleRecord a b) where" + , " encodeJson = genericEncodeAeson Argonaut.defaultOptions" + , "instance decodeJsonSingleRecord :: (Generic a ra, DecodeJson a, Generic b rb, DecodeJson b) => DecodeJson (SingleRecord a b) where" + , " decodeJson = genericDecodeAeson Argonaut.defaultOptions" + , "derive instance genericSingleRecord :: (Generic a ra, Generic b rb) => Generic (SingleRecord a b) _" , "derive instance newtypeSingleRecord :: Newtype (SingleRecord a b) _" , "" , "--------------------------------------------------------------------------------" @@ -238,12 +254,16 @@ allTests = do in recTypeText `shouldBe` txt it "tests generation of newtypes for haskell newtype" $ let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SomeNewtype)) - recTypeText = sumTypeToText settings recType + recTypeText = sumTypeToText defaultSettings recType txt = T.stripEnd $ T.unlines [ "newtype SomeNewtype =" , " SomeNewtype Int" , "" - , "derive instance genericSomeNewtype :: Generic SomeNewtype" + , "instance encodeJsonSomeNewtype :: EncodeJson SomeNewtype where" + , " encodeJson = genericEncodeAeson Argonaut.defaultOptions" + , "instance decodeJsonSomeNewtype :: DecodeJson SomeNewtype where" + , " decodeJson = genericDecodeAeson Argonaut.defaultOptions" + , "derive instance genericSomeNewtype :: Generic SomeNewtype _" , "derive instance newtypeSomeNewtype :: Newtype SomeNewtype _" , "" , "--------------------------------------------------------------------------------" @@ -254,12 +274,16 @@ allTests = do in recTypeText `shouldBe` txt it "tests generation of newtypes for haskell data type with one argument" $ let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SingleValueConstr)) - recTypeText = sumTypeToText settings recType + recTypeText = sumTypeToText defaultSettings recType txt = T.stripEnd $ T.unlines [ "newtype SingleValueConstr =" , " SingleValueConstr Int" , "" - , "derive instance genericSingleValueConstr :: Generic SingleValueConstr" + , "instance encodeJsonSingleValueConstr :: EncodeJson SingleValueConstr where" + , " encodeJson = genericEncodeAeson Argonaut.defaultOptions" + , "instance decodeJsonSingleValueConstr :: DecodeJson SingleValueConstr where" + , " decodeJson = genericDecodeAeson Argonaut.defaultOptions" + , "derive instance genericSingleValueConstr :: Generic SingleValueConstr _" , "derive instance newtypeSingleValueConstr :: Newtype SingleValueConstr _" , "" , "--------------------------------------------------------------------------------" @@ -270,12 +294,16 @@ allTests = do in recTypeText `shouldBe` txt it "tests generation for haskell data type with one constructor, two arguments" $ let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SingleProduct)) - recTypeText = sumTypeToText settings recType + recTypeText = sumTypeToText defaultSettings recType txt = T.stripEnd $ T.unlines [ "data SingleProduct =" , " SingleProduct String Int" , "" - , "derive instance genericSingleProduct :: Generic SingleProduct" + , "instance encodeJsonSingleProduct :: EncodeJson SingleProduct where" + , " encodeJson = genericEncodeAeson Argonaut.defaultOptions" + , "instance decodeJsonSingleProduct :: DecodeJson SingleProduct where" + , " decodeJson = genericDecodeAeson Argonaut.defaultOptions" + , "derive instance genericSingleProduct :: Generic SingleProduct _" , "" , "--------------------------------------------------------------------------------" , "_SingleProduct :: Prism' SingleProduct { a :: String, b :: Int }" @@ -291,8 +319,8 @@ allTests = do recTypeOptics = recordOptics recType in recTypeOptics `shouldBe` "" -- No record optics for multi-constructors - describe "buildBridge without lens-code-gen for purescript 0.11" $ do - let settings = getSettings (noLenses <> useGen) + describe "buildBridge without lens-code-gen and argonaut-codecs" $ do + let settings = getSettings (noLenses <> useGen <> noArgonautCodecs) it "tests generation of for custom type Foo" $ let proxy = Proxy :: Proxy Foo recType = bridgeSumType (buildBridge defaultBridge) (order proxy $ mkSumType proxy) @@ -378,8 +406,8 @@ allTests = do in recTypeText `shouldBe` txt - describe "buildBridge without lens-code-gen and generics-rep" $ do - let settings = getSettings (noLenses <> useGenRep) + describe "buildBridge without lens-code-gen, generics-rep, and argonaut-codecs" $ do + let settings = getSettings (noLenses <> useGenRep <> noArgonautCodecs) it "tests generation of for custom type Foo" $ let proxy = Proxy :: Proxy Foo recType = bridgeSumType (buildBridge defaultBridge) (order proxy $ mkSumType proxy) From 046bca3aeb47a0f75549f350de932236208d51b1 Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 12 Jun 2021 20:39:23 -0700 Subject: [PATCH 11/13] update readme --- README.md | 8 ++++++-- example/packages.dhall | 18 +----------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 78bb341a..684445ad 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,13 @@ Data type translation is fully and easily customizable by providing your own `Br ## JSON encoding / decoding -For compatible JSON representations you should be using [aeson](http://hackage.haskell.org/package/aeson)'s generic encoding/decoding with default options -and `encodeJson` and `decodeJson` from "Data.Argonaut.Generic.Aeson" in [purescript-argonaut-generic-codecs](https://github.com/eskimor/purescript-argonaut-generic-codecs). +For compatible JSON representations: +* On Haskell side: + * Use [`aeson`](http://hackage.haskell.org/package/aeson)'s generic encoding/decoding with default options +* On Purescript side: + * Use [`purescript-argonaut-aeson-generic`](https://pursuit.purescript.org/packages/purescript-argonaut-aeson-generic). [This branch](https://github.com/coot/purescript-argonaut-aeson-generic/pull/15) is updated for Purescript 0.14. + * Or use [`purescript-foreign-generic`](https://pursuit.purescript.org/packages/purescript-foreign-generic). ## Documentation diff --git a/example/packages.dhall b/example/packages.dhall index 840e4238..a049ff73 100644 --- a/example/packages.dhall +++ b/example/packages.dhall @@ -17,22 +17,6 @@ let additions = "git://github.com/peterbecich/purescript-argonaut-aeson-generic.git" , version = "2c8c5ee2381ddb786af7fb79a73e3b83001d68e8" } - , foreign-generic = - { dependencies = - [ "console" - , "effect" - , "psci-support" - , "prelude" - , "tuples" - , "bifunctors" - , "foreign" - , "foreign-object" - , "assert" - , "record" - ] - , repo = "git://github.com/paf31/purescript-foreign-generic.git" - , version = "3cddc5fe3e87e426e0f719465ba60b9df4c0c72d" - } } -in upstream // additions +in upstream ⫽ additions From ff0231396174f7d4976fb7b605aaf64b2969a3da Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 12 Jun 2021 20:53:33 -0700 Subject: [PATCH 12/13] update Purescript Github Action --- .github/workflows/purescript.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/purescript.yml b/.github/workflows/purescript.yml index 606358f9..6a51e524 100644 --- a/.github/workflows/purescript.yml +++ b/.github/workflows/purescript.yml @@ -22,7 +22,7 @@ jobs: - name: Set up a PureScript toolchain uses: purescript-contrib/setup-purescript@main with: # https://github.com/purescript-contrib/setup-purescript#specify-versions - purescript: "0.14.0" + purescript: "0.14.1" - name: Cache PureScript dependencies uses: actions/cache@v2 From e13bd8ce90e2f73c821c3ace9c54d1b7a655b0dc Mon Sep 17 00:00:00 2001 From: Peter Becich Date: Sat, 12 Jun 2021 22:17:13 -0700 Subject: [PATCH 13/13] sort Purescript imports --- src/Language/PureScript/Bridge/Printer.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Language/PureScript/Bridge/Printer.hs b/src/Language/PureScript/Bridge/Printer.hs index 404cb7ac..d138d76b 100644 --- a/src/Language/PureScript/Bridge/Printer.hs +++ b/src/Language/PureScript/Bridge/Printer.hs @@ -87,8 +87,8 @@ _lensImports :: Switches.Settings -> [ImportLine] _lensImports settings | Switches.generateLenses settings = [ ImportLine "Data.Lens" Nothing $ Set.fromList ["Iso'", "Prism'", "Lens'", "prism'", "lens"] - , ImportLine "Data.Lens.Record" Nothing $ Set.fromList ["prop"] , ImportLine "Data.Lens.Iso.Newtype" Nothing $ Set.fromList ["_Newtype"] + , ImportLine "Data.Lens.Record" Nothing $ Set.fromList ["prop"] ] <> baseline <> [ ImportLine "Data.Symbol" Nothing $ Set.fromList ["SProxy(SProxy)"] ] @@ -113,8 +113,8 @@ _argonautCodecsImports settings _foreignImports :: Switches.Settings -> [ImportLine] _foreignImports settings | (isJust . Switches.generateForeign) settings = - [ ImportLine "Foreign.Generic" Nothing $ Set.fromList ["defaultOptions", "genericDecode", "genericEncode"] - , ImportLine "Foreign.Class" Nothing $ Set.fromList ["class Decode", "class Encode"] + [ ImportLine "Foreign.Class" Nothing $ Set.fromList ["class Decode", "class Encode"] + , ImportLine "Foreign.Generic" Nothing $ Set.fromList ["defaultOptions", "genericDecode", "genericEncode"] ] | otherwise = mempty