git.iko.soy/typeable/schematic
on master help sign in
Constructor DSL
commit5be2bf63a26d0c202c2d9887ff5d1a6f3bde4411 authorDenis Redozubov <denis.redozubov@gmail.com> dateWed 11 Oct 2017, 11:55 (9 years ago) parentec1996d
5 files changed+143−37
src/Data/Schematic.hs+87−14 src/Data/Schematic/Lens.hs+1−2 src/Data/Schematic/Schema.hs+9−2 src/Data/Schematic/Validation.hs+38−15 test/SchemaSpec.hs+8−4
src/Data/Schematic.hs+87−14
@@ -19,22 +19,33 @@ module Data.Schematic
1919 , isDecodingError
2020 , isValidationError
2121 , ParseResult(..)
22+ , withRepr
23+ , field
24+ , (<:&>)
2225 ) where
2326
24−import Control.Monad.Validation
25−import Data.Aeson as J
26−import Data.Aeson.Types as J
27−import Data.ByteString.Lazy as BL
28−import Data.Functor.Identity
29−import Data.Schematic.JsonSchema
30−import Data.Schematic.Helpers
31−import Data.Schematic.Lens
32−import Data.Schematic.Migration
33−import Data.Schematic.Schema
34−import Data.Schematic.Utils
35−import Data.Schematic.Validation
36−import Data.Singletons.Prelude
37−import Data.Text as T
27+import Control.Applicative
28+import Control.Monad.Validation
29+import Data.Aeson as J
30+import Data.Aeson.Types as J
31+import Data.ByteString.Lazy as BL
32+import Data.Functor.Identity
33+import Data.Kind
34+import Data.Schematic.Helpers
35+import Data.Schematic.JsonSchema
36+import Data.Schematic.Lens
37+import Data.Schematic.Migration
38+import Data.Schematic.Schema
39+import Data.Schematic.Utils
40+import Data.Schematic.Validation
41+import Data.Scientific
42+import Data.Singletons.Prelude
43+import Data.Singletons.TypeLits
44+import Data.Tagged
45+import Data.Text as T
46+import Data.Union
47+import qualified Data.Vector as V
48+import Data.Vinyl
3849
3950
4051parseAndValidateJson
@@ -125,3 +136,65 @@ decodeAndValidateVersionedWithPureMList
125136 -> ParseResult (JsonRepr (Head (MapSnd (AllVersions versioned))))
126137decodeAndValidateVersionedWithPureMList a b c =
127138 runIdentity $ decodeAndValidateVersionedWithMList a b c
139+
140+-- convenience helpers
141+
142+type Constructor a
143+ = forall b. FSubset (FieldsOf a) b (FImage (FieldsOf a) b)
144+ => Tagged (FieldsOf a) (Rec FieldRepr b)
145+ -> JsonRepr ('SchemaObject (FieldsOf a))
146+
147+withRepr :: Constructor a
148+withRepr = ReprObject . unTagged . fmap fcast
149+
150+(<:&>)
151+ :: Applicative m
152+ => m (FieldRepr fns)
153+ -> m (Rec FieldRepr b)
154+ -> m (Rec FieldRepr (fns ': b))
155+(<:&>) = liftA2 (:&)
156+
157+infixr 7 <:&>
158+
159+class Representable s where
160+ type Repr s :: Type
161+ construct :: Sing fn -> Proxy s -> Repr s -> FieldRepr '(fn, s)
162+
163+instance SingI so => Representable ('SchemaObject so) where
164+ type Repr ('SchemaObject so) = Rec FieldRepr so
165+ construct sfn _ o = withKnownSymbol sfn $ FieldRepr $ ReprObject o
166+
167+instance (SingI cs, SingI sa) => Representable ('SchemaArray cs sa) where
168+ type Repr ('SchemaArray cs sa) = V.Vector (JsonRepr sa)
169+ construct sfn _ a = withKnownSymbol sfn $ FieldRepr $ ReprArray a
170+
171+instance SingI cs => Representable ('SchemaText cs) where
172+ type Repr ('SchemaText cs) = Text
173+ construct sfn _ t = withKnownSymbol sfn $ FieldRepr $ ReprText t
174+
175+instance SingI cs => Representable ('SchemaNumber cs) where
176+ type Repr ('SchemaNumber cs) = Scientific
177+ construct sfn _ n = withKnownSymbol sfn $ FieldRepr $ ReprNumber n
178+
179+instance Representable 'SchemaBoolean where
180+ type Repr 'SchemaBoolean = Bool
181+ construct sfn _ b = withKnownSymbol sfn $ FieldRepr $ ReprBoolean b
182+
183+instance SingI so => Representable ('SchemaOptional so) where
184+ type Repr ('SchemaOptional so) = Maybe (JsonRepr so)
185+ construct sfn _ o = withKnownSymbol sfn $ FieldRepr $ ReprOptional o
186+
187+instance SingI (h ': tl) => Representable ('SchemaUnion (h ': tl)) where
188+ type Repr ('SchemaUnion (h ': tl)) = Union JsonRepr (h ': tl)
189+ construct sfn _ u = withKnownSymbol sfn $ FieldRepr $ ReprUnion u
190+
191+type family FieldsOf (s :: Schema) :: [(Symbol, Schema)] where
192+ FieldsOf ('SchemaObject fs) = fs
193+
194+type FieldConstructor fn =
195+ forall fs. (Representable (ByField fn fs (FIndex fn fs)))
196+ => Repr (ByField fn fs (FIndex fn fs))
197+ -> Tagged fs (FieldRepr '(fn, (ByField fn fs (FIndex fn fs))))
198+
199+field :: forall fn. KnownSymbol fn => FieldConstructor fn
200+field = Tagged . construct (sing :: Sing fn) Proxy
src/Data/Schematic/Lens.hs+1−2
@@ -98,8 +98,7 @@ type Iso s t a b = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f
9898type Iso' s a = Iso s s a a
9999
100100iso
101− :: (s -> a)
102− -> (b -> t)
101+ :: (s -> a) -> (b -> t)
103102 -> Iso s t a b
104103iso sa bt = dimap sa (fmap bt)
105104{-# INLINE iso #-}
src/Data/Schematic/Schema.hs+9−2
@@ -319,6 +319,10 @@ instance
319319 => Serial m (FieldRepr '(name, schema)) where
320320 series = FieldRepr <$> series
321321
322+type family USubsets (u :: [k]) :: Constraint where
323+ USubsets '[] = ()
324+ USubsets (h ': tl) = (USubset tl (h ': tl) (V.RImage tl (h ': tl)), USubsets tl)
325+
322326data JsonRepr :: Schema -> Type where
323327 ReprText :: Text -> JsonRepr ('SchemaText cs)
324328 ReprNumber :: Scientific -> JsonRepr ('SchemaNumber cs)
@@ -327,9 +331,12 @@ data JsonRepr :: Schema -> Type where
327331 ReprArray :: V.Vector (JsonRepr s) -> JsonRepr ('SchemaArray cs s)
328332 ReprObject :: Rec FieldRepr fs -> JsonRepr ('SchemaObject fs)
329333 ReprOptional :: Maybe (JsonRepr s) -> JsonRepr ('SchemaOptional s)
330− ReprUnion :: Union JsonRepr (h ': tl) -> JsonRepr ('SchemaUnion (h ': tl))
334+ ReprUnion
335+ -- :: USubsets (h ': tl)
336+ :: Union JsonRepr (h ': tl)
337+ -> JsonRepr ('SchemaUnion (h ': tl))
331338
332−-- | Move to the different package
339+-- | Move to the union package
333340type family UnionAll (f :: u -> *) (rs :: [u]) (c :: * -> Constraint) :: Constraint where
334341 UnionAll f '[] c = ()
335342 UnionAll f (r ': rs) c = (c (f r), UnionAll f rs c)
src/Data/Schematic/Validation.hs+38−15
@@ -16,6 +16,7 @@ import Data.Traversable
1616import Data.Union
1717import Data.Vector as V
1818import Data.Vinyl
19+import Data.Vinyl.TypeLevel
1920import Prelude as P
2021import Text.Regex.TDFA
2122
@@ -206,21 +207,43 @@ validateJsonRepr sschema dpath jr = case jr of
206207 let newPath = dpath <> [DKey (knownFieldName f)]
207208 validateJsonRepr (knownFieldSchema f) newPath d
208209 go ftl
209− ReprUnion u -> pure () -- case sschema of
210− -- SSchemaUnion ss -> case ss of
211− -- SNil -> do
212− -- let JSONPath path = demotedPathToText dpath
213− -- void $ vWarning $ mmSingleton path
214− -- $ pure "union handling error, please report this as bug"
215− -- SCons s stl -> case umatch' s u of
216− -- Nothing -> case urestrict u of
217− -- Nothing -> fail "impossible to urestrict union, please report this as a bug"
218− -- Just x -> do
219− -- let
220− -- toUnion :: Sing ss -> Union JsonRepr ss -> JsonRepr ('SchemaUnion ss)
221− -- toUnion _ = ReprUnion
222− -- validateJsonRepr (SSchemaUnion stl) dpath $ toUnion stl x
223− -- Just x -> validateJsonRepr s dpath x
210+ ReprUnion u -> pure ()
211+ -- case sschema of
212+ -- SSchemaUnion ss -> case ss of
213+ -- SCons s stl -> case umatch' s u of
214+ -- Nothing -> case urestrict u of
215+ -- Nothing ->
216+ -- fail "impossible to produce subUnion, please report this as a bug"
217+ -- Just x -> do
218+ -- let
219+ -- JSONPath path = demotedPathToText dpath
220+ -- case stl of
221+ -- SNil -> void $ vWarning $ mmSingleton path
222+ -- $ pure "union handling error, please report this as bug"
223+ -- SCons s' stl' ->
224+ -- validateJsonRepr (SSchemaUnion (SCons s' stl')) dpath
225+ -- $ toUnion (SCons s' stl') x
226+ -- Just x -> validateJsonRepr s dpath x
227+
228+-- subUnion
229+-- :: Sing (s ': stl)
230+-- -> ( USubset stl (s ': stl) (RImage stl (s ': stl))
231+-- => Union f (s ': stl)
232+-- -> Maybe (Union f stl) )
233+-- subUnion (SCons s stl) = urestrict
234+
235+-- withUSubset
236+-- :: Sing (s ': stl)
237+-- -> (USubset stl (s ': stl) (RImage stl (s ': stl)) => Maybe (Union f stl))
238+-- -> Maybe (Union f stl)
239+-- withUSubset (SCons s stl) r = r
240+
241+toUnion
242+ :: USubset s' (s ': ss) (RImage s' (s ': ss))
243+ => Sing (s ': ss)
244+ -> Union JsonRepr s'
245+ -> JsonRepr ('SchemaUnion (s ': ss))
246+toUnion _ = ReprUnion . urelax
224247
225248umatch' :: UElem a as i => Sing a -> Union f as -> Maybe (f a)
226249umatch' _ u = umatch u
test/SchemaSpec.hs+8−4
@@ -10,11 +10,15 @@ import Data.Schematic
1010import Data.Vinyl
1111import Test.Hspec
1212
13+type SchemaExample = 'SchemaObject
14+ '[ '("foo", 'SchemaArray '[ 'AEq 1] ('SchemaNumber '[ 'NGt 10]))
15+ , '("bar", 'SchemaOptional ('SchemaText '[ 'TEnum '["foo", "bar"]]))]
1316
14−type SchemaExample
15− = 'SchemaObject
16− '[ '("foo", 'SchemaArray '[ 'AEq 1] ('SchemaNumber '[ 'NGt 10]))
17− , '("bar", 'SchemaOptional ('SchemaText '[ 'TEnum '["foo", "bar"]]))]
17+example :: JsonRepr SchemaExample
18+example = withRepr @SchemaExample $
19+ field @"bar" (Just $ ReprText "bar")
20+ <:&> field @"foo" [ReprNumber 12]
21+ <:&> pure RNil
1822
1923type TestMigration =
2024 'Migration "test_revision"