git.iko.soy/typeable/schematic
on master help sign in
serialization
commitfceaca433f13f1fc1db620fae12579ca28ee336f authorDenis Redozubov <denis.redozubov@gmail.com> dateSat 20 May 2017, 22:05 (9 years ago) parentb6a94f1
5 files changed+136−63
schematic.cabal+4 src/Data/Schematic/Schema.hs+73−24 src/Data/Schematic/Utils.hs+37 stack.yaml+1−16 test/SchemaSpec.hs+21−23
schematic.cabal+4
@@ -18,6 +18,7 @@ cabal-version: >=1.10
1818library
1919 exposed-modules: Data.Aeson.Validation
2020 , Data.Schematic
21+ , Data.Schematic.Instances
2122 , Data.Schematic.Schema
2223 , Data.Schematic.Utils
2324 -- ghc-options:
@@ -60,6 +61,7 @@ library
6061 , scientific
6162 , singletons
6263 , smallcheck
64+ , smallcheck-series
6365 , text
6466 , unordered-containers
6567 , validationt >= 0.1.0.1
@@ -108,6 +110,8 @@ test-suite spec
108110 , hspec-smallcheck
109111 , regex-compat
110112 , schematic
113+ , smallcheck
114+ , smallcheck-series
111115 , singletons
112116 , text
113117 , unordered-containers
src/Data/Schematic/Schema.hs+73−24
@@ -16,6 +16,7 @@ import Data.Functor.Classes
1616import Data.HashMap.Strict as H
1717import Data.Kind
1818import Data.Maybe
19+import Data.Schematic.Instances
1920import Data.Schematic.Utils
2021import Data.Scientific
2122import Data.Singletons.Decide
@@ -26,7 +27,9 @@ import Data.Text as T
2627import Data.Vector as V
2728import Data.Vinyl hiding (Dict)
2829import Data.Vinyl.Functor
30+import Data.Vinyl.TypeLevel hiding (Nat)
2931import GHC.Generics (Generic)
32+import Prelude as P
3033import Test.SmallCheck.Series
3134import Text.Show.Deriving (deriveShow1)
3235
@@ -98,10 +101,10 @@ data Schema
98101 deriving (Generic)
99102
100103data instance Sing (schema :: Schema) where
101− SSchemaText :: Sing tcs -> Sing (SchemaText tcs)
102− SSchemaNumber :: Sing ncs -> Sing (SchemaNumber ncs)
103− SSchemaArray :: Sing acs -> Sing schema -> Sing (SchemaArray acs schema)
104− SSchemaObject :: Sing fields -> Sing (SchemaObject fields)
104+ SSchemaText :: Known (Sing tcs) => Sing tcs -> Sing (SchemaText tcs)
105+ SSchemaNumber :: Known (Sing ncs) => Sing ncs -> Sing (SchemaNumber ncs)
106+ SSchemaArray :: (Known (Sing acs), Known (Sing schema)) => Sing acs -> Sing schema -> Sing (SchemaArray acs schema)
107+ SSchemaObject :: Known (Sing fields) => Sing fields -> Sing (SchemaObject fields)
105108 SSchemaNull :: Sing SchemaNull
106109
107110instance Known (Sing sl) => Known (Sing (SchemaText sl)) where
@@ -124,6 +127,12 @@ instance Eq (Sing (SchemaObject cs)) where a == b = True
124127data FieldRepr :: (Symbol, Schema) -> Type where
125128 FieldRepr :: KnownSymbol name => JsonRepr schema -> FieldRepr '(name, schema)
126129
130+type family MapSnd (cs :: [(Symbol, Schema)]) :: [Schema] where
131+ MapSnd '[] = '[]
132+ MapSnd ( '(fn, schema) ': as) = schema ': MapSnd as
133+
134+deriving instance Show (JsonRepr schema) => Show (FieldRepr '(name, schema))
135+
127136instance Eq (JsonRepr schema) => Eq (FieldRepr '(name, schema)) where
128137 FieldRepr a == FieldRepr b = a == b
129138
@@ -140,6 +149,39 @@ data JsonRepr :: Schema -> Type where
140149 ReprArray :: V.Vector (JsonRepr s) -> JsonRepr (SchemaArray cs s)
141150 ReprObject :: Rec FieldRepr fs -> JsonRepr (SchemaObject fs)
142151
152+instance Show (JsonRepr (SchemaText cs)) where
153+ show (ReprText t) = "ReprText " P.++ show t
154+
155+instance Show (JsonRepr (SchemaNumber cs)) where
156+ show (ReprNumber n) = "ReprNumber " P.++ show n
157+
158+instance Show (JsonRepr SchemaNull) where show _ = "ReprNull"
159+
160+instance Show (JsonRepr s) => Show (JsonRepr (SchemaArray acs s)) where
161+ show (ReprArray v) = "ReprArray " P.++ show v
162+
163+instance RecAll FieldRepr fs Show => Show (JsonRepr (SchemaObject fs)) where
164+ show (ReprObject fs) = "ReprObject " P.++ show fs
165+
166+instance (Monad m, Serial m Text)
167+ => Serial m (JsonRepr (SchemaText cs)) where
168+ series = cons1 ReprText
169+
170+instance (Monad m, Serial m Scientific)
171+ => Serial m (JsonRepr (SchemaNumber cs)) where
172+ series = cons1 ReprNumber
173+
174+instance Monad m => Serial m (JsonRepr SchemaNull) where
175+ series = cons0 ReprNull
176+
177+instance (Serial m (V.Vector (JsonRepr s)))
178+ => Serial m (JsonRepr (SchemaArray cs s)) where
179+ series = cons1 ReprArray
180+
181+instance (Monad m, Serial m (Rec FieldRepr fs))
182+ => Serial m (JsonRepr (SchemaObject fs)) where
183+ series = cons1 ReprObject
184+
143185instance Eq (Rec FieldRepr fs) => Eq (JsonRepr (SchemaObject fs)) where
144186 ReprObject a == ReprObject b = a == b
145187
@@ -159,27 +201,34 @@ instance Known (Sing schema) => J.FromJSON (JsonRepr schema) where
159201 parseJSON value = case known :: Sing schema of
160202 SSchemaText _ -> withText "String" (pure . ReprText) value
161203 SSchemaNumber _ -> withScientific "Number" (pure . ReprNumber) value
162− SSchemaNull -> pure ReprNull
163− SSchemaArray c s -> do
204+ SSchemaNull -> case value of
205+ J.Null -> pure ReprNull
206+ _ -> typeMismatch "Null" value
207+ SSchemaArray c s -> withArray "Array" (fmap ReprArray . traverse parseJSON) value
208+ SSchemaObject fs -> do
164209 let
165− f :: Sing cs -> Sing s -> V.Vector J.Value -> Parser (JsonRepr (SchemaArray cs s))
166− f _ _ = fmap ReprArray . traverse parseJSON
167− withArray "Array" (f c s) value
168− SSchemaObject fs -> ReprObject <$> withObject "Object" (demoteFields fs) value
169−
170−demoteFields
171− :: SList s
172− -> H.HashMap Text J.Value
173− -> Parser (Rec FieldRepr s)
174−demoteFields SNil h
175− | H.null h = pure RNil
176− | otherwise = mzero
177−demoteFields (SCons (STuple2 (n :: Sing fn) s) tl) h = withKnownSymbol n $ do
178− let fieldName = T.pack $ symbolVal (Proxy @fn)
179− fieldRepr <- case H.lookup fieldName h of
180− Just v -> FieldRepr <$> (withSingI s $ parseJSON v)
181− Nothing -> mzero
182− (fieldRepr :&) <$> demoteFields tl h
210+ demoteFields :: SList s -> H.HashMap Text J.Value -> Parser (Rec FieldRepr s)
211+ demoteFields SNil h = pure RNil
212+ demoteFields (SCons (STuple2 (n :: Sing fn) s) tl) h = withKnownSymbol n $ do
213+ let fieldName = T.pack $ symbolVal (Proxy @fn)
214+ fieldRepr <- case s of
215+ SSchemaText _ -> case H.lookup fieldName h of
216+ Just v -> FieldRepr <$> parseJSON v
217+ Nothing -> fail "schematext"
218+ SSchemaNumber _ -> case H.lookup fieldName h of
219+ Just v -> FieldRepr <$> parseJSON v
220+ Nothing -> fail "schemanumber"
221+ SSchemaNull -> case H.lookup fieldName h of
222+ Just v -> FieldRepr <$> parseJSON v
223+ Nothing -> fail "schemanull"
224+ SSchemaArray _ _ -> case H.lookup fieldName h of
225+ Just v -> FieldRepr <$> parseJSON v
226+ Nothing -> fail "schemaarray"
227+ SSchemaObject _ -> case H.lookup fieldName h of
228+ Just v -> FieldRepr <$> parseJSON v
229+ Nothing -> fail "schemaobject"
230+ (fieldRepr :&) <$> demoteFields tl h
231+ ReprObject <$> withObject "Object" (demoteFields fs) value
183232
184233instance J.ToJSON (JsonRepr a) where
185234 toJSON ReprNull = J.Null
src/Data/Schematic/Utils.hsnew file+37
@@ -0,0 +1,37 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+
3+module Data.Schematic.Utils where
4+
5+import Data.Kind
6+import Data.Proxy
7+import Data.Singletons
8+import Data.Singletons.Prelude
9+import Data.Singletons.TypeLits
10+import Data.Vinyl hiding (Dict)
11+
12+
13+class Known a where
14+ known :: a
15+
16+instance KnownNat n => Known (Sing n) where
17+ known = SNat
18+
19+instance KnownSymbol s => Known (Sing s) where
20+ known = SSym
21+
22+instance (Known (Sing a), Known (Sing b)) => Known (Sing '(a,b)) where
23+ known = STuple2 known known
24+
25+instance Known (Sing '[]) where known = SNil
26+
27+instance (Known (Sing a), Known (Sing as)) => Known (Sing (a ': as)) where
28+ known = SCons known known
29+
30+instance Known (Rec Sing '[]) where
31+ known = RNil
32+
33+instance (Known (Sing a), Known (Rec Sing tl)) => Known (Rec Sing (a ': tl)) where
34+ known = known :& known
35+
36+data Dict c where
37+ Dict :: c => Dict c
stack.yaml+1−16
@@ -1,19 +1,3 @@
1−# This file was automatically generated by 'stack init'
2−#
3−# Some commonly used options have been documented as comments in this file.
4−# For advanced use and comprehensive documentation of the format, please see:
5−# http://docs.haskellstack.org/en/stable/yaml_configuration/
6−
7−# Resolver to choose a 'specific' stackage snapshot or a compiler version.
8−# A snapshot resolver dictates the compiler version and the set of packages
9−# to be used for project dependencies. For example:
10−#
11−# resolver: lts-3.5
12−# resolver: nightly-2015-09-21
13−# resolver: ghcjs-0.1.0_ghc-7.10.2
14−# resolver:
15−# name: custom-snapshot
16−# location: "./custom-snapshot.yaml"
171resolver: lts-8.12
182#resolver: ghc-8.0.2
193
@@ -43,6 +27,7 @@ packages:
4327
4428extra-deps:
4529- validationt-0.2.0.0
30+- smallcheck-series-0.6
4631
4732# Override default flag values for local packages and extra-deps
4833flags: {}
test/SchemaSpec.hs+21−23
@@ -1,5 +1,6 @@
11module SchemaSpec (spec, main) where
22
3+import Control.Monad
34import Data.Aeson
45import Data.Proxy
56import Data.Schematic
@@ -7,45 +8,42 @@ import Data.Singletons
78import Data.Vinyl
89import Test.Hspec
910import Test.Hspec.SmallCheck
11+import Test.SmallCheck
12+import Test.SmallCheck.Series.Instances
1013
1114
1215type SchemaExample
1316 = SchemaObject
14− '[ '("foo", SchemaArray '[AEq 3] (SchemaNumber '[NGt 10]))
17+ '[ '("foo", SchemaArray '[AEq 1] (SchemaNumber '[NGt 10]))
1518 , '("bar", SchemaText '[TRegex "\\w+"])]
1619
17−schemaExample
18− :: ( DemoteRep Schema ~ Sing SchemaExample
19− , DemoteRep Schema ~ s
20− , SingKind Schema
21− , SingI SchemaExample
22− ) => s
23−schemaExample = fromSing (sing :: Sing SchemaExample)
24−
2520exampleTest :: JsonRepr (SchemaText '[TEq 3])
26−exampleTest = ReprText "lilo"
21+exampleTest = ReprText "lil"
2722
28−exampleNumber :: JsonRepr (SchemaNumber '[NEq 3])
29−exampleNumber = ReprNumber 3
23+exampleNumber :: JsonRepr (SchemaNumber '[NGt 10])
24+exampleNumber = ReprNumber 12
3025
31−exampleArray :: JsonRepr (SchemaArray '[AEq 1] (SchemaNumber '[NEq 3]))
26+exampleArray :: JsonRepr (SchemaArray '[AEq 1] (SchemaNumber '[NGt 10]))
3227exampleArray = ReprArray [exampleNumber]
3328
34−exampleObject
35− :: JsonRepr (SchemaObject '[ '("foo", SchemaArray '[AEq 1] (SchemaNumber '[NEq 3]))] )
36−exampleObject = ReprObject $ FieldRepr exampleArray :& RNil
29+exampleObject :: JsonRepr SchemaExample
30+exampleObject = ReprObject $ FieldRepr exampleArray :& FieldRepr (ReprText "barval") :& RNil
3731
38−example :: JsonRepr SchemaExample
39−example = ReprObject $
40− FieldRepr (ReprArray [ReprNumber 3])
41− :& FieldRepr (ReprText "test")
32+jsonExample :: JsonRepr SchemaExample
33+jsonExample = ReprObject $
34+ FieldRepr (ReprArray [ReprNumber 12])
35+ :& FieldRepr (ReprText "tes")
4236 :& RNil
4337
38+-- schemaExample :: Sing SchemaExample
39+-- schemaExample = known
40+
4441spec :: Spec
4542spec = do
46− it "decode/encode JsonRepr properly" $ do
47− withSingI schemaExample $ property
48− $ \(a :: JsonRepr SchemaExample) -> decode (encode a) == Just a
43+ -- it "show/read JsonRepr properly" $
44+ -- read (show example) == example
45+ it "decode/encode JsonRepr properly" $
46+ decode (encode jsonExample) == Just jsonExample
4947
5048main :: IO ()
5149main = hspec spec