packages feed

verdict-json-0.0.0.0: src/Verdict/JSON/Types.hs

{-# LANGUAGE DeriveAnyClass    #-}
{-# LANGUAGE OverloadedStrings #-}
module Verdict.JSON.Types where

import           Data.Aeson
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Map            as Map
import           Data.Maybe
import           Data.Monoid
import qualified Data.Text           as Text
import           Data.Vector         (fromList)
import           GHC.Generics        (Generic)

data NumericT = JSONInteger | JSONNumeric
  deriving (Eq, Show, Read, Generic)

data AnySchema = ObjectS ObjectSchema
               | NumericS NumericSchema
               {-| ArrayS ArraySchema-}
               | StringS StringSchema
               | EmptyS
  deriving (Eq, Show, Read, Generic)

instance Monoid AnySchema where
    mempty = EmptyS
    (ObjectS a)  `mappend` (ObjectS b)  = ObjectS (a <> b)
    (NumericS a) `mappend` (NumericS b) = NumericS (a <> b)
    (StringS a)  `mappend` (StringS b)  = StringS (a <> b)
    EmptyS       `mappend` x            = x
    _            `mappend` _            = error "must be same constructor"

instance ToJSON AnySchema where
    toJSON (ObjectS os)  = let (Object o) = toJSON os
                           in Object $ HashMap.insert "type" (String "object") o
    toJSON (NumericS ns) = let (Object o) = toJSON ns
                           in Object $ HashMap.insert "type" (String "number") o
    toJSON (StringS ss)  = let (Object o) = toJSON ss
                           in Object $ HashMap.insert "type" (String "string") o
    toJSON EmptyS        = object []

data Either' a b = Left' a | Right' b
    deriving (Eq, Show, Read, Functor, Generic)

instance (ToJSON a, ToJSON b) => ToJSON (Either' a b) where
    toJSON (Left' a)  = toJSON a
    toJSON (Right' b) = toJSON b

data ObjectSchema = ObjectSchema
    { properties           :: Map.Map Text.Text (Required, AnySchema)
    , additionalProperties :: ()
    , patternProperties    :: Map.Map Text.Text AnySchema
    } deriving (Eq, Show, Read, Generic)

instance Monoid ObjectSchema where
    mempty = ObjectSchema mempty mempty mempty
    a `mappend` b = ObjectSchema
        { properties           = properties a <> properties b
        , additionalProperties = mempty
        , patternProperties    = patternProperties a <> patternProperties b
        }

instance ToJSON ObjectSchema where
    toJSON os = object [
        "properties"           .= toJSON (snd <$> properties os)
      , "required"             .= Array (String <$> fromList reqs)
      {-, "additionalProperties" .= toJSON (additionalProperties os)-}
      {-, "patternProperties"    .= toJSON (patternProperties os)-}
      ]
      where reqs = Map.keys $ Map.filter ((== Required) . fst) $ properties os

data NumericSchema = NumericSchema
    { multipleOf  :: [Int]
    , maximum'    :: Maybe Max
    , minimum'    :: Maybe Min
    } deriving (Eq, Show, Read, Generic)

instance Monoid NumericSchema where
    mempty = NumericSchema mempty mempty mempty
    a `mappend` b = NumericSchema { multipleOf = multipleOf a <> multipleOf b
                                  , maximum' = maximum' a <> maximum' b
                                  , minimum' = minimum' a <> minimum' b
                                  }

instance ToJSON NumericSchema where
    toJSON ns = object $ catMaybes go
      where
        go = [ ("multipleOf" .=) <$> (toJSON <$> listToMaybe (multipleOf ns))
             , ("maximum"    .=) <$> (toJSON . unMax <$> maximum' ns)
             , ("minimum"    .=) <$> (toJSON . unMin <$> minimum' ns)
             ]

newtype Max = Max { unMax :: Int}
    deriving (Eq, Show, Bounded, Ord, Read, Generic)

instance Monoid Max where
    mempty  = minBound
    mappend = max

newtype Min = Min { unMin :: Int }
    deriving (Eq, Show, Bounded, Ord, Read, Generic)

instance Monoid Min where
    mempty  = maxBound
    mappend = min

data ArraySchema = ArraySchema
    { items           :: [AnySchema]
    , additionalItems :: Either' Bool AnySchema
    } deriving (Eq, Show, Read, Generic)

data StringSchema = StringSchema
    { maxLength :: Maybe Max
    , minLength :: Maybe Min
    {-, pattern   :: Maybe Regex-}
    } deriving (Eq, Show, Read, Generic)

instance Monoid StringSchema where
    mempty = StringSchema mempty mempty
    a `mappend` b = StringSchema { maxLength = maxLength a <> maxLength b
                                 , minLength = minLength a <> minLength b
                                 }

instance ToJSON StringSchema where
    toJSON ss = object $
        catMaybes [ ("maxLength" .=) <$> (toJSON . unMax <$> maxLength ss)
                  , ("minLength" .=) <$> (toJSON . unMin <$> minLength ss)
                  ]

data Metadata = Metadata
    { title       :: Maybe Text.Text
    , description :: Maybe Text.Text
    } deriving (Eq, Show, Read, Generic)

data SchemaVersion = Draft4
  deriving (Eq, Show, Read, Generic)

data Required = Required | NotRequired
  deriving (Eq, Show, Read, Generic)