packages feed

json-schema (empty) → 0.4

raw patch · 7 files changed

+284/−0 lines, 7 filesdep +aesondep +basedep +containerssetup-changed

Dependencies added: aeson, base, containers, generic-aeson, generic-deriving, tagged, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Silk++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Silk nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ json-schema.cabal view
@@ -0,0 +1,31 @@+Name:                json-schema+Version:             0.4+Synopsis:            Types and type classes for defining JSON schemas.+Description:         Types and type classes for defining JSON schemas.+License:             BSD3+License-file:        LICENSE+Author:              Silk+Maintainer:          code@silk.co+Category:            Data+Build-type:          Simple+Cabal-version:       >=1.6++Library+  Hs-Source-Dirs:    src+  Exposed-Modules:   Data.JSON.Schema+                   , Data.JSON.Schema.Combinators+                   , Data.JSON.Schema.Generic+                   , Data.JSON.Schema.Types+  Build-Depends:     base == 4.*+                   , aeson >= 0.6 && < 0.8+                   , containers >= 0.3 && < 0.6+                   , generic-aeson == 0.1.*+                   , generic-deriving == 1.6.*+                   , tagged >= 0.2 && < 0.8+                   -- No bounds, since we only use the Text type.+                   , text+  Ghc-Options:       -Wall++Source-repository head+  Type:              Git+  Location:          https://github.com/silkapp/json-schema.git
+ src/Data/JSON/Schema.hs view
@@ -0,0 +1,9 @@+module Data.JSON.Schema+  ( module Data.JSON.Schema.Generic+  , module Data.JSON.Schema.Types+  , Proxy (..)+  ) where++import Data.JSON.Schema.Generic+import Data.JSON.Schema.Types+import Data.Proxy
+ src/Data/JSON/Schema/Combinators.hs view
@@ -0,0 +1,58 @@+-- | Combinators for creating JSON schemas.+module Data.JSON.Schema.Combinators where++import Data.JSON.Schema.Types++-- | A schema combinator.+type SchemaC = Schema -> Schema++-- | Optionality operator for schemas.+infixl 3 <|>+(<|>) :: Schema -> Schema -> Schema+(Choice s1) <|> (Choice s2) = Choice $ s1 ++ s2+(Choice s1) <|> v           = Choice $ s1 ++ [v]+v           <|> (Choice s2) = Choice $ v : s2+v1          <|> v2          = Choice $ [v1, v2]++-- | Tupling.+infixl 4 <+>+(<+>) :: Schema -> Schema -> Schema+(Tuple t1) <+> (Tuple t2) = Tuple $ t1 ++ t2+(Tuple t1) <+> v          = Tuple $ t1 ++ [v]+v          <+> (Tuple t2) = Tuple $ v : t2+v1         <+> v2         = Tuple $ [v1, v2]++-- | If passed two objects, merges the fields. Otherwise creates a+-- tuple.+merge :: Schema -> Schema -> Schema+merge (Object f1) (Object f2) = Object $ f1 ++ f2+merge v1          v2          = v1 <+> v2++-- | Create an object with a single field.+field :: String -> Bool -> Schema -> Schema+field k r v = Object [Field k r v]++-- | An unbounded string.+value :: Schema+value = Value 0 (-1)++-- | An unbounded number.+number :: Schema+number = Number 0 (-1)++-- | An unbounded array with non-unique values.+array :: Schema -> Schema+array = Array 0 (-1) False++-- | Add a field to an object, or tuple if passed a non-object.+addField :: String -> Bool -> Schema -> SchemaC+addField k r v = merge $ field k r v++-- | Add multiple fields to an object, or tuple if passed a+-- non-object.+addFields :: [(String, Bool, Schema)] -> SchemaC+addFields = flip $ foldr (\(k, r, v) -> addField k r v)++-- | An empty object.+empty :: Schema+empty = Object []
+ src/Data/JSON/Schema/Generic.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE TypeOperators+           , TupleSections+           , ScopedTypeVariables+           , FlexibleContexts+           , FlexibleInstances+           , OverlappingInstances+           , TypeFamilies+           #-}+-- | Generic derivation of schemas. The schemas generated match the+-- JSON generated by type 'generic-aeson' package. See that package+-- for documentation on the format and examples of it.+module Data.JSON.Schema.Generic (gSchema) where++import Control.Applicative hiding (empty, (<|>))+import Data.Char+import Data.JSON.Schema.Combinators+import Data.JSON.Schema.Types+import Data.Maybe+import Data.Proxy+import GHC.Generics+import Generics.Deriving.ConNames+import Generics.Generic.IsEnum++class GJSONSCHEMA f where+  gSchema' :: Bool -> Bool -> Proxy (f a) -> Schema++-- Recursive positions disabled for now, it causes infintite data structures. This is a problem to be solved!+{-+instance GJSONSCHEMA I where+  gSchema' _ f = f . fmap unI+-}++instance JSONSchema c => GJSONSCHEMA (K1 i c) where+  gSchema' _ _ = schema . fmap unK1++instance GJSONSCHEMA (K1 i String) where+  gSchema' _ _ _ = Value 0 (-1)++instance GJSONSCHEMA U1 where+  gSchema' _ _ _ = empty++instance (GJSONSCHEMA f, GJSONSCHEMA g) => GJSONSCHEMA (f :+: g) where+  gSchema' mc enm p =+        gSchema' mc enm (gL <$> p)+    <|> gSchema' mc enm (gR <$> p)+    where+      gL :: (f :+: g) r -> f r+      gL _ = undefined+      gR :: (f :+: g) r -> g r+      gR _ = undefined++gFst :: (f :*: g) r -> f r+gFst (f :*: _) = f++gSnd :: (f :*: g) r -> g r+gSnd (_ :*: g) = g++instance (GJSONSCHEMA f, GJSONSCHEMA g) => GJSONSCHEMA (f :*: g) where+  gSchema' mc enm p = gSchema' mc enm (gFst <$> p) `merge` gSchema' mc enm (gSnd <$> p)++instance (Constructor c, GJSONSCHEMA f) => GJSONSCHEMA (M1 C c f) where+  gSchema' _  True = const $ Value 0 (-1)+  gSchema' mc enm = wrap . gSchema' mc enm . fmap unM1+    where+      wrap = if mc+             then field (firstLetterToLower $ conName (undefined :: M1 C c f p)) True+             else id++instance GJSONSCHEMA f => GJSONSCHEMA (M1 D c f) where+  gSchema' mc enm = gSchema' mc enm . fmap unM1++firstLetterToLower :: String -> String+firstLetterToLower ""     = ""+firstLetterToLower (l:ls) = toLower l : ls++instance (Selector c, JSONSchema a) => GJSONSCHEMA (M1 S c (K1 i (Maybe a))) where+  gSchema' _ _ = field (selName (undefined :: M1 S c f p)) False . schema . fmap (fromJust . unK1 . unM1)++instance Selector c => GJSONSCHEMA (M1 S c (K1 i (Maybe String))) where+  gSchema' _ _ _ = field (selName (undefined :: M1 S c f p)) False $ Value 0 (-1)++instance (Selector c, GJSONSCHEMA f) => GJSONSCHEMA (M1 S c f) where+  gSchema' mc enm = field (selName (undefined :: M1 S c f p)) True . gSchema' mc enm . fmap unM1++multipleConstructors :: (Generic a, ConNames (Rep a)) => Proxy a -> Bool+multipleConstructors = (> 1) . length . conNames . pv+  where pv :: Proxy a -> a+        pv _ = undefined++-- | Derive a JSON schema for types with an instance of 'Generic'.+gSchema :: (Generic a, GJSONSCHEMA (Rep a), ConNames (Rep a), GIsEnum (Rep a)) => Proxy a -> Schema+gSchema p = gSchema' (multipleConstructors p) (isEnum p) (fmap from p)
+ src/Data/JSON/Schema/Types.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}+-- | Types for defining JSON schemas.+module Data.JSON.Schema.Types where++import Data.Maybe+import Data.Proxy+import Data.Text (Text)+import Data.Word (Word32)++-- | A schema is any JSON value.+type Schema = Value++-- | A schema for a JSON value.+data Value =+    Choice [Value] -- ^ A choice of multiple values, e.g. for sum types.+  | Object [Field] -- ^ A JSON object.+  | Array Int Int Bool Value -- ^ An array. The integers represent the+                             -- lower and upper bound of the array+                             -- size. The value -1 indicates no bound.+                             -- The boolean denotes whether items have+                             -- to unique.+  | Tuple [Value]   -- ^ A fixed-length tuple of different values.+  | Value Int Int   -- ^ A string. The integers denote the lower and+                    -- upper bound of the length of the string. The+                    -- value -1 indicates no bound.+  | Boolean+  | Number Int Int  -- ^ A number. The integers denote the lower and+                    -- upper bound on the value. The value -1+                    -- indicates no bound.+  | Null+  deriving (Eq, Show)++-- | A field in an object.+data Field = Field { key :: String, required :: Bool, content :: Schema } deriving (Eq, Show)++-- | Class representing JSON schemas+class JSONSchema a where+  schema :: Proxy a -> Schema++instance JSONSchema () where+  schema _ = Null++instance JSONSchema Int where+  schema _ = Number 0 (-1)++instance JSONSchema Integer where+  schema _ = Number 0 (-1)++instance JSONSchema Word32 where+  schema _ = Number 0 4294967295++instance JSONSchema Bool where+  schema _ = Boolean++instance JSONSchema Text where+  schema _ = Value 0 (-1)++instance JSONSchema a => JSONSchema (Maybe a) where+  schema p = Choice [Object [Field "Just" True $ schema $ fmap fromJust p], Object [Field "Nothing" True Null]]++instance JSONSchema a => JSONSchema [a] where+  schema = Array 0 (-1) False . schema . fmap head