module JSONSchema.Validator.Draft4.Array where
import Import
import qualified Data.List.NonEmpty as NE
import qualified Data.Vector as V
import qualified JSONPointer as JP
import JSONSchema.Validator.Utils (allUniqueValues)
newtype MaxItems
= MaxItems { _unMaxItems :: Int }
deriving (Eq, Show)
instance FromJSON MaxItems where
parseJSON = withObject "MaxItems" $ \o ->
MaxItems <$> o .: "maxItems"
data MaxItemsInvalid
= MaxItemsInvalid MaxItems (Vector Value)
deriving (Eq, Show)
maxItemsVal :: MaxItems -> Vector Value -> Maybe MaxItemsInvalid
maxItemsVal a@(MaxItems n) xs
| n < 0 = Nothing
| V.length xs > n = Just (MaxItemsInvalid a xs)
| otherwise = Nothing
newtype MinItems
= MinItems { _unMinItems :: Int }
deriving (Eq, Show)
instance FromJSON MinItems where
parseJSON = withObject "MinItems" $ \o ->
MinItems <$> o .: "minItems"
data MinItemsInvalid
= MinItemsInvalid MinItems (Vector Value)
deriving (Eq, Show)
minItemsVal :: MinItems -> Vector Value -> Maybe MinItemsInvalid
minItemsVal a@(MinItems n) xs
| n < 0 = Nothing
| V.length xs < n = Just (MinItemsInvalid a xs)
| otherwise = Nothing
newtype UniqueItems
= UniqueItems { _unUniqueItems :: Bool }
deriving (Eq, Show)
instance FromJSON UniqueItems where
parseJSON = withObject "UniqueItems" $ \o ->
UniqueItems <$> o .: "uniqueItems"
newtype UniqueItemsInvalid
= UniqueItemsInvalid (Vector Value)
deriving (Eq, Show)
uniqueItemsVal :: UniqueItems -> Vector Value -> Maybe UniqueItemsInvalid
uniqueItemsVal (UniqueItems True) xs
| allUniqueValues xs = Nothing
| otherwise = Just (UniqueItemsInvalid xs)
uniqueItemsVal (UniqueItems False) _ = Nothing
data ItemsRelated schema = ItemsRelated
{ _irItems :: Maybe (Items schema)
, _irAdditional :: Maybe (AdditionalItems schema)
} deriving (Eq, Show)
instance FromJSON schema => FromJSON (ItemsRelated schema) where
parseJSON = withObject "ItemsRelated" $ \o -> ItemsRelated
<$> o .:! "items"
<*> o .:! "additionalItems"
emptyItems :: ItemsRelated schema
emptyItems = ItemsRelated
{ _irItems = Nothing
, _irAdditional = Nothing
}
data Items schema
= ItemsObject schema
| ItemsArray [schema]
deriving (Eq, Show)
instance FromJSON schema => FromJSON (Items schema) where
parseJSON v = fmap ItemsObject (parseJSON v)
<|> fmap ItemsArray (parseJSON v)
instance ToJSON schema => ToJSON (Items schema) where
toJSON (ItemsObject hm) = toJSON hm
toJSON (ItemsArray schemas) = toJSON schemas
instance Arbitrary schema => Arbitrary (Items schema) where
arbitrary = oneof [ ItemsObject <$> arbitrary
, ItemsArray <$> arbitrary
]
data ItemsRelatedInvalid err
= IRInvalidItems (ItemsInvalid err)
| IRInvalidAdditional (AdditionalItemsInvalid err)
deriving (Eq, Show)
data ItemsInvalid err
= ItemsObjectInvalid (NonEmpty (JP.Index, NonEmpty err))
| ItemsArrayInvalid (NonEmpty (JP.Index, NonEmpty err))
deriving (Eq, Show)
itemsRelatedVal
:: forall err schema.
(schema -> Value -> [err])
-> ItemsRelated schema
-> Vector Value
-> [ItemsRelatedInvalid err]
itemsRelatedVal f a xs =
let (itemsFailure, remaining) = case _irItems a of
Nothing -> (Nothing, mempty)
Just b -> itemsVal f b xs
additionalFailure = (\b -> additionalItemsVal f b remaining)
=<< _irAdditional a
in catMaybes [ IRInvalidItems <$> itemsFailure
, IRInvalidAdditional <$> additionalFailure
]
itemsVal
:: forall err schema.
(schema -> Value -> [err])
-> Items schema
-> Vector Value
-> (Maybe (ItemsInvalid err), [(JP.Index, Value)])
itemsVal f a xs =
case a of
ItemsObject subSchema ->
case NE.nonEmpty (mapMaybe (validateElem subSchema) indexed) of
Nothing -> (Nothing, mempty)
Just errs -> (Just (ItemsObjectInvalid errs), mempty)
ItemsArray subSchemas ->
let remaining = drop (length subSchemas) indexed
res = catMaybes (zipWith validateElem subSchemas indexed)
in case NE.nonEmpty res of
Nothing -> (Nothing, remaining)
Just errs -> (Just (ItemsArrayInvalid errs), remaining)
where
indexed :: [(JP.Index, Value)]
indexed = zip (JP.Index <$> [0..]) (V.toList xs)
validateElem
:: schema
-> (JP.Index, Value)
-> Maybe (JP.Index, NonEmpty err)
validateElem schema (index,x) = (index,) <$> NE.nonEmpty (f schema x)
data AdditionalItems schema
= AdditionalBool Bool
| AdditionalObject schema
deriving (Eq, Show)
instance FromJSON schema => FromJSON (AdditionalItems schema) where
parseJSON v = fmap AdditionalBool (parseJSON v)
<|> fmap AdditionalObject (parseJSON v)
instance ToJSON schema => ToJSON (AdditionalItems schema) where
toJSON (AdditionalBool b) = toJSON b
toJSON (AdditionalObject hm) = toJSON hm
instance Arbitrary schema => Arbitrary (AdditionalItems schema) where
arbitrary = oneof [ AdditionalBool <$> arbitrary
, AdditionalObject <$> arbitrary
]
data AdditionalItemsInvalid err
= AdditionalItemsBoolInvalid (NonEmpty (JP.Index, Value))
| AdditionalItemsObjectInvalid (NonEmpty (JP.Index, NonEmpty err))
deriving (Eq, Show)
additionalItemsVal
:: forall err schema.
(schema -> Value -> [err])
-> AdditionalItems schema
-> [(JP.Index, Value)]
-> Maybe (AdditionalItemsInvalid err)
additionalItemsVal _ (AdditionalBool True) _ = Nothing
additionalItemsVal _ (AdditionalBool False) xs =
AdditionalItemsBoolInvalid <$> NE.nonEmpty xs
additionalItemsVal f (AdditionalObject subSchema) xs =
let res = mapMaybe
(\(index,x) -> (index,) <$> NE.nonEmpty (f subSchema x))
xs
in AdditionalItemsObjectInvalid <$> NE.nonEmpty res