diff --git a/src/Gargantext/Utils/Argonaut.purs b/src/Gargantext/Utils/Argonaut.purs index c8e15c12a2b2bbb611f038d0d289f846a22e1e8c..71a6e70113239e6bf5ce1f4503b7d15629e75a7b 100644 --- a/src/Gargantext/Utils/Argonaut.purs +++ b/src/Gargantext/Utils/Argonaut.purs @@ -5,7 +5,7 @@ import Prelude import Control.Alt ((<|>)) import Data.Argonaut (Json) import Data.Argonaut as Argonaut -import Data.Either (Either) +import Data.Either (Either(..)) import Data.Generic.Rep as GR import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) @@ -92,3 +92,58 @@ instance genericSumEncodeJsonRepArgument :: ( Argonaut.EncodeJson a ) => GenericSumEncodeJsonRep (GR.Argument a) where genericSumEncodeJsonRep (GR.Argument f) = Argonaut.encodeJson f + +genericEnumDecodeJson :: forall a rep + . GR.Generic a rep + => GenericEnumDecodeJson rep + => Json + -> Either String a +genericEnumDecodeJson f = + GR.to <$> genericEnumDecodeJsonRep f + +-- | Generic Enum Sum Representations, with constructor names as strings +class GenericEnumDecodeJson rep where + genericEnumDecodeJsonRep :: Json -> Either String rep + +instance sumEnumDecodeJsonRep :: + ( GenericEnumDecodeJson a + , GenericEnumDecodeJson b + ) => GenericEnumDecodeJson (GR.Sum a b) where + genericEnumDecodeJsonRep f + = GR.Inl <$> genericEnumDecodeJsonRep f + <|> GR.Inr <$> genericEnumDecodeJsonRep f + +instance constructorEnumSumRep :: + ( IsSymbol name + ) => GenericEnumDecodeJson (GR.Constructor name GR.NoArguments) where + genericEnumDecodeJsonRep f = do + s <- Argonaut.decodeJson f + if s == name + then pure $ GR.Constructor GR.NoArguments + else Left $ "Enum string " <> s <> " did not match expected string " <> name + where + name = reflectSymbol (SProxy :: SProxy name) + +genericEnumEncodeJson :: forall a rep + . GR.Generic a rep + => GenericEnumEncodeJson rep + => a + -> Json +genericEnumEncodeJson f = + genericEnumEncodeJsonRep $ GR.from f + +-- | Generic Enum Sum Representations, with constructor names as strings +class GenericEnumEncodeJson rep where + genericEnumEncodeJsonRep :: rep -> Json + +instance sumGenericEnumEncodeJson :: + ( GenericEnumEncodeJson a + , GenericEnumEncodeJson b + ) => GenericEnumEncodeJson (GR.Sum a b) where + genericEnumEncodeJsonRep (GR.Inl x) = genericEnumEncodeJsonRep x + genericEnumEncodeJsonRep (GR.Inr x) = genericEnumEncodeJsonRep x + +instance constructorGenericEnumEncodeJson :: + ( IsSymbol name + ) => GenericEnumEncodeJson (GR.Constructor name GR.NoArguments) where + genericEnumEncodeJsonRep _ = Argonaut.encodeJson $ reflectSymbol (SProxy :: SProxy name) diff --git a/test/Gargantext/Utils/Spec.purs b/test/Gargantext/Utils/Spec.purs index 93edcb10ee3cbac396dc132ccb3d45f2947846b4..201b30e1fa885580dadac1538bbf7d6dbcd37a37 100644 --- a/test/Gargantext/Utils/Spec.purs +++ b/test/Gargantext/Utils/Spec.purs @@ -7,7 +7,7 @@ import Data.Either (Either(..), isLeft) import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) import Gargantext.Utils as GU -import Gargantext.Utils.Argonaut (genericSumDecodeJson, genericSumEncodeJson) +import Gargantext.Utils.Argonaut (genericEnumDecodeJson, genericEnumEncodeJson, genericSumDecodeJson, genericSumEncodeJson) import Gargantext.Utils.Crypto as GUC import Gargantext.Utils.Math as GUM import Test.Spec (Spec, describe, it) @@ -17,7 +17,6 @@ data Fruit = Boat { hi :: Int } | Gravy String | Pork Int - | Chicken derive instance eqFruit :: Eq Fruit derive instance genericFruit :: Generic Fruit _ @@ -28,6 +27,20 @@ instance decodeJsonFruit :: Argonaut.DecodeJson Fruit where instance encodeJsonFruit :: Argonaut.EncodeJson Fruit where encodeJson = genericSumEncodeJson +data EnumTest + = Member1 + | Member2 + | Member3 + +derive instance eqEnumTest :: Eq EnumTest +derive instance genericEnumTest :: Generic EnumTest _ +instance showEnumTest :: Show EnumTest where + show = genericShow +instance decodeJsonEnumTest :: Argonaut.DecodeJson EnumTest where + decodeJson = genericEnumDecodeJson +instance encodeJsonEnumTest :: Argonaut.EncodeJson EnumTest where + encodeJson = genericEnumEncodeJson + spec :: Spec Unit spec = describe "G.Utils" do @@ -78,8 +91,25 @@ spec = Argonaut.stringify result2 `shouldEqual` """{"Gravy":"hi"}""" result2' `shouldEqual` Right input2 - let input3 = Chicken - let result3 = Argonaut.encodeJson input3 - let result3' = Argonaut.decodeJson result3 - Argonaut.stringify result3 `shouldEqual` """{"Chicken":null}""" - result3' `shouldEqual` Right input3 + it "genericEnumDecodeJson works" do + let result1 = Argonaut.decodeJson =<< Argonaut.jsonParser "\"Member1\"" + result1 `shouldEqual` Right Member1 + + let result2 = Argonaut.decodeJson =<< Argonaut.jsonParser "\"Member2\"" + result2 `shouldEqual` Right Member2 + + let result3 = Argonaut.decodeJson =<< Argonaut.jsonParser "\"Failure\"" + isLeft (result3 :: Either String EnumTest) `shouldEqual` true + + it "genericSumEncodeJson works and loops back with decode" do + let input1 = Member1 + let result1 = Argonaut.encodeJson input1 + let result1' = Argonaut.decodeJson result1 + Argonaut.stringify result1 `shouldEqual` "\"Member1\"" + result1' `shouldEqual` Right input1 + + let input2 = Member2 + let result2 = Argonaut.encodeJson input2 + let result2' = Argonaut.decodeJson result2 + Argonaut.stringify result2 `shouldEqual` "\"Member2\"" + result2' `shouldEqual` Right input2