unjson 0.14.1.3 → 0.15.0.0
raw patch · 4 files changed
+125/−49 lines, 4 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
- Data.Unjson: instance (Data.Primitive.Types.Prim a, Data.Unjson.Unjson a) => Data.Unjson.Unjson (Data.Vector.Primitive.Vector a)
- Data.Unjson: instance (Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector a, Data.Unjson.Unjson a, Data.Vector.Unboxed.Base.Unbox a) => Data.Unjson.Unjson (Data.Vector.Unboxed.Base.Vector a)
- Data.Unjson: instance (Foreign.Storable.Storable a, Data.Unjson.Unjson a) => Data.Unjson.Unjson (Data.Vector.Storable.Vector a)
- Data.Unjson: instance (GHC.Classes.Eq a, Data.Hashable.Class.Hashable a, Data.Unjson.Unjson a) => Data.Unjson.Unjson (Data.HashSet.HashSet a)
- Data.Unjson: instance (GHC.Classes.Ord a, Data.Unjson.Unjson a) => Data.Unjson.Unjson (Data.Set.Base.Set a)
- Data.Unjson: instance Data.Unjson.Unjson Data.IntSet.Base.IntSet
- Data.Unjson: instance Data.Unjson.Unjson Data.Time.Clock.UTC.UTCTime
- Data.Unjson: instance Data.Unjson.Unjson Data.Time.LocalTime.LocalTime.ZonedTime
- Data.Unjson: instance Data.Unjson.Unjson a => Data.Unjson.Unjson (Data.IntMap.Base.IntMap a)
- Data.Unjson: instance Data.Unjson.Unjson a => Data.Unjson.Unjson (Data.Vector.Vector a)
- Data.Unjson: instance Data.Unjson.Unjson a => Data.Unjson.Unjson [a]
- Data.Unjson: instance Data.Unjson.Unjson v => Data.Unjson.Unjson (Data.HashMap.Base.HashMap Data.Text.Internal.Lazy.Text v)
- Data.Unjson: instance Data.Unjson.Unjson v => Data.Unjson.Unjson (Data.HashMap.Base.HashMap Data.Text.Internal.Text v)
- Data.Unjson: instance Data.Unjson.Unjson v => Data.Unjson.Unjson (Data.HashMap.Base.HashMap GHC.Base.String v)
- Data.Unjson: instance Data.Unjson.Unjson v => Data.Unjson.Unjson (Data.Map.Base.Map Data.Text.Internal.Lazy.Text v)
- Data.Unjson: instance Data.Unjson.Unjson v => Data.Unjson.Unjson (Data.Map.Base.Map Data.Text.Internal.Text v)
- Data.Unjson: instance Data.Unjson.Unjson v => Data.Unjson.Unjson (Data.Map.Base.Map GHC.Base.String v)
+ Data.Unjson: enumUnjsonDef :: forall a. (Eq a, Typeable a, Enum a, Bounded a, Data a) => UnjsonDef a
+ Data.Unjson: instance (Data.Primitive.Types.Prim a, Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.Vector.Primitive.Vector a)
+ Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.HashMap.Base.HashMap Data.Text.Internal.Lazy.Text v)
+ Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.HashMap.Base.HashMap Data.Text.Internal.Text v)
+ Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.HashMap.Base.HashMap GHC.Base.String v)
+ Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.Map.Internal.Map Data.Text.Internal.Lazy.Text v)
+ Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.Map.Internal.Map Data.Text.Internal.Text v)
+ Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.Map.Internal.Map GHC.Base.String v)
+ Data.Unjson: instance (Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.IntMap.Internal.IntMap a)
+ Data.Unjson: instance (Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.Vector.Vector a)
+ Data.Unjson: instance (Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson [a]
+ Data.Unjson: instance (Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector a, Data.Unjson.Unjson a, Data.Vector.Unboxed.Base.Unbox a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.Vector.Unboxed.Base.Vector a)
+ Data.Unjson: instance (Foreign.Storable.Storable a, Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.Vector.Storable.Vector a)
+ Data.Unjson: instance (GHC.Classes.Eq a, Data.Hashable.Class.Hashable a, Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.HashSet.HashSet a)
+ Data.Unjson: instance (GHC.Classes.Ord a, Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.Set.Internal.Set a)
+ Data.Unjson: instance Data.Unjson.Unjson Data.IntSet.Internal.IntSet
+ Data.Unjson: instance Data.Unjson.Unjson Data.Time.Clock.Internal.UTCTime.UTCTime
+ Data.Unjson: instance Data.Unjson.Unjson Data.Time.LocalTime.Internal.ZonedTime.ZonedTime
- Data.Unjson: [ArrayUnjsonDef] :: Maybe (PrimaryKeyExtraction k) -> ArrayMode -> ([k] -> Result v) -> (v -> [k]) -> UnjsonDef k -> UnjsonDef v
+ Data.Unjson: [ArrayUnjsonDef] :: Typeable k => Maybe (PrimaryKeyExtraction k) -> ArrayMode -> ([k] -> Result v) -> (v -> [k]) -> UnjsonDef k -> UnjsonDef v
- Data.Unjson: [FieldDefDef] :: Text -> Text -> a -> (s -> a) -> UnjsonDef a -> FieldDef s a
+ Data.Unjson: [FieldDefDef] :: Typeable a => Text -> Text -> a -> (s -> a) -> UnjsonDef a -> FieldDef s a
- Data.Unjson: [FieldOptDef] :: Text -> Text -> (s -> Maybe a) -> UnjsonDef a -> FieldDef s (Maybe a)
+ Data.Unjson: [FieldOptDef] :: Typeable a => Text -> Text -> (s -> Maybe a) -> UnjsonDef a -> FieldDef s (Maybe a)
- Data.Unjson: [FieldRODef] :: Text -> Text -> (s -> a) -> UnjsonDef a -> FieldDef s ()
+ Data.Unjson: [FieldRODef] :: Typeable a => Text -> Text -> (s -> a) -> UnjsonDef a -> FieldDef s ()
- Data.Unjson: [FieldReqDef] :: Text -> Text -> (s -> a) -> UnjsonDef a -> FieldDef s a
+ Data.Unjson: [FieldReqDef] :: Typeable a => Text -> Text -> (s -> a) -> UnjsonDef a -> FieldDef s a
- Data.Unjson: [MapUnjsonDef] :: UnjsonDef k -> (HashMap Text k -> Result v) -> (v -> HashMap Text k) -> UnjsonDef v
+ Data.Unjson: [MapUnjsonDef] :: Typeable k => UnjsonDef k -> (HashMap Text k -> Result v) -> (v -> HashMap Text k) -> UnjsonDef v
- Data.Unjson: arrayOf :: UnjsonDef a -> UnjsonDef [a]
+ Data.Unjson: arrayOf :: Typeable a => UnjsonDef a -> UnjsonDef [a]
- Data.Unjson: arrayWithModeAndPrimaryKeyOf :: (Ord pk) => ArrayMode -> (a -> pk) -> UnjsonDef pk -> UnjsonDef a -> UnjsonDef [a]
+ Data.Unjson: arrayWithModeAndPrimaryKeyOf :: (Ord pk, Typeable a) => ArrayMode -> (a -> pk) -> UnjsonDef pk -> UnjsonDef a -> UnjsonDef [a]
- Data.Unjson: arrayWithModeOf :: ArrayMode -> UnjsonDef a -> UnjsonDef [a]
+ Data.Unjson: arrayWithModeOf :: Typeable a => ArrayMode -> UnjsonDef a -> UnjsonDef [a]
- Data.Unjson: arrayWithPrimaryKeyOf :: (Ord pk) => (a -> pk) -> UnjsonDef pk -> UnjsonDef a -> UnjsonDef [a]
+ Data.Unjson: arrayWithPrimaryKeyOf :: (Ord pk, Typeable a) => (a -> pk) -> UnjsonDef pk -> UnjsonDef a -> UnjsonDef [a]
- Data.Unjson: field :: (Unjson a) => Text -> (s -> a) -> Text -> Ap (FieldDef s) a
+ Data.Unjson: field :: (Unjson a, Typeable a) => Text -> (s -> a) -> Text -> Ap (FieldDef s) a
- Data.Unjson: fieldBy :: Text -> (s -> a) -> Text -> UnjsonDef a -> Ap (FieldDef s) a
+ Data.Unjson: fieldBy :: Typeable a => Text -> (s -> a) -> Text -> UnjsonDef a -> Ap (FieldDef s) a
- Data.Unjson: fieldDef :: (Unjson a) => Text -> a -> (s -> a) -> Text -> Ap (FieldDef s) a
+ Data.Unjson: fieldDef :: (Unjson a, Typeable a) => Text -> a -> (s -> a) -> Text -> Ap (FieldDef s) a
- Data.Unjson: fieldDefBy :: Text -> a -> (s -> a) -> Text -> UnjsonDef a -> Ap (FieldDef s) a
+ Data.Unjson: fieldDefBy :: Typeable a => Text -> a -> (s -> a) -> Text -> UnjsonDef a -> Ap (FieldDef s) a
- Data.Unjson: fieldOpt :: (Unjson a) => Text -> (s -> Maybe a) -> Text -> Ap (FieldDef s) (Maybe a)
+ Data.Unjson: fieldOpt :: (Unjson a, Typeable a) => Text -> (s -> Maybe a) -> Text -> Ap (FieldDef s) (Maybe a)
- Data.Unjson: fieldOptBy :: Text -> (s -> Maybe a) -> Text -> UnjsonDef a -> Ap (FieldDef s) (Maybe a)
+ Data.Unjson: fieldOptBy :: Typeable a => Text -> (s -> Maybe a) -> Text -> UnjsonDef a -> Ap (FieldDef s) (Maybe a)
- Data.Unjson: fieldReadonly :: (Unjson a) => Text -> (s -> a) -> Text -> Ap (FieldDef s) ()
+ Data.Unjson: fieldReadonly :: (Unjson a, Typeable a) => Text -> (s -> a) -> Text -> Ap (FieldDef s) ()
- Data.Unjson: fieldReadonlyBy :: Text -> (s -> a) -> Text -> UnjsonDef a -> Ap (FieldDef s) ()
+ Data.Unjson: fieldReadonlyBy :: Typeable a => Text -> (s -> a) -> Text -> UnjsonDef a -> Ap (FieldDef s) ()
- Data.Unjson: mapOf :: UnjsonDef x -> UnjsonDef (HashMap Text x)
+ Data.Unjson: mapOf :: Typeable x => UnjsonDef x -> UnjsonDef (HashMap Text x)
Files
- CHANGELOG.md +6/−0
- src/Data/Unjson.hs +53/−34
- test/Test.hs +60/−9
- unjson.cabal +6/−6
+ CHANGELOG.md view
@@ -0,0 +1,6 @@+# unjson-0.15.0.0 (2018-02-22)+* Breaking change: Unjson definitions now require the underlying types+ to have Typeable instances ([#6](https://github.com/scrive/unjson/pull/6)).+* Added a way to automatically derive Unjson definitions for+ parameterless sum types (`enumUnjsonDef`) ([#7](https://github.com/scrive/unjson/pull/7)).+* Dropped support for GHC < 7.8.
src/Data/Unjson.hs view
@@ -122,6 +122,7 @@ -- ** Maps, enums, sums , mapOf , enumOf+, enumUnjsonDef , disjointUnionOf , unionOf -- ** Helpers@@ -317,7 +318,7 @@ -- 'UnjsonDef'. unjsonDef :: UnjsonDef a -instance {-# OVERLAPPABLE #-} (Unjson a) => Unjson [a] where+instance {-# OVERLAPPABLE #-} (Unjson a, Typeable a) => Unjson [a] where unjsonDef = arrayOf unjsonDef instance {-# INCOHERENT #-} Unjson String where@@ -364,44 +365,44 @@ instance (Unjson a, Unjson b) => Unjson (Either a b) where unjsonDef = unjsonAeson -} -instance Unjson a => Unjson (IntMap.IntMap a)+instance (Unjson a, Typeable a) => Unjson (IntMap.IntMap a) where unjsonDef = invmap IntMap.fromList IntMap.toList unjsonDef-instance (Ord a, Unjson a) => Unjson (Set.Set a)+instance (Ord a, Unjson a, Typeable a) => Unjson (Set.Set a) where unjsonDef = invmap Set.fromList Set.toList unjsonDef-instance (Eq a, Hashable a, Unjson a) => Unjson (HashSet.HashSet a)+instance (Eq a, Hashable a, Unjson a, Typeable a) => Unjson (HashSet.HashSet a) where unjsonDef = invmap HashSet.fromList HashSet.toList unjsonDef-instance Unjson a => Unjson (Vector.Vector a)+instance (Unjson a, Typeable a) => Unjson (Vector.Vector a) where unjsonDef = invmap Vector.fromList Vector.toList unjsonDef-instance (Data.Vector.Generic.Vector Data.Vector.Unboxed.Vector a, Unjson a, Data.Vector.Unboxed.Unbox a) => Unjson (Data.Vector.Unboxed.Vector a)+instance (Data.Vector.Generic.Vector Data.Vector.Unboxed.Vector a, Unjson a, Data.Vector.Unboxed.Unbox a, Typeable a) => Unjson (Data.Vector.Unboxed.Vector a) where unjsonDef = invmap Data.Vector.Unboxed.fromList Data.Vector.Unboxed.toList unjsonDef-instance (Storable a, Unjson a) => Unjson (Data.Vector.Storable.Vector a)+instance (Storable a, Unjson a, Typeable a) => Unjson (Data.Vector.Storable.Vector a) where unjsonDef = invmap Data.Vector.Storable.fromList Data.Vector.Storable.toList unjsonDef-instance (Prim a, Unjson a) => Unjson (Data.Vector.Primitive.Vector a)+instance (Prim a, Unjson a, Typeable a) => Unjson (Data.Vector.Primitive.Vector a) where unjsonDef = invmap Data.Vector.Primitive.fromList Data.Vector.Primitive.toList unjsonDef mapFst :: (a -> c) -> (a,b) -> (c,b) mapFst f (a,b) = (f a, b) -instance Unjson v => Unjson (Map.Map String v)+instance (Typeable v, Unjson v) => Unjson (Map.Map String v) where unjsonDef = invmap (Map.fromList . map (mapFst Text.unpack) . HashMap.toList) (HashMap.fromList . map (mapFst Text.pack) . Map.toList) unjsonDef-instance Unjson v => Unjson (Map.Map Text.Text v)+instance (Typeable v, Unjson v) => Unjson (Map.Map Text.Text v) where unjsonDef = invmap (Map.fromList . HashMap.toList) (HashMap.fromList . Map.toList) unjsonDef-instance Unjson v => Unjson (Map.Map LazyText.Text v)+instance (Typeable v, Unjson v) => Unjson (Map.Map LazyText.Text v) where unjsonDef = invmap (Map.fromList . map (mapFst LazyText.fromStrict) . HashMap.toList) (HashMap.fromList . map (mapFst LazyText.toStrict) . Map.toList) unjsonDef-instance Unjson v => Unjson (HashMap.HashMap String v)+instance (Typeable v, Unjson v) => Unjson (HashMap.HashMap String v) where unjsonDef = invmap (HashMap.fromList . map (mapFst Text.unpack) . HashMap.toList) (HashMap.fromList . map (mapFst Text.pack) . HashMap.toList) unjsonDef-instance Unjson v => Unjson (HashMap.HashMap Text.Text v)+instance (Typeable v, Unjson v) => Unjson (HashMap.HashMap Text.Text v) where unjsonDef = MapUnjsonDef unjsonDef pure id-instance Unjson v => Unjson (HashMap.HashMap LazyText.Text v)+instance (Typeable v, Unjson v) => Unjson (HashMap.HashMap LazyText.Text v) where unjsonDef = invmap (HashMap.fromList . map (mapFst LazyText.fromStrict) . HashMap.toList) (HashMap.fromList . map (mapFst LazyText.toStrict) . HashMap.toList) unjsonDef@@ -590,12 +591,12 @@ -- | Opaque 'UnjsonDef' defines a bidirectional JSON parser. data UnjsonDef a where SimpleUnjsonDef :: Text.Text -> (Aeson.Value -> Result k) -> (k -> Aeson.Value) -> UnjsonDef k- ArrayUnjsonDef :: Maybe (PrimaryKeyExtraction k) -> ArrayMode -> ([k] -> Result v) -> (v -> [k]) -> UnjsonDef k -> UnjsonDef v+ ArrayUnjsonDef :: Typeable k => Maybe (PrimaryKeyExtraction k) -> ArrayMode -> ([k] -> Result v) -> (v -> [k]) -> UnjsonDef k -> UnjsonDef v ObjectUnjsonDef :: Ap (FieldDef k) (Result k) -> UnjsonDef k TupleUnjsonDef :: Ap (TupleFieldDef k) (Result k) -> UnjsonDef k DisjointUnjsonDef :: Text.Text -> [(Text.Text, k -> Bool, Ap (FieldDef k) (Result k))] -> UnjsonDef k UnionUnjsonDef :: [(k -> Bool, Ap (FieldDef k) (Result k))] -> UnjsonDef k- MapUnjsonDef :: UnjsonDef k -> (HashMap.HashMap Text.Text k -> Result v) -> (v -> HashMap.HashMap Text.Text k) -> UnjsonDef v+ MapUnjsonDef :: Typeable k => UnjsonDef k -> (HashMap.HashMap Text.Text k -> Result v) -> (v -> HashMap.HashMap Text.Text k) -> UnjsonDef v instance Invariant UnjsonDef where invmap f g (SimpleUnjsonDef name p s) = SimpleUnjsonDef name (fmap f . p) (s . g)@@ -634,10 +635,10 @@ -- parsing definition. 'FieldDef' has three cases for fields that are -- required, optional (via 'Maybe') or jave default value. data FieldDef s a where- FieldReqDef :: Text.Text -> Text.Text -> (s -> a) -> UnjsonDef a -> FieldDef s a- FieldOptDef :: Text.Text -> Text.Text -> (s -> Maybe a) -> UnjsonDef a -> FieldDef s (Maybe a)- FieldDefDef :: Text.Text -> Text.Text -> a -> (s -> a) -> UnjsonDef a -> FieldDef s a- FieldRODef :: Text.Text -> Text.Text -> (s -> a) -> UnjsonDef a -> FieldDef s ()+ FieldReqDef :: Typeable a => Text.Text -> Text.Text -> (s -> a) -> UnjsonDef a -> FieldDef s a+ FieldOptDef :: Typeable a => Text.Text -> Text.Text -> (s -> Maybe a) -> UnjsonDef a -> FieldDef s (Maybe a)+ FieldDefDef :: Typeable a => Text.Text -> Text.Text -> a -> (s -> a) -> UnjsonDef a -> FieldDef s a+ FieldRODef :: Typeable a => Text.Text -> Text.Text -> (s -> a) -> UnjsonDef a -> FieldDef s () -- | Define a tuple element. 'TupleFieldDef' holds information about -- index, accessor function and a parser definition.@@ -1021,7 +1022,7 @@ -- > -- > data Thing = Thing { thingCredentials :: Credentials, ... } -- > unjsonCredentials :: UnjsonDef Credentials-fieldBy :: Text.Text -> (s -> a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) a+fieldBy :: Typeable a => Text.Text -> (s -> a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) a fieldBy key f docstring valuedef = liftAp (FieldReqDef key docstring f valuedef) -- | Declare a required field with definition from 'Unjson' typeclass.@@ -1036,7 +1037,7 @@ -- > -- > data Thing = Thing { thingCredentials :: Credentials, ... } -- > instance Unjson Credentials where ...-field :: (Unjson a) => Text.Text -> (s -> a) -> Text.Text -> Ap (FieldDef s) a+field :: (Unjson a, Typeable a) => Text.Text -> (s -> a) -> Text.Text -> Ap (FieldDef s) a field key f docstring = fieldBy key f docstring unjsonDef -- | Declare an optional field and definition by valuedef.@@ -1052,7 +1053,7 @@ -- > -- > data Thing = Thing { thingCredentials :: Credentials, ... } -- > unjsonCredentials :: UnjsonDef Credentials-fieldOptBy :: Text.Text -> (s -> Maybe a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) (Maybe a)+fieldOptBy :: Typeable a => Text.Text -> (s -> Maybe a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) (Maybe a) fieldOptBy key f docstring valuedef = liftAp (FieldOptDef key docstring f valuedef) -- | Declare an optional field and definition by 'Unjson' typeclass.@@ -1067,7 +1068,7 @@ -- > -- > data Thing = Thing { thingCredentials :: Credentials, ... } -- > instance Unjson Credentials where ...-fieldOpt :: (Unjson a) => Text.Text -> (s -> Maybe a) -> Text.Text -> Ap (FieldDef s) (Maybe a)+fieldOpt :: (Unjson a, Typeable a) => Text.Text -> (s -> Maybe a) -> Text.Text -> Ap (FieldDef s) (Maybe a) fieldOpt key f docstring = fieldOptBy key f docstring unjsonDef -- | Declare a field with default value and definition by valuedef.@@ -1083,7 +1084,7 @@ -- > -- > data Thing = Thing { thingCredentials :: Credentials, ... } -- > unjsonCredentials :: UnjsonDef Credentials-fieldDefBy :: Text.Text -> a -> (s -> a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) a+fieldDefBy :: Typeable a => Text.Text -> a -> (s -> a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) a fieldDefBy key def f docstring valuedef = liftAp (FieldDefDef key docstring def f valuedef) -- | Declare a field with default value and definition by 'Unjson' typeclass.@@ -1097,7 +1098,7 @@ -- > "Port to listen on, defaults to 80" -- > -- > data Thing = Thing { thingPort :: Int, ... }-fieldDef :: (Unjson a) => Text.Text -> a -> (s -> a) -> Text.Text -> Ap (FieldDef s) a+fieldDef :: (Unjson a, Typeable a) => Text.Text -> a -> (s -> a) -> Text.Text -> Ap (FieldDef s) a fieldDef key def f docstring = fieldDefBy key def f docstring unjsonDef @@ -1116,7 +1117,7 @@ -- > "Additional string" -- > -- > data Thing = Thing { thingPort :: Int, thingString :: String, ... }-fieldReadonly :: (Unjson a) => Text.Text -> (s -> a) -> Text.Text -> Ap (FieldDef s) ()+fieldReadonly :: (Unjson a, Typeable a) => Text.Text -> (s -> a) -> Text.Text -> Ap (FieldDef s) () fieldReadonly key f docstring = fieldReadonlyBy key f docstring unjsonDef -- | Declare a field that is readonly from the point of view of Haskell structures,@@ -1136,7 +1137,7 @@ -- > "Additional string" -- > -- > data Thing = Thing { thingPort :: Port, thingString :: String, ... }-fieldReadonlyBy :: Text.Text -> (s -> a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) ()+fieldReadonlyBy :: Typeable a => Text.Text -> (s -> a) -> Text.Text -> UnjsonDef a -> Ap (FieldDef s) () fieldReadonlyBy key f docstring valuedef = liftAp (FieldRODef key docstring f valuedef) -- | Declare an object as bidirectional mapping from JSON object to Haskell record and back.@@ -1173,7 +1174,7 @@ -- > objectOf $ pure X -- > <*> field "xmap" xMap -- > "Map string to Y value"-mapOf :: UnjsonDef x -> UnjsonDef (LazyHashMap.HashMap Text.Text x)+mapOf :: Typeable x => UnjsonDef x -> UnjsonDef (LazyHashMap.HashMap Text.Text x) mapOf def = MapUnjsonDef def pure id -- | Provide sum type support. Bidirectional case matching in Haskell@@ -1237,7 +1238,7 @@ unionOf alternates = UnionUnjsonDef (map (\(b,c) -> (b,fmap return c)) alternates) --- | Provide sum type support for parametersless constructors.+-- | Provide sum type support for parameterless constructors. -- -- For related functionality see 'disjointUnionOf'. --@@ -1253,6 +1254,24 @@ enumOf key alternates = DisjointUnjsonDef key (map (\(a,b) -> (a,(==)b,fmap return (pure b))) alternates) +-- | Automatic sum type conversion with parameterless constructors.+--+-- Basically an automatic version of 'enumOf'.+--+-- Example:+--+-- > data X = A | B deriving (Eq, Data, Enum, Bounded)+-- >+-- > instance Unjson X where unjsonDef = enumUnjsonDef+--+enumUnjsonDef+ :: forall a. (Eq a, Typeable a, Enum a, Bounded a, Data a)+ => UnjsonDef a+enumUnjsonDef = enumOf typeName [ (Text.pack $ show $ toConstr c, c) | c <- constructors ]+ where+ typeName = Text.pack . show . typeRep $ (Proxy :: Proxy a)+ constructors = enumFromTo minBound maxBound :: [a]+ -- | Declare array of values where each of them is described by -- valuedef. Use 'unjsonAeson' to parse. --@@ -1263,7 +1282,7 @@ -- > -- > unjsonThing :: UnjsonDef Thing -- > unjsonThing = ...-arrayOf :: UnjsonDef a -> UnjsonDef [a]+arrayOf :: Typeable a => UnjsonDef a -> UnjsonDef [a] arrayOf = arrayWithModeOf ArrayModeStrict -- | Declare array of values where each of them is described by@@ -1276,7 +1295,7 @@ -- > -- > unjsonThing :: UnjsonDef Thing -- > unjsonThing = ...-arrayWithModeOf :: ArrayMode -> UnjsonDef a -> UnjsonDef [a]+arrayWithModeOf :: Typeable a => ArrayMode -> UnjsonDef a -> UnjsonDef [a] arrayWithModeOf mode valuedef = ArrayUnjsonDef Nothing mode pure id valuedef -- | Declare array of primitive values lifed from 'Aeson'. Accepts@@ -1308,7 +1327,7 @@ -- > (objectOf $ pure (,) -- > <*> field "key" fst "Key in mapping" -- > <*> field "value" fst "Value in mapping")-arrayWithModeAndPrimaryKeyOf :: (Ord pk)+arrayWithModeAndPrimaryKeyOf :: (Ord pk, Typeable a) => ArrayMode -> (a -> pk) -> UnjsonDef pk@@ -1349,7 +1368,7 @@ -- > (objectOf $ pure (,) -- > <*> field "key" fst "Key in mapping" -- > <*> field "value" fst "Value in mapping")-arrayWithPrimaryKeyOf :: (Ord pk)+arrayWithPrimaryKeyOf :: (Ord pk, Typeable a) => (a -> pk) -> UnjsonDef pk -> UnjsonDef a
test/Test.hs view
@@ -4,6 +4,7 @@ import qualified Data.Text as Text import qualified Data.ByteString.Lazy.Char8 as BSL+import Data.Int import Data.Typeable import Data.Unjson import Control.Applicative@@ -36,6 +37,7 @@ , konfigComment :: Maybe Text.Text , konfigOptions :: [Text.Text] , konfigAlternates :: Maybe (Text.Text,Credentials)+ , konfigInt32 :: Int32 } deriving (Eq,Ord,Show,Typeable) @@ -68,6 +70,9 @@ <*> fieldOpt "alternates" konfigAlternates "Alternate names for this server"+ <*> field "int32"+ konfigInt32+ "A bounded integer fieldd." unjsonCredentials :: UnjsonDef Credentials unjsonCredentials = objectOf $ pure Credentials@@ -95,6 +100,7 @@ [ "username" .= "usr1" , "password" .= "pass1" ]+ , "int32" .= 42 ] let expect = Konfig { konfigHostname = "www.example.com"@@ -103,6 +109,7 @@ , konfigCredentials = Credentials "usr1" "pass1" Nothing , konfigAlternates = Nothing , konfigOptions = []+ , konfigInt32 = 42 } let Result val iss = parse unjsonKonfig json@@ -136,6 +143,7 @@ , "credentials" .= Aeson.object [ "username" .= "usr1" ]+ , "int32" .= 999 ] let json = Aeson.object [ "payload" .= json1@@ -166,6 +174,7 @@ [ "username" .= "usr1" ] , "credentials" .= "www.example.com"+ , "int32" .= 999 ] let Result val iss = parse unjsonKonfig json@@ -220,6 +229,7 @@ , konfigCredentials = Credentials "usr1" "pass1" Nothing , konfigAlternates = Nothing , konfigOptions = []+ , konfigInt32 = 42 } let json = unjsonToJSON unjsonKonfig expect@@ -237,6 +247,7 @@ , konfigCredentials = Credentials "usr1" "pass1" Nothing , konfigAlternates = Nothing , konfigOptions = []+ , konfigInt32 = 42 } let jsonstr = BSL.unpack $ unjsonToByteStringLazy' (Options { nulls = False, indent = 4, pretty = True }) unjsonKonfig konfig@@ -249,7 +260,8 @@ , " \"password\": \"pass1\"" , " }," , " \"comment\": \"nice server\","- , " \"options\": []"+ , " \"options\": [],"+ , " \"int32\": 42" , "}" ] assertEqual "Serialize pretty prints proper indents" expect jsonstr@@ -263,7 +275,8 @@ , " \"password\": \"pass1\"" , " }," , " \"comment\": \"nice server\","- , " \"options\": []"+ , " \"options\": [],"+ , " \"int32\": 42" , "}" ] assertEqual "Serialize pretty prints proper indents" expect5 jsonstr5@@ -277,7 +290,8 @@ , "\"password\":\"pass1\"" , "}," , "\"comment\":\"nice server\","- , "\"options\":[]"+ , "\"options\":[],"+ , "\"int32\":42" , "}" ] assertEqual "Serialize pretty prints proper indents" expect3 jsonstr3@@ -292,6 +306,7 @@ , konfigCredentials = Credentials "usr1" "pass1" Nothing , konfigAlternates = Nothing , konfigOptions = []+ , konfigInt32 = 42 } let jsonstr = BSL.unpack $ unjsonToByteStringLazy' (Options { nulls = True, indent = 4, pretty = True }) unjsonKonfig konfig@@ -306,7 +321,8 @@ , " }," , " \"comment\": \"nice server\"," , " \"options\": [],"- , " \"alternates\": null"+ , " \"alternates\": null,"+ , " \"int32\": 42" , "}" ] assertEqual "Serialize pretty prints proper indents" expect jsonstr@@ -388,16 +404,15 @@ assertBool "Documentation generates" (length docstr > 0) return () + data AB = A | B deriving (Show, Eq, Ord) - unjsonEnumAB :: UnjsonDef AB unjsonEnumAB = enumOf "mode" [ ("A", A) , ("B", B)] - test_enum_field :: Test test_enum_field = "test_enum_field" ~: do do@@ -421,6 +436,37 @@ let Result val iss = parse unjsonEnumAB json assertEqual "No problems" [Anchored (Path [PathElemKey "mode"]) "value 'wrong' is not one of the allowed for enumeration [A,B]"] iss ++data AutoAB = AutoA | AutoB+ deriving (Show, Eq, Ord, Enum, Bounded, Data, Typeable)++unjsonAutoEnumAB :: UnjsonDef AutoAB+unjsonAutoEnumAB = enumUnjsonDef++test_auto_enum_field :: Test+test_auto_enum_field = "test_auto_enum_field" ~: do+ do+ let json = Aeson.object+ [ "AutoAB" .= "AutoA"+ ]+ let Result val iss = parse unjsonAutoEnumAB json+ assertEqual "No problems" [] iss+ assertEqual "Proper value present" AutoA val+ do+ let json = Aeson.object+ [ "AutoAB" .= "AutoB"+ ]+ let Result val iss = parse unjsonAutoEnumAB json+ assertEqual "No problems" [] iss+ assertEqual "Proper value present" AutoB val+ do+ let json = Aeson.object+ [ "AutoAB" .= "wrong"+ ]+ let Result val iss = parse unjsonAutoEnumAB json+ assertEqual "No problems" [Anchored (Path [PathElemKey "AutoAB"]) "value 'wrong' is not one of the allowed for enumeration [AutoA,AutoB]"] iss+ + test_update_from_serialization :: Test test_update_from_serialization = "test_update_from_serialization" ~: do let initial = Konfig@@ -430,6 +476,7 @@ , konfigCredentials = Credentials "usr1" "pass1" Nothing , konfigAlternates = Nothing , konfigOptions = []+ , konfigInt32 = 42 } let expect = Konfig { konfigHostname = "www.example.com"@@ -438,6 +485,7 @@ , konfigCredentials = Credentials "usr2" "pass1" (Just "domain") , konfigAlternates = Nothing , konfigOptions = []+ , konfigInt32 = 256 } let json = Aeson.object@@ -447,6 +495,7 @@ , "credentials" .= Aeson.object [ "domain" .= "domain" , "username" .= "usr2" ]+ , "int32" .= 256 ] let Result val iss = update initial unjsonKonfig json assertEqual "No problems" [] iss@@ -462,6 +511,7 @@ , konfigCredentials = Credentials "usr1" "pass1" (Just "domain") , konfigAlternates = Nothing , konfigOptions = []+ , konfigInt32 = 0 } let expect = Konfig { konfigHostname = "www.example.com"@@ -470,6 +520,7 @@ , konfigCredentials = Credentials "usr1" "pass1" (Nothing) , konfigAlternates = Just ("abc", Credentials "usrx" "passx" Nothing) , konfigOptions = []+ , konfigInt32 = 256 } let json = Aeson.object@@ -486,7 +537,7 @@ ] ] let Result _ iss = update initial unjsonKonfig json- assertEqual "Cannot reset mangatory field without default"+ assertEqual "Cannot reset mandatory field without default" [Anchored (Path [PathElemKey "hostname"]) "expected Text, encountered Null"] iss return () @@ -600,12 +651,12 @@ ] jsonEmbedded = Aeson.object [ "a_map" .= json ]- let unjsonMapByInstance :: (Unjson a) => UnjsonDef a+ let unjsonMapByInstance :: (Unjson a, Typeable a) => UnjsonDef a unjsonMapByInstance = objectOf $ pure id <*> field "a_map" id "The only map"- let unjsonMapByExplicit :: (Unjson a) => UnjsonDef (HashMap.HashMap Text.Text a)+ let unjsonMapByExplicit :: (Unjson a, Typeable a) => UnjsonDef (HashMap.HashMap Text.Text a) unjsonMapByExplicit = objectOf $ pure id <*> fieldBy "a_map" id
unjson.cabal view
@@ -1,5 +1,5 @@ name: unjson-version: 0.14.1.3+version: 0.15.0.0 synopsis: Bidirectional JSON parsing and generation. description: Bidirectional JSON parsing and generation with automatic documentation support.@@ -13,10 +13,10 @@ copyright: Scrive AB category: Data build-type: Simple-extra-source-files: README.md+extra-source-files: README.md, CHANGELOG.md cabal-version: >=1.18-tested-with: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4,- GHC == 7.10.3, GHC == 8.0.1+tested-with: GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2,+ GHC == 8.2.2 source-repository head type: git@@ -25,13 +25,13 @@ source-repository this type: git location: https://github.com/scrive/unjson.git- tag: 0.14.1.3+ tag: 0.15.0.0 library exposed-modules: Data.Unjson -- other-modules: -- other-extensions:- build-depends: base >= 4.5 && < 5, text, aeson >= 1.0 && < 1.3,+ build-depends: base >= 4.7 && < 5, text, aeson >= 1.0 && < 1.3, free, scientific, attoparsec, unordered-containers, vector, pretty, bytestring >= 0.10, containers, hashable,