@@ -1,24 +1,32 @@
11{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE OverloadedLists #-}
3+{-# LANGUAGE OverloadedStrings #-}
24
35module Data.Schematic.Validation where
46
57import Control.Applicative
68import Control.Category ((<<<), (>>>))
9+import Data.Aeson as J
710import Data.Eq.Deriving (deriveEq1)
811import Data.Foldable as F
912import Data.Functor.Classes
1013import Data.Functor.Foldable
14+import Data.HashMap.Strict as H
1115import Data.Kind
1216import Data.Maybe
17+import Data.Scientific
1318import Data.Singletons.Decide
19+import Data.Singletons.Prelude.List
1420import Data.Singletons.TH
21+import Data.Singletons.TypeLits
1522import Data.Text as T
23+import Data.Vector as V
1624import Data.Vinyl hiding (Dict)
1725import Data.Vinyl.Functor
1826import Text.Show.Deriving (deriveShow1)
1927
2028
21−-- | Type Tags
29+-- -- | Type Tags
2230data JType
2331 = JText
2432 | JNumber
@@ -29,52 +37,127 @@ data JType
2937
3038genSingletons [ ''JType ]
3139
32−instance Show (Sing JText) where showsPrec 4 _ = showString "SJText"
33−instance Show (Sing JNumber) where showsPrec 4 _ = showString "SJNumber"
34−instance Show (Sing JArray) where showsPrec 4 _ = showString "SJArray"
35−instance Show (Sing JObject) where showsPrec 4 _ = showString "SJObject"
36−instance Show (Sing JNull) where showsPrec 4 _ = showString "SJNull"
40+-- instance Show (Sing JText) where showsPrec 4 _ = showString "SJText"
41+-- instance Show (Sing JNumber) where showsPrec 4 _ = showString "SJNumber"
42+-- instance Show (Sing JArray) where showsPrec 4 _ = showString "SJArray"
43+-- instance Show (Sing JObject) where showsPrec 4 _ = showString "SJObject"
44+-- instance Show (Sing JNull) where showsPrec 4 _ = showString "SJNull"
3745
38−instance Eq (Sing JText) where (==) _ _ = True
39−instance Eq (Sing JNumber) where (==) _ _ = True
40−instance Eq (Sing JArray) where (==) _ _ = True
41−instance Eq (Sing JObject) where (==) _ _ = True
42−instance Eq (Sing JNull) where (==) _ _ = True
46+-- instance Eq (Sing JText) where (==) _ _ = True
47+-- instance Eq (Sing JNumber) where (==) _ _ = True
48+-- instance Eq (Sing JArray) where (==) _ _ = True
49+-- instance Eq (Sing JObject) where (==) _ _ = True
50+-- instance Eq (Sing JNull) where (==) _ _ = True
4351
44−type family CRepr (jty :: JType) :: Type where
45− CRepr JText = TextConstraint
46− CRepr JNumber = NumberConstraint
47− CRepr JObject = (String, Schema)
48− CRepr JArray = ArrayConstraint
52+type family CRepr (jty :: Schema) :: Type where
53+ CRepr (SchemaText cs) = TextConstraint
54+ CRepr (SchemaNumber cs) = NumberConstraint
55+ CRepr (SchemaObject fs) = (String, Schema)
56+ CRepr (SchemaArray ar s) = ArrayConstraint
4957
5058data TextConstraint
51− = LengthEq Integer
52− | LengthLe Integer
53− | LengthGt Integer
54− deriving (Show, Eq)
59+ = TEq Nat
60+ | TLe Nat
61+ | TGt Nat
62+ | Regex Symbol
63+
64+data instance Sing (tc :: TextConstraint) where
65+ STextLengthEq :: Sing n -> Sing (TEq n)
66+ STextLengthLe :: Sing n -> Sing (TLe n)
67+ STextLengthGt :: Sing n -> Sing (TGt n)
5568
5669data NumberConstraint
57− = Le Integer
58− | Gt Integer
59− | Eq Integer
60− deriving (Show, Eq)
70+ = NLe Nat
71+ | NGt Nat
72+ | NEq Nat
73+
74+data instance Sing (nc :: NumberConstraint) where
75+ SNumberEq :: Sing n -> Sing (NEq n)
76+ SNumberGt :: Sing n -> Sing (NGt n)
77+ SNumberLe :: Sing n -> Sing (NLe n)
6178
6279data ArrayConstraint
63− = LengthArrEq Integer
64− deriving (Show, Eq)
80+ = AEq Nat
6581
66−type ObjectConstraint s = [(String, s)]
82+data instance Sing (ac :: ArrayConstraint) where
83+ SArrayEq :: Sing n -> Sing (AEq n)
6784
68−data SchemaF s
85+data Schema
6986 = SchemaText [TextConstraint]
7087 | SchemaNumber [NumberConstraint]
71− | SchemaObject (ObjectConstraint s)
72− | SchemaArray [ArrayConstraint] s
88+ | SchemaObject [(Symbol, Schema)]
89+ | SchemaArray [ArrayConstraint] Schema
7390 | SchemaNull
74− deriving (Functor)
75−
76−type Schema = Fix SchemaF
77−
78−deriveEq1 ''SchemaF
7991
80−deriveShow1 ''SchemaF
92+data instance Sing (schema :: Schema) where
93+ SSchemaText :: Sing tcs -> Sing (SchemaText tcs)
94+ SSchemaNumber :: Sing ncs -> Sing (SchemaNumber ncs)
95+ SSchemaArray :: Sing acs -> Sing schema -> Sing (SchemaArray acs schema)
96+ SSchemaObject :: Sing fields -> Sing (SchemaObject fields)
97+ SSchemaNull :: Sing SchemaNull
98+
99+type family MapSnd (l :: [(k, Schema)]) :: [Schema] where
100+ MapSnd '[] = '[]
101+ MapSnd ( '(f,s) ': tl) = s ': MapSnd tl
102+
103+data FieldRepr :: (Symbol, Schema) -> Type where
104+ FieldRepr :: KnownSymbol name => JsonRepr schema -> FieldRepr '(name, schema)
105+
106+data JsonRepr :: Schema -> Type where
107+ ReprText :: Text -> JsonRepr (SchemaText cs)
108+ ReprNumber :: Scientific -> JsonRepr (SchemaNumber cs)
109+ ReprNull :: JsonRepr SchemaNull
110+ ReprArray :: V.Vector (JsonRepr s) -> JsonRepr (SchemaArray cs s)
111+ ReprObject :: Rec FieldRepr fs -> JsonRepr (SchemaObject fs)
112+
113+instance J.ToJSON (JsonRepr a) where
114+ toJSON (ReprText t) = J.String t
115+ toJSON ReprNull = J.Null
116+ toJSON (ReprNumber n) = J.Number n
117+ toJSON (ReprArray v) = J.Array $ toJSON <$> v
118+ toJSON (ReprObject r) = let
119+ extract
120+ :: forall name s
121+ . (KnownSymbol name)
122+ => FieldRepr '(name, s)
123+ -> (Text, Value)
124+ extract (FieldRepr s) = (T.pack $ symbolVal $ Proxy @name, toJSON s)
125+ fold :: Rec FieldRepr fs -> [(Text, J.Value)]
126+ fold = \case
127+ RNil -> []
128+ fr@(FieldRepr s) :& tl -> (extract fr) : fold tl
129+ in J.Object . H.fromList . fold $ r
130+
131+type Spec
132+ = SchemaObject
133+ '[ '("foo", SchemaArray '[AEq 3] (SchemaNumber '[NGt 10]))
134+ , '("bar", SchemaText '[Regex "\\w+"])]
135+
136+spec :: (SingKind Schema, SingI Spec) => DemoteRep Schema
137+spec = fromSing (sing :: Sing Spec)
138+
139+exampleTest :: JsonRepr (SchemaText '[TEq 3])
140+exampleTest = ReprText "ololo"
141+
142+exampleNumber :: JsonRepr (SchemaNumber '[NEq 3])
143+exampleNumber = ReprNumber 3
144+
145+exampleArray :: JsonRepr (SchemaArray '[AEq 1] (SchemaNumber '[NEq 3]))
146+exampleArray = ReprArray [exampleNumber]
147+
148+exampleObject
149+ :: JsonRepr (SchemaObject '[ '("foo", SchemaArray '[AEq 1] (SchemaNumber '[NEq 3]))] )
150+exampleObject = ReprObject $ FieldRepr exampleArray :& RNil
151+
152+example :: JsonRepr Spec
153+example = ReprObject $
154+ FieldRepr (ReprArray [ReprNumber 3])
155+ :& FieldRepr (ReprText "test")
156+ :& RNil
157+
158+class FalseConstraint
159+
160+type family TopLevel (spec :: Schema) :: Constraint where
161+ TopLevel (SchemaArray a e) = ()
162+ TopLevel (SchemaObject o) = ()
163+ TopLevel spec = FalseConstraint