@@ -5,7 +5,7 @@
55
66module Data.Schematic.Schema where
77
8−import Control.Applicative ()
8+import Control.Applicative ((<|>))
99import Control.Monad
1010import Data.Aeson as J
1111import Data.Aeson.Types as J
@@ -14,10 +14,11 @@ import Data.Kind
1414import Data.Maybe
1515import Data.Schematic.Instances ()
1616import Data.Scientific
17−import Data.Singletons.Prelude.List hiding (All)
17+import Data.Singletons.Prelude.List hiding (All, Union)
1818import Data.Singletons.TH
1919import Data.Singletons.TypeLits
2020import Data.Text as T
21+import Data.Union
2122import Data.Vector as V
2223import Data.Vinyl hiding (Dict)
2324import qualified Data.Vinyl.TypeLevel as V
@@ -205,6 +206,7 @@ data Schema
205206 | SchemaArray [ArrayConstraint] Schema
206207 | SchemaNull
207208 | SchemaOptional Schema
209+ | SchemaUnion [Schema]
208210 deriving (Generic)
209211
210212data DemotedSchema
@@ -215,6 +217,7 @@ data DemotedSchema
215217 | DSchemaArray [DemotedArrayConstraint] DemotedSchema
216218 | DSchemaNull
217219 | DSchemaOptional DemotedSchema
220+ | DSchemaUnion [DemotedSchema]
218221 deriving (Generic)
219222
220223data instance Sing (schema :: Schema) where
@@ -225,6 +228,7 @@ data instance Sing (schema :: Schema) where
225228 SSchemaObject :: Sing fields -> Sing ('SchemaObject fields)
226229 SSchemaOptional :: Sing s -> Sing ('SchemaOptional s)
227230 SSchemaNull :: Sing 'SchemaNull
231+ SSchemaUnion :: Sing ss -> Sing ('SchemaUnion ss)
228232
229233instance SingI sl => SingI ('SchemaText sl) where
230234 sing = SSchemaText sing
@@ -240,6 +244,8 @@ instance SingI stl => SingI ('SchemaObject stl) where
240244 sing = SSchemaObject sing
241245instance SingI s => SingI ('SchemaOptional s) where
242246 sing = SSchemaOptional sing
247+instance SingI s => SingI ('SchemaUnion s) where
248+ sing = SSchemaUnion sing
243249
244250instance Eq (Sing ('SchemaText cs)) where _ == _ = True
245251instance Eq (Sing ('SchemaNumber cs)) where _ == _ = True
@@ -248,6 +254,7 @@ instance Eq (Sing 'SchemaBoolean) where _ == _ = True
248254instance Eq (Sing ('SchemaArray as s)) where _ == _ = True
249255instance Eq (Sing ('SchemaObject cs)) where _ == _ = True
250256instance Eq (Sing ('SchemaOptional s)) where _ == _ = True
257+instance Eq (Sing ('SchemaUnion s)) where _ == _ = True
251258
252259instance SingKind Schema where
253260 type Demote Schema = DemotedSchema
@@ -258,12 +265,8 @@ instance SingKind Schema where
258265 SSchemaArray cs s -> DSchemaArray (fromSing cs) (fromSing s)
259266 SSchemaOptional s -> DSchemaOptional $ fromSing s
260267 SSchemaNull -> DSchemaNull
261− SSchemaObject cs -> let
262− dem :: Sing (s :: [(Symbol, Schema)]) -> [(Text, DemotedSchema)]
263− dem SNil = []
264− dem (SCons (STuple2 ss ssch) fs) = withKnownSymbol ss
265− $ (T.pack (symbolVal ss), fromSing ssch) : dem fs
266− in DSchemaObject $ dem cs
268+ SSchemaObject cs -> DSchemaObject $ fromSing cs
269+ SSchemaUnion ss -> DSchemaUnion $ fromSing ss
267270 toSing = \case
268271 DSchemaText cs -> case toSing cs of
269272 SomeSing scs -> SomeSing $ SSchemaText scs
@@ -277,6 +280,8 @@ instance SingKind Schema where
277280 DSchemaNull -> SomeSing SSchemaNull
278281 DSchemaObject cs -> case toSing cs of
279282 SomeSing scs -> SomeSing $ SSchemaObject scs
283+ DSchemaUnion ss -> case toSing ss of
284+ SomeSing sss -> SomeSing $ SSchemaUnion sss
280285
281286data FieldRepr :: (Symbol, Schema) -> Type where
282287 FieldRepr
@@ -322,6 +327,16 @@ data JsonRepr :: Schema -> Type where
322327 ReprArray :: V.Vector (JsonRepr s) -> JsonRepr ('SchemaArray cs s)
323328 ReprObject :: Rec FieldRepr fs -> JsonRepr ('SchemaObject fs)
324329 ReprOptional :: Maybe (JsonRepr s) -> JsonRepr ('SchemaOptional s)
330+ ReprUnion :: Union JsonRepr s -> JsonRepr ('SchemaUnion ss)
331+
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)
336+
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
325340
326341instance Show (JsonRepr ('SchemaText cs)) where
327342 show (ReprText t) = "ReprText " P.++ show t
@@ -340,6 +355,8 @@ instance V.RecAll FieldRepr fs Show => Show (JsonRepr ('SchemaObject fs)) where
340355instance Show (JsonRepr s) => Show (JsonRepr ('SchemaOptional s)) where
341356 show (ReprOptional s) = "ReprOptional " P.++ show s
342357
358+deriving instance UnionAll f as Show => Show (Union f as)
359+
343360instance (Monad m, Serial m Text)
344361 => Serial m (JsonRepr ('SchemaText cs)) where
345362 series = cons1 ReprText
@@ -388,6 +405,24 @@ fromOptional
388405 -> Parser (Maybe (JsonRepr s))
389406fromOptional _ = parseJSON
390407
408+parseUnion
409+ :: FromJSON (JsonRepr ('SchemaUnion ss))
410+ => sing (ss :: [Schema])
411+ -> Value
412+ -> Parser (JsonRepr ('SchemaUnion ss))
413+parseUnion _ val = parseJSON val
414+
415+instance FromJSON (Union JsonRepr '[]) where
416+ parseJSON = fail "empty union"
417+
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)))
421+
422+instance ToJSON (Union JsonRepr as) where
423+ toJSON (This fa) = toJSON fa
424+ toJSON (That u) = toJSON u
425+
391426instance SingI schema => J.FromJSON (JsonRepr schema) where
392427 parseJSON value = case sing :: Sing schema of
393428 SSchemaText _ -> withText "String" (pure . ReprText) value
@@ -427,8 +462,10 @@ instance SingI schema => J.FromJSON (JsonRepr schema) where
427462 SSchemaOptional so -> case H.lookup fieldName h of
428463 Just v -> withSingI so $ FieldRepr <$> parseJSON v
429464 Nothing -> fail "schemaoptional"
465+ SSchemaUnion ss -> withSingI ss $ FieldRepr <$> parseUnion ss value
430466 (fieldRepr :&) <$> demoteFields tl h
431467 ReprObject <$> withObject "Object" (demoteFields fs) value
468+ SSchemaUnion ss -> parseUnion ss value
432469
433470instance J.ToJSON (JsonRepr a) where
434471 toJSON ReprNull = J.Null
@@ -449,6 +486,7 @@ instance J.ToJSON (JsonRepr a) where
449486 fold = \case
450487 RNil -> []
451488 fr@(FieldRepr _) :& tl -> (extract fr) : fold tl
489+ toJSON (ReprUnion u) = toJSON u
452490
453491class FalseConstraint a
454492