@@ -5,7 +5,7 @@
6module Data.Schematic.Schema where
6module Data.Schematic.Schema where
8−import Control.Applicative ()
8+import Control.Applicative ((<|>))
9import Control.Monad
9import Control.Monad
10import Data.Aeson as J
10import Data.Aeson as J
11import Data.Aeson.Types as J
11import Data.Aeson.Types as J
@@ -14,10 +14,11 @@ import Data.Kind
14import Data.Maybe
14import Data.Maybe
15import Data.Schematic.Instances ()
15import Data.Schematic.Instances ()
16import Data.Scientific
16import Data.Scientific
17−import Data.Singletons.Prelude.List hiding (All)
17+import Data.Singletons.Prelude.List hiding (All, Union)
18import Data.Singletons.TH
18import Data.Singletons.TH
19import Data.Singletons.TypeLits
19import Data.Singletons.TypeLits
20import Data.Text as T
20import Data.Text as T
21import Data.Vector as V
22import Data.Vector as V
22import Data.Vinyl hiding (Dict)
23import Data.Vinyl hiding (Dict)
23import qualified Data.Vinyl.TypeLevel as V
24import qualified Data.Vinyl.TypeLevel as V
@@ -205,6 +206,7 @@ data Schema
205 | SchemaArray [ArrayConstraint] Schema
206 | SchemaArray [ArrayConstraint] Schema
206 | SchemaNull
207 | SchemaNull
207 | SchemaOptional Schema
208 | SchemaOptional Schema
209+ | SchemaUnion [Schema]
208 deriving (Generic)
210 deriving (Generic)
210data DemotedSchema
212data DemotedSchema
@@ -215,6 +217,7 @@ data DemotedSchema
215 | DSchemaArray [DemotedArrayConstraint] DemotedSchema
217 | DSchemaArray [DemotedArrayConstraint] DemotedSchema
216 | DSchemaNull
218 | DSchemaNull
217 | DSchemaOptional DemotedSchema
219 | DSchemaOptional DemotedSchema
220+ | DSchemaUnion [DemotedSchema]
218 deriving (Generic)
221 deriving (Generic)
220data instance Sing (schema :: Schema) where
223data instance Sing (schema :: Schema) where
@@ -225,6 +228,7 @@ data instance Sing (schema :: Schema) where
225 SSchemaObject :: Sing fields -> Sing ('SchemaObject fields)
228 SSchemaObject :: Sing fields -> Sing ('SchemaObject fields)
226 SSchemaOptional :: Sing s -> Sing ('SchemaOptional s)
229 SSchemaOptional :: Sing s -> Sing ('SchemaOptional s)
227 SSchemaNull :: Sing 'SchemaNull
230 SSchemaNull :: Sing 'SchemaNull
231+ SSchemaUnion :: Sing ss -> Sing ('SchemaUnion ss)
229instance SingI sl => SingI ('SchemaText sl) where
233instance SingI sl => SingI ('SchemaText sl) where
230 sing = SSchemaText sing
234 sing = SSchemaText sing
@@ -240,6 +244,8 @@ instance SingI stl => SingI ('SchemaObject stl) where
240 sing = SSchemaObject sing
244 sing = SSchemaObject sing
241instance SingI s => SingI ('SchemaOptional s) where
245instance SingI s => SingI ('SchemaOptional s) where
242 sing = SSchemaOptional sing
246 sing = SSchemaOptional sing
247+instance SingI s => SingI ('SchemaUnion s) where
248+ sing = SSchemaUnion sing
244instance Eq (Sing ('SchemaText cs)) where _ == _ = True
250instance Eq (Sing ('SchemaText cs)) where _ == _ = True
245instance Eq (Sing ('SchemaNumber cs)) where _ == _ = True
251instance Eq (Sing ('SchemaNumber cs)) where _ == _ = True
@@ -248,6 +254,7 @@ instance Eq (Sing 'SchemaBoolean) where _ == _ = True
248instance Eq (Sing ('SchemaArray as s)) where _ == _ = True
254instance Eq (Sing ('SchemaArray as s)) where _ == _ = True
249instance Eq (Sing ('SchemaObject cs)) where _ == _ = True
255instance Eq (Sing ('SchemaObject cs)) where _ == _ = True
250instance Eq (Sing ('SchemaOptional s)) where _ == _ = True
256instance Eq (Sing ('SchemaOptional s)) where _ == _ = True
257+instance Eq (Sing ('SchemaUnion s)) where _ == _ = True
252instance SingKind Schema where
259instance SingKind Schema where
253 type Demote Schema = DemotedSchema
260 type Demote Schema = DemotedSchema
@@ -258,12 +265,8 @@ instance SingKind Schema where
258 SSchemaArray cs s -> DSchemaArray (fromSing cs) (fromSing s)
265 SSchemaArray cs s -> DSchemaArray (fromSing cs) (fromSing s)
259 SSchemaOptional s -> DSchemaOptional $ fromSing s
266 SSchemaOptional s -> DSchemaOptional $ fromSing s
260 SSchemaNull -> DSchemaNull
267 SSchemaNull -> DSchemaNull
261− SSchemaObject cs -> let
268+ SSchemaObject cs -> DSchemaObject $ fromSing cs
262− dem :: Sing (s :: [(Symbol, Schema)]) -> [(Text, DemotedSchema)]
269+ SSchemaUnion ss -> DSchemaUnion $ fromSing ss
264− dem (SCons (STuple2 ss ssch) fs) = withKnownSymbol ss
265− $ (T.pack (symbolVal ss), fromSing ssch) : dem fs
266− in DSchemaObject $ dem cs
267 toSing = \case
270 toSing = \case
268 DSchemaText cs -> case toSing cs of
271 DSchemaText cs -> case toSing cs of
269 SomeSing scs -> SomeSing $ SSchemaText scs
272 SomeSing scs -> SomeSing $ SSchemaText scs
@@ -277,6 +280,8 @@ instance SingKind Schema where
277 DSchemaNull -> SomeSing SSchemaNull
280 DSchemaNull -> SomeSing SSchemaNull
278 DSchemaObject cs -> case toSing cs of
281 DSchemaObject cs -> case toSing cs of
279 SomeSing scs -> SomeSing $ SSchemaObject scs
282 SomeSing scs -> SomeSing $ SSchemaObject scs
283+ DSchemaUnion ss -> case toSing ss of
284+ SomeSing sss -> SomeSing $ SSchemaUnion sss
281data FieldRepr :: (Symbol, Schema) -> Type where
286data FieldRepr :: (Symbol, Schema) -> Type where
282 FieldRepr
287 FieldRepr
@@ -322,6 +327,16 @@ data JsonRepr :: Schema -> Type where
322 ReprArray :: V.Vector (JsonRepr s) -> JsonRepr ('SchemaArray cs s)
327 ReprArray :: V.Vector (JsonRepr s) -> JsonRepr ('SchemaArray cs s)
323 ReprObject :: Rec FieldRepr fs -> JsonRepr ('SchemaObject fs)
328 ReprObject :: Rec FieldRepr fs -> JsonRepr ('SchemaObject fs)
324 ReprOptional :: Maybe (JsonRepr s) -> JsonRepr ('SchemaOptional s)
329 ReprOptional :: Maybe (JsonRepr s) -> JsonRepr ('SchemaOptional s)
330+ ReprUnion :: Union JsonRepr s -> JsonRepr ('SchemaUnion ss)
332+-- | Move to the different package
333+type family UnionAll (f :: u -> *) (rs :: [u]) (c :: * -> Constraint) :: Constraint where
334+ UnionAll f '[] c = ()
335+ UnionAll f (r ': rs) c = (c (f r), UnionAll f rs c)
337+-- instance (UnionAll f as Show) => Show (Union f as) where
338+-- show (This fa) = "This " P.++ show fa
339+-- show (That u) = "That " P.++ show u
326instance Show (JsonRepr ('SchemaText cs)) where
341instance Show (JsonRepr ('SchemaText cs)) where
327 show (ReprText t) = "ReprText " P.++ show t
342 show (ReprText t) = "ReprText " P.++ show t
@@ -340,6 +355,8 @@ instance V.RecAll FieldRepr fs Show => Show (JsonRepr ('SchemaObject fs)) where
340instance Show (JsonRepr s) => Show (JsonRepr ('SchemaOptional s)) where
355instance Show (JsonRepr s) => Show (JsonRepr ('SchemaOptional s)) where
341 show (ReprOptional s) = "ReprOptional " P.++ show s
356 show (ReprOptional s) = "ReprOptional " P.++ show s
358+deriving instance UnionAll f as Show => Show (Union f as)
343instance (Monad m, Serial m Text)
360instance (Monad m, Serial m Text)
344 => Serial m (JsonRepr ('SchemaText cs)) where
361 => Serial m (JsonRepr ('SchemaText cs)) where
345 series = cons1 ReprText
362 series = cons1 ReprText
@@ -388,6 +405,24 @@ fromOptional
388 -> Parser (Maybe (JsonRepr s))
405 -> Parser (Maybe (JsonRepr s))
389fromOptional _ = parseJSON
406fromOptional _ = parseJSON
409+ :: FromJSON (JsonRepr ('SchemaUnion ss))
410+ => sing (ss :: [Schema])
412+ -> Parser (JsonRepr ('SchemaUnion ss))
413+parseUnion _ val = parseJSON val
415+instance FromJSON (Union JsonRepr '[]) where
416+ parseJSON = fail "empty union"
418+instance (SingI a, FromJSON (Union JsonRepr as)) => FromJSON (Union JsonRepr (a ': as)) where
419+ parseJSON val = (This <$> parseJSON val)
420+ <|> (That <$> (parseJSON val :: Parser (Union JsonRepr as)))
422+instance ToJSON (Union JsonRepr as) where
423+ toJSON (This fa) = toJSON fa
424+ toJSON (That u) = toJSON u
391instance SingI schema => J.FromJSON (JsonRepr schema) where
426instance SingI schema => J.FromJSON (JsonRepr schema) where
392 parseJSON value = case sing :: Sing schema of
427 parseJSON value = case sing :: Sing schema of
393 SSchemaText _ -> withText "String" (pure . ReprText) value
428 SSchemaText _ -> withText "String" (pure . ReprText) value
@@ -427,8 +462,10 @@ instance SingI schema => J.FromJSON (JsonRepr schema) where
427 SSchemaOptional so -> case H.lookup fieldName h of
462 SSchemaOptional so -> case H.lookup fieldName h of
428 Just v -> withSingI so $ FieldRepr <$> parseJSON v
463 Just v -> withSingI so $ FieldRepr <$> parseJSON v
429 Nothing -> fail "schemaoptional"
464 Nothing -> fail "schemaoptional"
465+ SSchemaUnion ss -> withSingI ss $ FieldRepr <$> parseUnion ss value
430 (fieldRepr :&) <$> demoteFields tl h
466 (fieldRepr :&) <$> demoteFields tl h
431 ReprObject <$> withObject "Object" (demoteFields fs) value
467 ReprObject <$> withObject "Object" (demoteFields fs) value
468+ SSchemaUnion ss -> parseUnion ss value
433instance J.ToJSON (JsonRepr a) where
470instance J.ToJSON (JsonRepr a) where
434 toJSON ReprNull = J.Null
471 toJSON ReprNull = J.Null
@@ -449,6 +486,7 @@ instance J.ToJSON (JsonRepr a) where
449 fold = \case
486 fold = \case
450 RNil -> []
487 RNil -> []
451 fr@(FieldRepr _) :& tl -> (extract fr) : fold tl
488 fr@(FieldRepr _) :& tl -> (extract fr) : fold tl
489+ toJSON (ReprUnion u) = toJSON u
453class FalseConstraint a
491class FalseConstraint a