module Data.Aeson.Validation.Internal where
import Data.Bits (xor)
import Data.Hashable (Hashable(..))
import Data.List.NonEmpty (NonEmpty(..), (<|))
import Data.Scientific
import Data.Semigroup
import Data.Text (Text)
import Lens.Micro
import Prelude.Compat
import qualified Data.List.NonEmpty as NonEmpty
import qualified GHC.Exts as GHC
data Schema
= SBool
| STrue
| SFalse
| SNumber
| SInteger
| STheNumber !Scientific
| STheInteger !Integer
| SSomeNumber (Scientific -> Bool) !(Maybe Text)
| SString
| STheString !Text
| SSomeString (Text -> Bool) !(Maybe Text)
| SDateTime
| SObject !Strict ![ShallowField]
| SArray !Unique !Int !Int !Schema
| STuple ![Schema]
| SAnything
| SNullable !Schema
| SAlts !(NonEmpty Schema)
| SNegate !Schema
instance Num Schema where
(+) = error "Data.Aeson.Validation: (+) not implemented for Schema"
(-) = error "Data.Aeson.Validation: (-) not implemented for Schema"
(*) = error "Data.Aeson.Validation: (*) not implemented for Schema"
abs = error "Data.Aeson.Validation: abs not implemented for Schema"
signum = error "Data.Aeson.Validation: signum not implemented for Schema"
fromInteger = STheInteger
negate = SNegate
instance Fractional Schema where
(/) = error "Data.Aeson.Validation: (/) not implemented for Schema"
recip = error "Data.Aeson.Validation: recip not implemented for Schema"
fromRational = STheNumber . fromRational
instance Semigroup Schema where
SAlts xs <> SAlts ys = SAlts (xs <> ys)
SAlts (x:|xs) <> y = SAlts (x :| xs ++ [y])
x <> SAlts ys = SAlts (x <| ys)
x <> y = SAlts (x :| [y])
instance GHC.IsString Schema where
fromString = STheString . GHC.fromString
data Demand
= Opt
| Req
deriving Eq
instance Hashable Demand where
hash Opt = 0
hash Req = 1
hashWithSalt s x = s * 16777619 `xor` hash x
data Field
= Field !Demand !Path !Schema
data ShallowField = ShallowField
{ fieldDemand :: !Demand
, fieldKey :: !Text
, fieldSchema :: !Schema
}
fieldSchemaL :: Lens' ShallowField Schema
fieldSchemaL f (ShallowField a b c) = (\c' -> ShallowField a b c') <$> f c
data Path
= Link !Text !Path
| Leaf !Text
deriving (Eq, Ord)
instance Show Path where
show = show . GHC.toList
instance GHC.IsString Path where
fromString = Leaf . GHC.fromString
instance GHC.IsList Path where
type Item Path = Text
fromList :: [GHC.Item Path] -> Path
fromList [] =
errorWithoutStackTrace "Data.Aeson.Validation.Path.fromList: empty list"
fromList xs0 = go xs0
where
go :: [Text] -> Path
go [] = error "impossible"
go [x] = Leaf x
go (x:xs) = Link x (go xs)
toList :: Path -> [GHC.Item Path]
toList (Leaf x) = [x]
toList (Link x xs) = x : GHC.toList xs
data Strict
= Strict
| NotStrict
data Unique
= Unique
| NotUnique
deriving Eq
normalize :: Schema -> Schema
normalize = transform
(\case
SAlts ss ->
SAlts (NonEmpty.fromList (NonEmpty.toList ss >>= unAlt))
s -> s)
where
unAlt :: Schema -> [Schema]
unAlt (SAlts ss) = NonEmpty.toList ss
unAlt s = [s]
universe :: Schema -> [Schema]
universe = \case
SBool -> [SBool]
STrue -> [STrue]
SFalse -> [SFalse]
SNumber -> [SNumber]
SInteger -> [SInteger]
STheNumber a -> [STheNumber a]
STheInteger a -> [STheInteger a]
SSomeNumber a b -> [SSomeNumber a b]
SString -> [SString]
STheString a -> [STheString a]
SSomeString a b -> [SSomeString a b]
SDateTime -> [SDateTime]
SObject a b -> SObject a b : concatMap universe (map fieldSchema b)
SArray a b c d -> SArray a b c d : universe d
STuple a -> STuple a : concatMap universe a
SAnything -> [SAnything]
SNullable a -> SNullable a : universe a
SAlts a -> SAlts a : concatMap universe (NonEmpty.toList a)
SNegate a -> SNegate a : universe a
transform :: (Schema -> Schema) -> Schema -> Schema
transform f = f . transform' (transform f)
transform' :: (Schema -> Schema) -> Schema -> Schema
transform' f = \case
SBool -> SBool
STrue -> STrue
SFalse -> SFalse
SNumber -> SNumber
SInteger -> SInteger
STheNumber a -> STheNumber a
STheInteger a -> STheInteger a
SSomeNumber a b -> SSomeNumber a b
SString -> SString
STheString a -> STheString a
SSomeString a b -> SSomeString a b
SDateTime -> SDateTime
SObject a b -> SObject a (b & each.fieldSchemaL %~ f)
SArray a b c d -> SArray a b c (f d)
STuple a -> STuple (map f a)
SAnything -> SAnything
SNullable a -> SNullable (f a)
SAlts a -> SAlts (fmap f a)
SNegate a -> SNegate (f a)