From 9b00fcc6b04bd999d3fd3b9de2ae830bff473a71 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Thu, 18 Jun 2020 23:39:43 -0700 Subject: [PATCH] Standardize style in the library and remove deprecated functions (#82) --- src/Data/Argonaut/Decode.purs | 13 +- src/Data/Argonaut/Decode/Class.purs | 23 +- src/Data/Argonaut/Decode/Combinators.purs | 38 +--- src/Data/Argonaut/Decode/Decoders.purs | 263 ++++++++++++++-------- src/Data/Argonaut/Encode/Class.purs | 6 +- src/Data/Argonaut/Encode/Combinators.purs | 19 +- src/Data/Argonaut/Encode/Encoders.purs | 55 ++--- test/Test/Main.purs | 42 +++- 8 files changed, 269 insertions(+), 190 deletions(-) diff --git a/src/Data/Argonaut/Decode.purs b/src/Data/Argonaut/Decode.purs index c22b7e3..8ffc817 100644 --- a/src/Data/Argonaut/Decode.purs +++ b/src/Data/Argonaut/Decode.purs @@ -6,6 +6,15 @@ module Data.Argonaut.Decode ) where import Data.Argonaut.Decode.Class (class DecodeJson, decodeJson) -import Data.Argonaut.Decode.Combinators (getField, getFieldDeprecated, getFieldOptional, getFieldOptionalDeprecated, getFieldOptional', defaultField, defaultFieldDeprecated, (.:), (.?), (.:!), (.:?), (.??), (.!=), (.?=)) +import Data.Argonaut.Decode.Combinators + ( getField + , getFieldOptional + , getFieldOptional' + , defaultField + , (.:) + , (.:!) + , (.:?) + , (.!=) + ) import Data.Argonaut.Decode.Error (JsonDecodeError(..), printJsonDecodeError) -import Data.Argonaut.Decode.Parser (parseJson) \ No newline at end of file +import Data.Argonaut.Decode.Parser (parseJson) diff --git a/src/Data/Argonaut/Decode/Class.purs b/src/Data/Argonaut/Decode/Class.purs index c033937..3b1f62f 100644 --- a/src/Data/Argonaut/Decode/Class.purs +++ b/src/Data/Argonaut/Decode/Class.purs @@ -1,12 +1,12 @@ module Data.Argonaut.Decode.Class where -import Prelude (class Ord, Unit, Void, bind, ($), (<<<)) +import Data.Argonaut.Decode.Decoders import Data.Argonaut.Core (Json, toObject) import Data.Argonaut.Decode.Error (JsonDecodeError(..)) import Data.Array.NonEmpty (NonEmptyArray) -import Data.Either (Either(..)) import Data.Bifunctor (lmap) +import Data.Either (Either(..)) import Data.Identity (Identity) import Data.List (List) import Data.List.NonEmpty (NonEmptyList) @@ -18,11 +18,11 @@ import Data.String (CodePoint) import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol) import Data.Tuple (Tuple) import Foreign.Object as FO +import Prelude (class Ord, Unit, Void, bind, ($), (<<<)) import Prim.Row as Row import Prim.RowList as RL import Record as Record import Type.Data.RowList (RLProxy(..)) -import Data.Argonaut.Decode.Decoders class DecodeJson a where decodeJson :: Json -> Either JsonDecodeError a @@ -98,7 +98,7 @@ instance decodeRecord decodeJson json = case toObject json of Just object -> gDecodeJson object (RLProxy :: RLProxy list) - Nothing -> Left $ TypeMismatch "Object" + Nothing -> Left $ TypeMismatch "Object" class GDecodeJson (row :: # Type) (list :: RL.RowList) | list -> row where gDecodeJson :: FO.Object Json -> RLProxy list -> Either JsonDecodeError (Record row) @@ -114,19 +114,16 @@ instance gDecodeJsonCons , Row.Lacks field rowTail ) => GDecodeJson row (RL.Cons field value tail) where - gDecodeJson object _ = - let - sProxy :: SProxy field - sProxy = SProxy + gDecodeJson object _ = do + let + _field = SProxy :: SProxy field + fieldName = reflectSymbol _field - fieldName = reflectSymbol sProxy - in case FO.lookup fieldName object of + case FO.lookup fieldName object of Just jsonVal -> do val <- lmap (AtKey fieldName) <<< decodeJson $ jsonVal - rest <- gDecodeJson object (RLProxy :: RLProxy tail) - - Right $ Record.insert sProxy val rest + Right $ Record.insert _field val rest Nothing -> Left $ AtKey fieldName MissingValue diff --git a/src/Data/Argonaut/Decode/Combinators.purs b/src/Data/Argonaut/Decode/Combinators.purs index 4442679..0625cde 100644 --- a/src/Data/Argonaut/Decode/Combinators.purs +++ b/src/Data/Argonaut/Decode/Combinators.purs @@ -1,21 +1,15 @@ module Data.Argonaut.Decode.Combinators ( getField - , getFieldDeprecated , getFieldOptional - , getFieldOptionalDeprecated , getFieldOptional' , defaultField - , defaultFieldDeprecated , (.:) - , (.?) , (.:!) , (.:?) - , (.??) , (.!=) - , (.?=) ) where -import Prelude ((<$>)) +import Prelude import Data.Argonaut.Core (Json) import Data.Argonaut.Decode.Error (JsonDecodeError) @@ -23,7 +17,6 @@ import Data.Argonaut.Decode.Class (class DecodeJson, decodeJson) import Data.Either (Either) import Data.Maybe (Maybe, fromMaybe) import Foreign.Object as FO -import Prim.TypeError (class Warn, Text) import Data.Argonaut.Decode.Decoders as Decoders -- | Attempt to get the value for a given key on an `Object Json`. @@ -35,16 +28,6 @@ getField = Decoders.getField decodeJson infix 7 getField as .: -getFieldDeprecated - :: forall a. Warn ( Text "`.?` is deprecated, use `.:` instead" ) - => DecodeJson a - => FO.Object Json - -> String - -> Either JsonDecodeError a -getFieldDeprecated = getField - -infix 7 getFieldDeprecated as .? - -- | Attempt to get the value for a given key on an `Object Json`. -- | -- | The result will be `Right Nothing` if the key and value are not present, @@ -70,21 +53,11 @@ getFieldOptional = Decoders.getFieldOptional decodeJson infix 7 getFieldOptional as .:! -getFieldOptionalDeprecated - :: forall a. Warn ( Text "`.??` is deprecated, use `.:!` or `.:?` instead" ) - => DecodeJson a - => FO.Object Json - -> String - -> Either JsonDecodeError (Maybe a) -getFieldOptionalDeprecated = Decoders.getFieldOptional decodeJson - -infix 7 getFieldOptionalDeprecated as .?? - -- | Helper for use in combination with `.:?` to provide default values for optional -- | `Object Json` fields. -- | -- | Example usage: --- | ```purescript +-- | ```purs -- | newtype MyType = MyType -- | { foo :: String -- | , bar :: Maybe Int @@ -103,10 +76,3 @@ defaultField :: forall a. Either JsonDecodeError (Maybe a) -> a -> Either JsonDe defaultField parser default = fromMaybe default <$> parser infix 6 defaultField as .!= - -defaultFieldDeprecated - :: forall a. Warn ( Text "`.?=` is deprecated, use `.!=` instead" ) - => Either JsonDecodeError (Maybe a) -> a -> Either JsonDecodeError a -defaultFieldDeprecated = defaultField - -infix 6 defaultFieldDeprecated as .?= diff --git a/src/Data/Argonaut/Decode/Decoders.purs b/src/Data/Argonaut/Decode/Decoders.purs index 9500ef5..d84756d 100644 --- a/src/Data/Argonaut/Decode/Decoders.purs +++ b/src/Data/Argonaut/Decode/Decoders.purs @@ -7,7 +7,7 @@ import Data.Argonaut.Decode.Error (JsonDecodeError(..)) import Data.Array as Arr import Data.Array.NonEmpty (NonEmptyArray) import Data.Array.NonEmpty as NEA -import Data.Bifunctor (lmap, rmap) +import Data.Bifunctor (lmap) import Data.Either (Either(..), note) import Data.Identity (Identity(..)) import Data.Int (fromNumber) @@ -25,31 +25,49 @@ import Data.TraversableWithIndex (traverseWithIndex) import Data.Tuple (Tuple(..)) import Foreign.Object as FO -decodeIdentity :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (Identity a) -decodeIdentity decoder j = Identity <$> decoder j - -decodeMaybe :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (Maybe a) -decodeMaybe decoder j - | isNull j = pure Nothing - | otherwise = Just <$> decoder j - -decodeTuple :: ∀ a b . (Json -> Either JsonDecodeError a) -> (Json -> Either JsonDecodeError b) -> Json -> Either JsonDecodeError (Tuple a b) -decodeTuple decoderA decoderB j = decodeArray Right j >>= f +decodeIdentity + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (Identity a) +decodeIdentity decoder json = Identity <$> decoder json + +decodeMaybe + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (Maybe a) +decodeMaybe decoder json + | isNull json = pure Nothing + | otherwise = Just <$> decoder json + +decodeTuple + :: forall a b + . (Json -> Either JsonDecodeError a) + -> (Json -> Either JsonDecodeError b) + -> Json + -> Either JsonDecodeError (Tuple a b) +decodeTuple decoderA decoderB json = decodeArray Right json >>= f where f :: Array Json -> Either JsonDecodeError (Tuple a b) - f [a, b] = Tuple <$> decoderA a <*> decoderB b - f _ = Left $ TypeMismatch "Tuple" - -decodeEither :: ∀ a b . (Json -> Either JsonDecodeError a) -> (Json -> Either JsonDecodeError b) -> Json -> Either JsonDecodeError (Either a b) -decodeEither decoderA decoderB j = - lmap (Named "Either") $ - decodeJObject j >>= \obj -> do - tag <- maybe (Left $ AtKey "tag" MissingValue) Right $ FO.lookup "tag" obj - val <- maybe (Left $ AtKey "value" MissingValue) Right $ FO.lookup "value" obj - case toString tag of - Just "Right" -> Right <$> decoderB val - Just "Left" -> Left <$> decoderA val - _ -> Left $ AtKey "tag" (UnexpectedValue tag) + f = case _ of + [a, b] -> Tuple <$> decoderA a <*> decoderB b + _ -> Left $ TypeMismatch "Tuple" + +decodeEither + :: forall a b + . (Json -> Either JsonDecodeError a) + -> (Json -> Either JsonDecodeError b) + -> Json + -> Either JsonDecodeError (Either a b) +decodeEither decoderA decoderB json = + lmap (Named "Either") $ decodeJObject json >>= \obj -> do + tag <- note (AtKey "tag" MissingValue) $ FO.lookup "tag" obj + val <- note (AtKey "value" MissingValue) $ FO.lookup "value" obj + case toString tag of + Just "Right" -> Right <$> decoderB val + Just "Left" -> Left <$> decoderA val + _ -> Left $ AtKey "tag" (UnexpectedValue tag) decodeNull :: Json -> Either JsonDecodeError Unit decodeNull = caseJsonNull (Left $ TypeMismatch "null") (const $ Right unit) @@ -61,96 +79,161 @@ decodeNumber :: Json -> Either JsonDecodeError Number decodeNumber = caseJsonNumber (Left $ TypeMismatch "Number") Right decodeInt :: Json -> Either JsonDecodeError Int -decodeInt = - maybe (Left $ TypeMismatch "Integer") Right - <<< fromNumber - <=< decodeNumber +decodeInt = note (TypeMismatch "Integer") <<< fromNumber <=< decodeNumber decodeString :: Json -> Either JsonDecodeError String decodeString = caseJsonString (Left $ TypeMismatch "String") Right -decodeNonEmpty_Array :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (NonEmpty Array a) -decodeNonEmpty_Array decoder = - lmap (Named "NonEmpty Array") - <<< (traverse decoder <=< (rmap (\x -> x.head :| x.tail) <<< note (TypeMismatch "NonEmpty Array") <<< Arr.uncons) <=< decodeJArray) - -decodeNonEmptyArray :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (NonEmptyArray a) +decodeNonEmpty_Array + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (NonEmpty Array a) +decodeNonEmpty_Array decoder = + lmap (Named "NonEmpty Array") + <<< traverse decoder + <=< map (\x -> x.head :| x.tail) + <<< note (TypeMismatch "NonEmpty Array") + <<< Arr.uncons + <=< decodeJArray + +decodeNonEmptyArray + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (NonEmptyArray a) decodeNonEmptyArray decoder = lmap (Named "NonEmptyArray") - <<< (traverse decoder <=< (rmap (\x -> NEA.cons' x.head x.tail) <<< note (TypeMismatch "NonEmptyArray") <<< Arr.uncons) <=< decodeJArray) - -decodeNonEmpty_List :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (NonEmpty List a) + <<< traverse decoder + <=< map (\x -> NEA.cons' x.head x.tail) + <<< note (TypeMismatch "NonEmptyArray") + <<< Arr.uncons + <=< decodeJArray + +decodeNonEmpty_List + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (NonEmpty List a) decodeNonEmpty_List decoder = lmap (Named "NonEmpty List") - <<< (traverse decoder <=< (rmap (\x -> x.head :| x.tail) <<< note (TypeMismatch "NonEmpty List") <<< L.uncons) <=< map (map fromFoldable) decodeJArray) - -decodeNonEmptyList :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (NonEmptyList a) + <<< traverse decoder + <=< map (\x -> x.head :| x.tail) + <<< note (TypeMismatch "NonEmpty List") + <<< L.uncons + <=< map (map fromFoldable) decodeJArray + +decodeNonEmptyList + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (NonEmptyList a) decodeNonEmptyList decoder = lmap (Named "NonEmptyList") - <<< (traverse decoder <=< (rmap (\x -> NEL.cons' x.head x.tail) <<< note (TypeMismatch "NonEmptyList") <<< L.uncons) <=< map (map fromFoldable) decodeJArray) + <<< traverse decoder + <=< map (\x -> NEL.cons' x.head x.tail) + <<< note (TypeMismatch "NonEmptyList") + <<< L.uncons + <=< map (map fromFoldable) decodeJArray decodeCodePoint :: Json -> Either JsonDecodeError CodePoint -decodeCodePoint j = - maybe (Left $ Named "CodePoint" $ UnexpectedValue j) Right - =<< codePointAt 0 <$> decodeString j - -decodeForeignObject :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (FO.Object a) +decodeCodePoint json = + note (Named "CodePoint" $ UnexpectedValue json) + =<< map (codePointAt 0) (decodeString json) + +decodeForeignObject + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (FO.Object a) decodeForeignObject decoder = - lmap (Named "ForeignObject") - <<< (traverse decoder <=< decodeJObject) - -decodeArray :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (Array a) + lmap (Named "ForeignObject") + <<< traverse decoder + <=< decodeJObject + +decodeArray + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (Array a) decodeArray decoder = lmap (Named "Array") - <<< (traverseWithIndex f <=< decodeJArray) - where - msg i m = AtIndex i m - f i = lmap (msg i) <<< decoder - -decodeList :: ∀ a . (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (List a) + <<< traverseWithIndex (\i -> lmap (AtIndex i) <<< decoder) + <=< decodeJArray + +decodeList + :: forall a + . (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (List a) decodeList decoder = lmap (Named "List") - <<< (traverse decoder <=< map (map fromFoldable) decodeJArray) - -decodeSet :: ∀ a . Ord a => (Json -> Either JsonDecodeError a) -> Json -> Either JsonDecodeError (S.Set a) -decodeSet decoder = map (S.fromFoldable :: List a -> S.Set a) <<< decodeList decoder - -decodeMap :: ∀ a b . Ord a => (Json -> Either JsonDecodeError a) -> (Json -> Either JsonDecodeError b) -> Json -> Either JsonDecodeError (M.Map a b) -decodeMap decoderA decoderB = map (M.fromFoldable :: List (Tuple a b) -> M.Map a b) <<< decodeList (decodeTuple decoderA decoderB) + <<< traverse decoder + <=< map (map fromFoldable) decodeJArray + +decodeSet + :: forall a + . Ord a + => (Json -> Either JsonDecodeError a) + -> Json + -> Either JsonDecodeError (S.Set a) +decodeSet decoder = + map (S.fromFoldable :: List a -> S.Set a) <<< decodeList decoder + +decodeMap + :: forall a b + . Ord a + => (Json -> Either JsonDecodeError a) + -> (Json -> Either JsonDecodeError b) + -> Json + -> Either JsonDecodeError (M.Map a b) +decodeMap decoderA decoderB = + map (M.fromFoldable :: List (Tuple a b) -> M.Map a b) + <<< decodeList (decodeTuple decoderA decoderB) decodeVoid :: Json -> Either JsonDecodeError Void decodeVoid _ = Left $ UnexpectedValue $ fromString "Value cannot be Void" decodeJArray :: Json -> Either JsonDecodeError (Array Json) -decodeJArray = maybe (Left $ TypeMismatch "Array") Right <<< toArray +decodeJArray = note (TypeMismatch "Array") <<< toArray decodeJObject :: Json -> Either JsonDecodeError (FO.Object Json) -decodeJObject = maybe (Left $ TypeMismatch "Object") Right <<< toObject - -getField :: forall a. (Json -> Either JsonDecodeError a) -> FO.Object Json -> String -> Either JsonDecodeError a -getField decoder o s = +decodeJObject = note (TypeMismatch "Object") <<< toObject + +getField + :: forall a + . (Json -> Either JsonDecodeError a) + -> FO.Object Json + -> String + -> Either JsonDecodeError a +getField decoder obj str = maybe - (Left $ AtKey s MissingValue) - (lmap (AtKey s) <<< decoder) - (FO.lookup s o) - -getFieldOptional :: forall a. (Json -> Either JsonDecodeError a) -> FO.Object Json -> String -> Either JsonDecodeError (Maybe a) -getFieldOptional decoder o s = - maybe - (pure Nothing) - decode - (FO.lookup s o) + (Left $ AtKey str MissingValue) + (lmap (AtKey str) <<< decoder) + (FO.lookup str obj) + +getFieldOptional + :: forall a + . (Json -> Either JsonDecodeError a) + -> FO.Object Json + -> String + -> Either JsonDecodeError (Maybe a) +getFieldOptional decoder obj str = + maybe (pure Nothing) (map Just <<< decode) (FO.lookup str obj) where - decode json = Just <$> (lmap (AtKey s) <<< decoder) json - -getFieldOptional' :: forall a. (Json -> Either JsonDecodeError a) -> FO.Object Json -> String -> Either JsonDecodeError (Maybe a) -getFieldOptional' decoder o s = - maybe - (pure Nothing) - decode - (FO.lookup s o) + decode = lmap (AtKey str) <<< decoder + +getFieldOptional' + :: forall a + . (Json -> Either JsonDecodeError a) + -> FO.Object Json + -> String + -> Either JsonDecodeError (Maybe a) +getFieldOptional' decoder obj str = + maybe (pure Nothing) decode (FO.lookup str obj) where - decode json = - if isNull json - then pure Nothing - else Just <$> (lmap (AtKey s) <<< decoder) json + decode json = + if isNull json then + pure Nothing + else + Just <$> (lmap (AtKey str) <<< decoder) json diff --git a/src/Data/Argonaut/Encode/Class.purs b/src/Data/Argonaut/Encode/Class.purs index 994e828..3d39fce 100644 --- a/src/Data/Argonaut/Encode/Class.purs +++ b/src/Data/Argonaut/Encode/Class.purs @@ -112,8 +112,8 @@ instance gEncodeJsonCons ) => GEncodeJson row (RL.Cons field value tail) where gEncodeJson row _ = do - let sProxy = SProxy :: SProxy field + let _field = SProxy :: SProxy field FO.insert - (reflectSymbol sProxy) - (encodeJson $ Record.get sProxy row) + (reflectSymbol _field) + (encodeJson $ Record.get _field row) (gEncodeJson row (RLProxy :: RLProxy tail)) diff --git a/src/Data/Argonaut/Encode/Combinators.purs b/src/Data/Argonaut/Encode/Combinators.purs index 1553aff..66e564f 100644 --- a/src/Data/Argonaut/Encode/Combinators.purs +++ b/src/Data/Argonaut/Encode/Combinators.purs @@ -1,29 +1,30 @@ -- | Provides operators for a DSL to construct `Json` values: -- | --- | ``` purescript --- | myJson --- | = "key1" := value1 --- | ~> "key2" :=? value2 --- | ~>? "key3" := value3 --- | ~> jsonEmptyObject +-- | ```purs +-- | myJson = +-- | "key1" := value1 +-- | ~> "key2" :=? value2 +-- | ~>? "key3" := value3 +-- | ~> jsonEmptyObject -- | ``` module Data.Argonaut.Encode.Combinators where - import Data.Argonaut.Core (Json) import Data.Argonaut.Encode.Class (class EncodeJson, encodeJson) import Data.Maybe (Maybe) import Data.Tuple (Tuple) import Data.Argonaut.Encode.Encoders as Encoders --- | Creates a `Tuple String Json` entry, representing a key/value pair for an object. +-- | Creates a `Tuple String Json` entry, representing a key/value pair for +-- | an object. infix 7 assoc as := -- | The named Encoders of the `(:=)` operator. assoc :: forall a. EncodeJson a => String -> a -> Tuple String Json assoc = Encoders.assoc encodeJson --- | Creates an optional `Tuple String Json` entry, representing an optional key/value pair for an object. +-- | Creates an optional `Tuple String Json` entry, representing an optional +-- | key/value pair for an object. infix 7 assocOptional as :=? -- | The named Encoders of the `(:=?)` operator. diff --git a/src/Data/Argonaut/Encode/Encoders.purs b/src/Data/Argonaut/Encode/Encoders.purs index 79fa7be..7c07469 100644 --- a/src/Data/Argonaut/Encode/Encoders.purs +++ b/src/Data/Argonaut/Encode/Encoders.purs @@ -22,25 +22,25 @@ import Data.String.CodeUnits as CU import Data.Tuple (Tuple(..)) import Foreign.Object as FO -encodeIdentity :: forall a . (a -> Json) -> Identity a -> Json +encodeIdentity :: forall a. (a -> Json) -> Identity a -> Json encodeIdentity encoder (Identity a) = encoder a -encodeMaybe :: forall a . (a -> Json) -> Maybe a -> Json +encodeMaybe :: forall a. (a -> Json) -> Maybe a -> Json encodeMaybe encoder = case _ of - Nothing -> jsonNull - Just a -> encoder a + Nothing -> jsonNull + Just a -> encoder a -encodeTuple :: forall a b . (a -> Json) -> (b -> Json) -> Tuple a b -> Json +encodeTuple :: forall a b. (a -> Json) -> (b -> Json) -> Tuple a b -> Json encodeTuple encoderA encoderB (Tuple a b) = fromArray [encoderA a, encoderB b] encodeEither :: forall a b . (a -> Json) -> (b -> Json) -> Either a b -> Json encodeEither encoderA encoderB = either (obj encoderA "Left") (obj encoderB "Right") - where - obj :: forall c. (c -> Json) -> String -> c -> Json - obj encoder tag x = - fromObject - $ FO.fromFoldable - $ Tuple "tag" (fromString tag) : Tuple "value" (encoder x) : Nil + where + obj :: forall c. (c -> Json) -> String -> c -> Json + obj encoder tag x = + fromObject + $ FO.fromFoldable + $ Tuple "tag" (fromString tag) : Tuple "value" (encoder x) : Nil encodeUnit :: Unit -> Json encodeUnit = const jsonNull @@ -60,35 +60,37 @@ encodeString = fromString encodeCodePoint :: CodePoint -> Json encodeCodePoint = encodeString <<< CP.singleton -encodeNonEmpty_Array :: forall a . ((a -> Json)) -> NonEmpty Array a -> Json +encodeNonEmpty_Array :: forall a. (a -> Json) -> NonEmpty Array a -> Json encodeNonEmpty_Array encoder (NonEmpty h t) = encodeArray encoder (Arr.cons h t) -encodeNonEmptyArray :: forall a . ((a -> Json)) -> NonEmptyArray a -> Json +encodeNonEmptyArray :: forall a. (a -> Json) -> NonEmptyArray a -> Json encodeNonEmptyArray encoder = encodeArray encoder <<< NEA.toArray -encodeNonEmpty_List :: forall a . ((a -> Json)) -> NonEmpty List a -> Json +encodeNonEmpty_List :: forall a. (a -> Json) -> NonEmpty List a -> Json encodeNonEmpty_List encoder (NonEmpty h t) = encodeList encoder (h : t) -encodeNonEmptyList :: forall a . ((a -> Json)) -> NonEmptyList a -> Json +encodeNonEmptyList :: forall a. (a -> Json) -> NonEmptyList a -> Json encodeNonEmptyList encoder = encodeList encoder <<< NEL.toList encodeChar :: Char -> Json encodeChar = encodeString <<< CU.singleton -encodeArray :: forall a . (a -> Json) -> Array a -> Json +encodeArray :: forall a. (a -> Json) -> Array a -> Json encodeArray encoder = fromArray <<< map encoder -encodeList :: forall a . (a -> Json) -> List a -> Json +encodeList :: forall a. (a -> Json) -> List a -> Json encodeList encoder = fromArray <<< map encoder <<< toUnfoldable -encodeForeignObject :: forall a . (a -> Json) -> FO.Object a -> Json +encodeForeignObject :: forall a. (a -> Json) -> FO.Object a -> Json encodeForeignObject encoder = fromObject <<< map encoder -encodeSet :: forall a . (Ord a) => (a -> Json) -> S.Set a -> Json +encodeSet :: forall a. Ord a => (a -> Json) -> S.Set a -> Json encodeSet encoder = encodeList encoder <<< (S.toUnfoldable :: S.Set a -> List a) -encodeMap :: forall a b . (Ord a) => (a -> Json) -> (b -> Json) -> M.Map a b -> Json -encodeMap encoderA encoderB = encodeList (encodeTuple encoderA encoderB) <<< (M.toUnfoldable :: M.Map a b -> List (Tuple a b)) +encodeMap :: forall a b. Ord a => (a -> Json) -> (b -> Json) -> M.Map a b -> Json +encodeMap encoderA encoderB = + encodeList (encodeTuple encoderA encoderB) + <<< (M.toUnfoldable :: M.Map a b -> List (Tuple a b)) encodeVoid :: Void -> Json encodeVoid = absurd @@ -102,16 +104,15 @@ assocOptional -> String -> Maybe a -> Maybe (Tuple String Json) -assocOptional encoder k = (<$>) (Tuple k <<< encoder) +assocOptional encoder k = map (Tuple k <<< encoder) extend :: forall a. (a -> Json) -> Tuple String Json -> a -> Json extend encoder (Tuple k v) = - caseJsonObject - (jsonSingletonObject k v) - (FO.insert k v >>> fromObject) + caseJsonObject (jsonSingletonObject k v) (fromObject <<< FO.insert k v) <<< encoder -- | The named Encoders of the `(~>?)` operator. extendOptional :: forall a. (a -> Json) -> Maybe (Tuple String Json) -> a -> Json -extendOptional encoder (Just kv) = extend encoder kv -extendOptional encoder Nothing = encoder +extendOptional encoder = case _ of + Just kv -> extend encoder kv + Nothing -> encoder diff --git a/test/Test/Main.purs b/test/Test/Main.purs index ab74bc2..b377b20 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -123,14 +123,19 @@ combinatorsCheck :: Test combinatorsCheck = do test "Check assoc builder `:=`" do quickCheck propAssocBuilderStr + test "Check assocOptional builder `:=?`" do quickCheck propAssocOptionalBuilderStr + test "Check JAssoc append `~>`" do quickCheck propAssocAppend + test "Check JAssoc appendOptional `~>?`" do quickCheck propAssocAppendOptional + test "Check get field `obj .: 'foo'`" do -- this doesn't really test .: quickCheck propGetJObjectField + where propAssocBuilderStr :: Gen Result propAssocBuilderStr = do @@ -173,6 +178,7 @@ combinatorsCheck = do propGetJObjectField = do obj <- genObj pure (true === maybe false go (toObject obj)) + where go :: FO.Object Json -> Boolean go object = @@ -215,14 +221,17 @@ manualRecordDecode = do case decodeJson fooNestedEmptyJson of Right (FooNested { bar: Nothing, baz: false }) -> pure unit _ -> failure ("Failed to properly decode JSON string: " <> stringify fooNestedEmptyJson) + test "Json with null values should fail to decode to FooNested" do case decodeJson fooNestedEmptyJsonNull of Right (FooNested _) -> failure ("Should have failed to decode JSON string: " <> stringify fooNestedEmptyJsonNull) _ -> pure unit + test "Empty Json should decode to FooNested'" do case decodeJson fooNestedEmptyJson of Right (FooNested' { bar: Nothing, baz: false }) -> pure unit _ -> failure ("Failed to properly decode JSON string: " <> stringify fooNestedEmptyJson) + test "Json with null values should decode to FooNested'" do case decodeJson fooNestedEmptyJsonNull of Right (FooNested' { bar: Nothing, baz: false }) -> pure unit @@ -234,14 +243,17 @@ manualRecordDecode = do case decodeJson fooNestedBazJson of Right (FooNested { bar: Nothing, baz: true }) -> pure unit _ -> failure ("Failed to properly decode JSON string: " <> stringify fooNestedBazJson) + test "Null 'bar' key should fail to decode to FooNested" do case decodeJson fooNestedBazJsonNull of Right (FooNested _) -> failure ("Should have failed to decode JSON string: " <> stringify fooNestedBazJsonNull) _ -> pure unit + test "Missing 'bar' key should decode to FooNested'" do case decodeJson fooNestedBazJson of Right (FooNested' { bar: Nothing, baz: true }) -> pure unit _ -> failure ("Failed to properly decode JSON string: " <> stringify fooNestedBazJson) + test "Null 'bar' key should decode to FooNested'" do case decodeJson fooNestedBazJsonNull of Right (FooNested' { bar: Nothing, baz: true }) -> pure unit @@ -253,14 +265,17 @@ manualRecordDecode = do case decodeJson fooNestedBarJson of Right (FooNested { bar: Just [1], baz: false }) -> pure unit _ -> failure ("Failed to properly decode JSON string: " <> stringify fooNestedBarJson) + test "Null 'baz' key should fail to decode to FooNested" do case decodeJson fooNestedBarJsonNull of Right (FooNested _) -> failure ("Should have failed to decode JSON string: " <> stringify fooNestedBarJsonNull) _ -> pure unit + test "Missing 'baz' key should decode to FooNested'" do case decodeJson fooNestedBarJson of Right (FooNested' { bar: Just [1], baz: false }) -> pure unit _ -> failure ("Failed to properly decode JSON string: " <> stringify fooNestedBarJson) + test "Null 'baz' key should decode to FooNested'" do case decodeJson fooNestedBarJsonNull of Right (FooNested' { bar: Just [1], baz: false }) -> pure unit @@ -272,6 +287,7 @@ manualRecordDecode = do case decodeJson fooNestedFullJson of Right (FooNested { bar: Just [1], baz: true }) -> pure unit _ -> failure ("Failed to properly decode JSON string: " <> stringify fooNestedFullJson) + test "Json should decode to FooNested'" do case decodeJson fooNestedFullJson of Right (FooNested { bar: Just [1], baz: true }) -> pure unit @@ -281,6 +297,7 @@ manualRecordDecode = do case decodeJson fooJson of Right (Foo _) -> pure unit Left err -> failure $ printJsonDecodeError err + suite "Test decoding empty record" testEmptyCases suite "Test decoding missing 'bar' key" testBarCases suite "Test decoding missing 'baz' key" testBazCases @@ -296,6 +313,7 @@ nonEmptyCheck = do ("x = " <> show x <> ", decoded = " <> show decoded) Left err -> false printJsonDecodeError err + test "Test EncodeJson/DecodeJson on NonEmptyArray" do quickCheck \(x :: NonEmptyArray String) -> case decodeJson (encodeJson x) of @@ -304,6 +322,7 @@ nonEmptyCheck = do ("x = " <> show x <> ", decoded = " <> show decoded) Left err -> false printJsonDecodeError err + test "Test EncodeJson/DecodeJson on NonEmpty List" do quickCheck \(x :: NonEmpty List String) -> case decodeJson (encodeJson x) of @@ -312,6 +331,7 @@ nonEmptyCheck = do ("x = " <> show x <> ", decoded = " <> show decoded) Left err -> false printJsonDecodeError err + test "Test EncodeJson/DecodeJson on NonEmptyList" do quickCheck \(x :: NonEmptyList String) -> case decodeJson (encodeJson x) of @@ -337,28 +357,30 @@ errorMsgCheck = do case notBar of Left err -> assertEqual { expected: barErr, actual: printJsonDecodeError err } _ -> failure "Should have failed to decode" + test "Test that decoding record fails with the proper message" do case notBaz of Left err -> assertEqual { expected: bazErr, actual: printJsonDecodeError err } _ -> failure "Should have failed to decode" + where barErr :: String barErr = joinWith "\n" - [ "An error occurred while decoding a JSON value:" - , " At object key 'bar':" - , " Under 'Array':" - , " At array index 1:" - , " Expected value of type 'Number'." - ] + [ "An error occurred while decoding a JSON value:" + , " At object key 'bar':" + , " Under 'Array':" + , " At array index 1:" + , " Expected value of type 'Number'." + ] bazErr :: String bazErr = joinWith "\n" - [ "An error occurred while decoding a JSON value:" - , " At object key 'baz':" - , " Expected value of type 'Boolean'." - ] + [ "An error occurred while decoding a JSON value:" + , " At object key 'baz':" + , " Expected value of type 'Boolean'." + ] newtype Foo = Foo { bar :: Array Int