packages feed

aeson-schemas 1.3.1 → 1.3.2

raw patch · 13 files changed

+192/−125 lines, 13 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.Aeson.Schema.Show: SchemaList :: SchemaType -> SchemaType
- Data.Aeson.Schema.Show: SchemaMaybe :: SchemaType -> SchemaType
- Data.Aeson.Schema.Show: SchemaObject :: [(SchemaKeyV, SchemaType)] -> SchemaType
- Data.Aeson.Schema.Show: SchemaScalar :: String -> SchemaType
- Data.Aeson.Schema.Show: SchemaTry :: SchemaType -> SchemaType
- Data.Aeson.Schema.Show: SchemaUnion :: [SchemaType] -> SchemaType
- Data.Aeson.Schema.Show: data SchemaType
- Data.Aeson.Schema.Show: instance GHC.Classes.Eq Data.Aeson.Schema.Show.SchemaType
- Data.Aeson.Schema.Show: instance GHC.Show.Show Data.Aeson.Schema.Show.SchemaType
- Data.Aeson.Schema.Show: showSchemaType :: SchemaType -> String
- Data.Aeson.Schema.Type: NameRef :: String -> NameLike
- Data.Aeson.Schema.Type: NameTH :: Name -> NameLike
- Data.Aeson.Schema.Type: data NameLike
- Data.Aeson.Schema.Type: instance (Language.Haskell.TH.Syntax.Lift s, Language.Haskell.TH.Syntax.Lift ty) => Language.Haskell.TH.Syntax.Lift (Data.Aeson.Schema.Type.Schema' s ty)
- Data.Aeson.Schema.Type: instance (Language.Haskell.TH.Syntax.Lift ty, Language.Haskell.TH.Syntax.Lift s) => Language.Haskell.TH.Syntax.Lift (Data.Aeson.Schema.Type.SchemaType' s ty)
- Data.Aeson.Schema.Type: instance GHC.Classes.Eq Data.Aeson.Schema.Type.NameLike
- Data.Aeson.Schema.Type: instance GHC.Show.Show Data.Aeson.Schema.Type.NameLike
+ Data.Aeson.Schema.Internal: instance Data.Aeson.Schema.Internal.IsSchema schema => Data.Aeson.Schema.Internal.HasSchemaResult ('Data.Aeson.Schema.Type.SchemaInclude ('Data.Either.Right schema))
+ Data.Aeson.Schema.Type: SchemaInclude :: Either ty (Schema' s ty) -> SchemaType' s ty
+ Data.Aeson.Schema.Type: instance Data.Aeson.Schema.Type.IsSchemaObjectMap (Data.Aeson.Schema.Type.FromSchema schema) => Data.Aeson.Schema.Type.IsSchemaType ('Data.Aeson.Schema.Type.SchemaInclude ('Data.Either.Right schema))
+ Data.Aeson.Schema.Type: toSchemaV :: forall schema. IsSchemaObjectMap (FromSchema schema) => Proxy schema -> SchemaV
+ Data.Aeson.Schema.Utils.NameLike: NameRef :: String -> NameLike
+ Data.Aeson.Schema.Utils.NameLike: NameTH :: Name -> NameLike
+ Data.Aeson.Schema.Utils.NameLike: data NameLike
+ Data.Aeson.Schema.Utils.NameLike: fromName :: NameLike -> String
+ Data.Aeson.Schema.Utils.NameLike: instance GHC.Classes.Eq Data.Aeson.Schema.Utils.NameLike.NameLike
+ Data.Aeson.Schema.Utils.NameLike: instance GHC.Show.Show Data.Aeson.Schema.Utils.NameLike.NameLike
+ Data.Aeson.Schema.Utils.NameLike: resolveName :: NameLike -> Q Name
- Data.Aeson.Schema: type IsSchema (schema :: Schema) = (HasSchemaResult (ToSchemaObject schema), All HasSchemaResultPair (FromSchema schema), SchemaResult (ToSchemaObject schema) ~ Object schema)
+ Data.Aeson.Schema: type IsSchema (schema :: Schema) = (HasSchemaResult (ToSchemaObject schema), All HasSchemaResultPair (FromSchema schema), IsSchemaObjectMap (FromSchema schema), SchemaResult (ToSchemaObject schema) ~ Object schema)
- Data.Aeson.Schema.Internal: type IsSchema (schema :: Schema) = (HasSchemaResult (ToSchemaObject schema), All HasSchemaResultPair (FromSchema schema), SchemaResult (ToSchemaObject schema) ~ Object schema)
+ Data.Aeson.Schema.Internal: type IsSchema (schema :: Schema) = (HasSchemaResult (ToSchemaObject schema), All HasSchemaResultPair (FromSchema schema), IsSchemaObjectMap (FromSchema schema), SchemaResult (ToSchemaObject schema) ~ Object schema)

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ ## Upcoming +## 1.3.2++Performance:++* Optimized including other schemas in a schema, which previously caused a huge slowdown, and possibly even out-of-memory errors.+ ## 1.3.1  Bug fixes:
aeson-schemas.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 82900f71614e4d0f5e4144719819585f4e34452136bb4bd5c4f9a7fd4f1b495c+-- hash: f878fea8c59bc8e0ba2ad6e3624a633b30cde5f6dba00bc63ed4c4777ac808f7  name:           aeson-schemas-version:        1.3.1+version:        1.3.2 synopsis:       Easily consume JSON data on-demand with type-safety description:    Parse JSON data easily and safely without defining new data types. Useful                 for deeply nested JSON data, which is difficult to parse using the default@@ -58,11 +58,11 @@       Data.Aeson.Schema       Data.Aeson.Schema.Internal       Data.Aeson.Schema.Key-      Data.Aeson.Schema.Show       Data.Aeson.Schema.TH       Data.Aeson.Schema.Type       Data.Aeson.Schema.Utils.All       Data.Aeson.Schema.Utils.Invariant+      Data.Aeson.Schema.Utils.NameLike       Data.Aeson.Schema.Utils.Sum   other-modules:       Data.Aeson.Schema.TH.Enum
src/Data/Aeson/Schema/Internal.hs view
@@ -67,6 +67,8 @@     , SchemaType'(..)     , ToSchemaObject     , showSchemaTypeV+    , showSchemaV+    , toSchemaV     ) import Data.Aeson.Schema.Utils.All (All(..)) import Data.Aeson.Schema.Utils.Invariant (unreachable)@@ -111,6 +113,7 @@ type IsSchema (schema :: Schema) =   ( HasSchemaResult (ToSchemaObject schema)   , All HasSchemaResultPair (FromSchema schema)+  , IsSchemaObjectMap (FromSchema schema)   , SchemaResult (ToSchemaObject schema) ~ Object schema   ) @@ -122,7 +125,9 @@ -- > showSchema @MySchema -- showSchema :: forall (schema :: Schema). IsSchema schema => String-showSchema = showSchemaType @(ToSchemaObject schema)+showSchema = "SchemaObject " ++ showSchemaV schema -- TODO: Remove "SchemaObject" prefix? Or rename to "Schema"?+  where+    schema = toSchemaV $ Proxy @schema  showSchemaType :: forall (schemaType :: SchemaType). HasSchemaResult schemaType => String showSchemaType = showSchemaTypeV schemaType@@ -139,6 +144,7 @@   SchemaResult ('SchemaList inner) = [SchemaResult inner]   SchemaResult ('SchemaUnion schemas) = SumType (SchemaResultList schemas)   SchemaResult ('SchemaObject inner) = Object ('Schema inner)+  SchemaResult ('SchemaInclude ('Right schema)) = SchemaResult (ToSchemaObject schema)  type family SchemaResultList (xs :: [SchemaType]) where   SchemaResultList '[] = '[]@@ -249,6 +255,11 @@   showValuePair _ o = (showSchemaKey @key, showValue @inner val)     where       val = unsafeGetKey @inner (Proxy @(FromSchemaKey key)) o++instance IsSchema schema => HasSchemaResult ('SchemaInclude ('Right schema)) where+  parseValue = parseValue @(ToSchemaObject schema)+  toValue = toValue @(ToSchemaObject schema)+  showValue = showValue @(ToSchemaObject schema)  -- | A helper for creating fail messages when parsing a schema. parseFail :: forall (schema :: SchemaType) m a. (MonadFail m, HasSchemaResult schema) => [Text] -> Value -> m a
− src/Data/Aeson/Schema/Show.hs
@@ -1,49 +0,0 @@-{-|-Module      :  Data.Aeson.Schema.Show-Maintainer  :  Brandon Chinn <brandon@leapyear.io>-Stability   :  experimental-Portability :  portable--Utilities for showing a schema. Meant to be imported qualified.--}-{-# LANGUAGE LambdaCase #-}--module Data.Aeson.Schema.Show-  ( SchemaType(..)-  , showSchemaType-  ) where--import Data.List (intercalate)--import Data.Aeson.Schema.Key (SchemaKeyV, showSchemaKeyV)---- | 'Data.Aeson.Schema.Internal.SchemaType', but for printing.-data SchemaType-  = SchemaScalar String-  | SchemaMaybe SchemaType-  | SchemaTry SchemaType-  | SchemaList SchemaType-  | SchemaObject [(SchemaKeyV, SchemaType)]-  | SchemaUnion [SchemaType]-  deriving (Show,Eq)---- | Pretty show the given SchemaType.-showSchemaType :: SchemaType -> String-showSchemaType schema = case schema of-  SchemaScalar s -> "SchemaScalar " ++ s-  SchemaMaybe inner -> "SchemaMaybe " ++ showSchemaType' inner-  SchemaTry inner -> "SchemaTry " ++ showSchemaType' inner-  SchemaList inner -> "SchemaList " ++ showSchemaType' inner-  SchemaUnion _ -> "SchemaUnion " ++ showSchemaType' schema-  SchemaObject _ -> "SchemaObject " ++ showSchemaType' schema-  where-    showSchemaType' = \case-      SchemaScalar s -> s-      SchemaMaybe inner -> "Maybe " ++ showSchemaType' inner-      SchemaTry inner -> "Try " ++ showSchemaType' inner-      SchemaList inner -> "List " ++ showSchemaType' inner-      SchemaUnion schemas -> "( " ++ mapJoin showSchemaType' " | " schemas ++ " )"-      SchemaObject pairs -> "{ " ++ mapJoin showPair ", " pairs ++ " }"-    showPair (key, inner) = showSchemaKeyV key ++ ": " ++ showSchemaType' inner--    mapJoin f delim = intercalate delim . map f
src/Data/Aeson/Schema/TH/Getter.hs view
@@ -20,7 +20,8 @@ import Data.Aeson.Schema.TH.Parse (GetterExp(..), parseGetterExp) import Data.Aeson.Schema.TH.Unwrap     (FunctorHandler(..), unwrapSchema, unwrapSchemaUsing)-import Data.Aeson.Schema.TH.Utils (reifySchemaName, schemaVToTypeQ)+import Data.Aeson.Schema.TH.Utils (loadSchema, lookupSchema, schemaVToTypeQ)+import Data.Aeson.Schema.Utils.NameLike (NameLike(..))  -- | A helper that generates a 'Data.Aeson.Schema.TH.get' expression and a type alias for the result -- of the expression.@@ -80,7 +81,7 @@   unless (isNothing start) $     fail $ "Getter expression should start with '.': " ++ ops -  startSchema <- reifySchemaName startSchemaName+  startSchema <- lookupSchema (NameTH startSchemaName) >>= loadSchema    let unwrapResult = unwrapSchema getterOps startSchema       funcResult = unwrapSchemaUsing ApplyFunctors getterOps startSchema
src/Data/Aeson/Schema/TH/Schema.hs view
@@ -30,16 +30,15 @@     (SchemaDef(..), SchemaDefObjItem(..), SchemaDefObjKey(..), parseSchemaDef) import Data.Aeson.Schema.TH.Utils (reifySchema, schemaVToTypeQ) import Data.Aeson.Schema.Type-    ( NameLike(..)-    , Schema'(..)+    ( Schema'(..)     , SchemaObjectMapV     , SchemaType'(..)     , SchemaTypeV     , fromSchemaV     , showSchemaTypeV-    , toSchemaObjectV     ) import Data.Aeson.Schema.Utils.Invariant (unreachable)+import Data.Aeson.Schema.Utils.NameLike (NameLike(..))  -- | Defines a QuasiQuoter for writing schemas. --@@ -147,6 +146,7 @@       SchemaTry _ -> True -- even if inner is a non-object schema, it'll still parse to be Nothing       SchemaUnion schemas -> any isValidPhantomSchema schemas       SchemaObject _ -> True+      SchemaInclude _ -> True       _ -> False  -- | Resolve the given keys with the following rules:@@ -181,7 +181,7 @@   SchemaDefMaybe inner   -> SchemaMaybe <$> fromSchemaDefType inner   SchemaDefTry inner     -> SchemaTry <$> fromSchemaDefType inner   SchemaDefList inner    -> SchemaList <$> fromSchemaDefType inner-  SchemaDefInclude other -> toSchemaObjectV <$> reifySchema other+  SchemaDefInclude other -> return $ SchemaInclude $ Left $ NameRef other   SchemaDefUnion schemas -> SchemaUnion . NonEmpty.toList <$> mapM fromSchemaDefType schemas   SchemaDefObj items     -> SchemaObject <$> generateSchemaObjectV items 
src/Data/Aeson/Schema/TH/Unwrap.hs view
@@ -12,7 +12,7 @@  module Data.Aeson.Schema.TH.Unwrap where -import Control.Monad ((>=>))+import Control.Monad ((<=<), (>=>)) import Data.Bifunctor (first) import qualified Data.List.NonEmpty as NonEmpty import Language.Haskell.TH@@ -23,7 +23,7 @@ import Data.Aeson.Schema.TH.Parse     (GetterOperation(..), GetterOps, UnwrapSchema(..), parseUnwrapSchema) import Data.Aeson.Schema.TH.Utils-    (reifySchema, schemaTypeVToTypeQ, schemaVToTypeQ)+    (reifySchema, resolveSchemaType, schemaTypeVToTypeQ, schemaVToTypeQ) import Data.Aeson.Schema.Type     ( Schema'(..)     , SchemaType'(..)@@ -81,7 +81,7 @@  -- | Unwrap the given schema by applying the given operations, using the given 'FunctorHandler'. unwrapSchemaUsing :: FunctorHandler -> GetterOps -> SchemaV -> TypeQ-unwrapSchemaUsing functorHandler getterOps = either fail toResultTypeQ . flip go (NonEmpty.toList getterOps) . toSchemaObjectV+unwrapSchemaUsing functorHandler getterOps = toResultTypeQ <=< flip go (NonEmpty.toList getterOps) . toSchemaObjectV   where     toResultTypeQ :: UnwrapSchemaResult -> TypeQ     toResultTypeQ = \case@@ -97,13 +97,16 @@                 StripFunctors -> ty         in handleFunctor <$> toResultTypeQ schemaResult -    go :: SchemaTypeV -> [GetterOperation] -> Either String UnwrapSchemaResult+    go :: SchemaTypeV -> [GetterOperation] -> Q UnwrapSchemaResult     go schemaType [] = pure $ SchemaResult schemaType-    go schemaType (op:ops) =-      let invalid message = Left $ message ++ ": " ++ showSchemaTypeV schemaType+    go schemaType' (op:ops) = do+      schemaType <- resolveSchemaType schemaType'++      let invalid message = fail $ message ++ ": " ++ showSchemaTypeV schemaType           wrapMaybe = SchemaResultWrapped (ConT ''Maybe)           wrapList = SchemaResultWrapped ListT-      in case op of++      case op of         GetterKey key ->           case schemaType of             SchemaObject pairs ->
src/Data/Aeson/Schema/TH/Utils.hs view
@@ -8,42 +8,60 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ViewPatterns #-}  module Data.Aeson.Schema.TH.Utils   ( reifySchema-  , reifySchemaName+  , lookupSchema+  , loadSchema+  , resolveSchemaType   , schemaVToTypeQ   , schemaTypeVToTypeQ   ) where -import Control.Monad (forM, (>=>))+import Control.Applicative (empty)+import Control.Monad (forM) import Data.Bifunctor (bimap)-import Data.Text (Text) import Language.Haskell.TH  import Data.Aeson.Schema.Internal (Object) import Data.Aeson.Schema.Key (SchemaKey'(..), SchemaKeyV) import Data.Aeson.Schema.Type-    ( NameLike(..)-    , Schema'(..)+    ( Schema'(..)     , SchemaObjectMapV     , SchemaType'(..)     , SchemaTypeV     , SchemaV     , fromSchemaV+    , toSchemaObjectV     )+import Data.Aeson.Schema.Utils.Invariant (unreachable)+import Data.Aeson.Schema.Utils.NameLike (NameLike(..), resolveName) +{- Loading schema from TH -}+ reifySchema :: String -> Q SchemaV-reifySchema name = lookupTypeName name >>= maybe unknownSchemaErr reifySchemaName-  where-    unknownSchemaErr = fail $ "Unknown schema: " ++ name+reifySchema name = lookupSchema (NameRef name) >>= loadSchema -reifySchemaName :: Name -> Q SchemaV-reifySchemaName = reifySchemaType >=> parseSchema+data ReifiedSchema = ReifiedSchema+  { reifiedSchemaName :: Name+  , reifiedSchemaType :: TypeWithoutKinds+  }++-- | Look up a schema with the given name. Errors if the name doesn't exist or if the name does+-- not refer to a schema.+lookupSchema :: NameLike -> Q ReifiedSchema+lookupSchema nameLike = do+  name <- lookupSchemaName nameLike+  ReifiedSchema name <$> reifySchemaType name   where+    lookupSchemaName = \case+      NameRef name -> lookupTypeName name >>= maybe (fail $ "Unknown schema: " ++ name) return+      NameTH name -> return name+     reifySchemaType :: Name -> Q TypeWithoutKinds     reifySchemaType schemaName = reify schemaName >>= \case       TyConI (TySynD _ _ (stripKinds -> ty))@@ -74,33 +92,41 @@       AppT (PromotedT name) _ | name == 'Schema -> True       _ -> False -    parseSchema :: TypeWithoutKinds -> Q SchemaV-    parseSchema ty = maybe (fail $ "Could not parse schema: " ++ show ty) return $ do+loadSchema :: ReifiedSchema -> Q SchemaV+loadSchema ReifiedSchema{reifiedSchemaType} =+  maybe (fail $ "Could not parse schema: " ++ show reifiedSchemaType) return $ parseSchema reifiedSchemaType+  where+    -- should be the inverse of schemaVToTypeQ+    parseSchema :: TypeWithoutKinds -> Maybe SchemaV+    parseSchema ty = do       schemaObjectType <- case ty of-        AppT (PromotedT name) schemaType | name == 'Schema -> Just schemaType-        _ -> Nothing+        AppT (PromotedT name) schemaType | name == 'Schema -> return schemaType+        _ -> empty -      Schema <$> parseSchemaObjectType schemaObjectType+      Schema <$> parseSchemaObjectMap schemaObjectType -    parseSchemaObjectType :: TypeWithoutKinds -> Maybe SchemaObjectMapV-    parseSchemaObjectType schemaObjectType = do+    -- should be the inverse of schemaObjectMapVToTypeQ+    parseSchemaObjectMap :: TypeWithoutKinds -> Maybe SchemaObjectMapV+    parseSchemaObjectMap schemaObjectType = do       schemaObjectListOfPairs <- mapM typeToPair =<< typeToList schemaObjectType       forM schemaObjectListOfPairs $ \(schemaKeyType, schemaTypeType) -> do         schemaKey <- parseSchemaKey schemaKeyType         schemaType <- parseSchemaType schemaTypeType-        Just (schemaKey, schemaType)+        return (schemaKey, schemaType) +    -- should be the inverse of schemaKeyVToTypeQ     parseSchemaKey :: TypeWithoutKinds -> Maybe SchemaKeyV     parseSchemaKey = \case       AppT (PromotedT ty) (LitT (StrTyLit key))-        | ty == 'NormalKey -> Just $ NormalKey key-        | ty == 'PhantomKey -> Just $ PhantomKey key-      _ -> Nothing+        | ty == 'NormalKey -> return $ NormalKey key+        | ty == 'PhantomKey -> return $ PhantomKey key+      _ -> empty +    -- should be the inverse of schemaTypeVToTypeQ     parseSchemaType :: TypeWithoutKinds -> Maybe SchemaTypeV     parseSchemaType = \case       AppT (PromotedT name) (ConT inner)-        | name == 'SchemaScalar -> Just $ SchemaScalar $ NameTH inner+        | name == 'SchemaScalar -> return $ SchemaScalar $ NameTH inner        AppT (PromotedT name) inner         | name == 'SchemaMaybe  -> SchemaMaybe <$> parseSchemaType inner@@ -113,10 +139,24 @@             schemas <- typeToList inner             SchemaUnion <$> mapM parseSchemaType schemas -        | name == 'SchemaObject -> SchemaObject <$> parseSchemaObjectType inner+        | name == 'SchemaObject -> SchemaObject <$> parseSchemaObjectMap inner -      _ -> Nothing+      AppT (PromotedT name) (AppT (PromotedT right) (ConT inner))+        | name == 'SchemaInclude+        , right == 'Right+        -> return $ SchemaInclude $ Left $ NameTH inner +      _ -> empty++-- | Resolve SchemaInclude, if present. (Not recursive)+resolveSchemaType :: SchemaTypeV -> Q SchemaTypeV+resolveSchemaType = \case+  SchemaInclude (Left name) -> fmap toSchemaObjectV . loadSchema =<< lookupSchema name+  SchemaInclude (Right _) -> unreachable "Found 'SchemaInclude Right' when resolving schema type"+  schemaType -> pure schemaType++{- Splicing schema into TH -}+ schemaVToTypeQ :: SchemaV -> TypeQ schemaVToTypeQ = appT [t| 'Schema |] . schemaObjectMapVToTypeQ . fromSchemaV @@ -139,18 +179,8 @@   SchemaList inner    -> [t| 'SchemaList $(schemaTypeVToTypeQ inner) |]   SchemaUnion schemas -> [t| 'SchemaUnion $(promotedListT $ map schemaTypeVToTypeQ schemas) |]   SchemaObject pairs  -> [t| 'SchemaObject $(schemaObjectMapVToTypeQ pairs) |]--resolveName :: NameLike -> Q Name-resolveName = \case-  -- some hardcoded cases-  NameRef "Bool"   -> pure ''Bool-  NameRef "Int"    -> pure ''Int-  NameRef "Double" -> pure ''Double-  NameRef "Text"   -> pure ''Text--  -- general cases-  NameRef name     -> lookupTypeName name >>= maybe (fail $ "Unknown type: " ++ name) pure-  NameTH name      -> pure name+  SchemaInclude (Left name) -> [t| 'SchemaInclude ('Right $(conT . reifiedSchemaName =<< lookupSchema name)) |]+  SchemaInclude (Right _)   -> unreachable "Found 'SchemaInclude Right' when converting to TypeQ"  {- TH utilities -} 
src/Data/Aeson/Schema/Type.hs view
@@ -7,13 +7,14 @@ Defines SchemaType, the AST that defines a JSON schema. -} {-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DeriveLift #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeInType #-}+{-# LANGUAGE UndecidableInstances #-}  module Data.Aeson.Schema.Type   ( Schema'(..)@@ -21,7 +22,6 @@   , SchemaV   , SchemaTypeV   , SchemaObjectMapV-  , NameLike(..)   , toSchemaObjectV   , fromSchemaV   , showSchemaV@@ -32,6 +32,7 @@   , FromSchema   , IsSchemaType(..)   , IsSchemaObjectMap+  , toSchemaV   ) where  import Data.Kind (Type)@@ -39,15 +40,16 @@ import Data.Proxy (Proxy(..)) import Data.Typeable (Typeable, tyConName, typeRep, typeRepTyCon) import GHC.TypeLits (Symbol)-import Language.Haskell.TH.Syntax (Lift, Name, nameBase)  import Data.Aeson.Schema.Key     (IsSchemaKey(..), SchemaKey, SchemaKey', SchemaKeyV, showSchemaKeyV) import Data.Aeson.Schema.Utils.All (All(..))+import Data.Aeson.Schema.Utils.Invariant (unreachable)+import Data.Aeson.Schema.Utils.NameLike (NameLike(..), fromName)  -- | The schema definition for a JSON object. data Schema' s ty = Schema (SchemaObjectMap' s ty)-  deriving (Show, Eq, Lift)+  deriving (Show, Eq)  -- | The AST defining a JSON schema. data SchemaType' s ty@@ -57,19 +59,18 @@   | SchemaList (SchemaType' s ty)   | SchemaUnion [SchemaType' s ty] -- ^ @since v1.1.0   | SchemaObject (SchemaObjectMap' s ty)-  deriving (Show, Eq, Lift)+  | SchemaInclude (Either ty (Schema' s ty))+    -- ^ An optimization for including schemas.+    --+    -- Will always be 'Left' when used in a value-level schema and 'Right' when used in+    -- a type-level schema. We can't use a type parameter for this because type synonyms+    -- can't be recursive (e.g. `type Schema = Schema' Symbol Type Schema`).+    --+    -- @since v1.3.2+  deriving (Show, Eq)  type SchemaObjectMap' s ty = [(SchemaKey' s, SchemaType' s ty)] -data NameLike = NameRef String | NameTH Name--instance Eq NameLike where-  ty1 == ty2 = show ty1 == show ty2--instance Show NameLike where-  show (NameRef ty) = ty-  show (NameTH ty) = nameBase ty- {- Value-level schema types -}  type SchemaV = Schema' String NameLike@@ -95,15 +96,18 @@   SchemaList inner -> "SchemaList " ++ showSchemaTypeV' inner   SchemaUnion _ -> "SchemaUnion " ++ showSchemaTypeV' schema   SchemaObject _ -> "SchemaObject " ++ showSchemaTypeV' schema+  SchemaInclude _ -> "SchemaInclude " ++ showSchemaTypeV' schema  showSchemaTypeV' :: SchemaTypeV -> String showSchemaTypeV' = \case-  SchemaScalar ty -> show ty+  SchemaScalar ty -> fromName ty   SchemaMaybe inner -> "Maybe " ++ showSchemaTypeV' inner   SchemaTry inner -> "Try " ++ showSchemaTypeV' inner   SchemaList inner -> "List " ++ showSchemaTypeV' inner   SchemaUnion schemas -> "( " ++ mapJoin showSchemaTypeV' " | " schemas ++ " )"   SchemaObject pairs -> "{ " ++ mapJoin showPair ", " pairs ++ " }"+  SchemaInclude (Left name) -> fromName name+  SchemaInclude (Right _) -> unreachable "Found 'SchemaInclude Right' when showing schema type"   where     showPair (key, inner) = showSchemaKeyV key ++ ": " ++ showSchemaTypeV' inner @@ -129,6 +133,12 @@ type family FromSchema (schema :: Schema) :: SchemaObjectMap where   FromSchema ('Schema schema) = schema +toSchemaV :: forall schema. IsSchemaObjectMap (FromSchema schema) => Proxy schema -> SchemaV+toSchemaV _ = Schema $ toSchemaTypeMapV $ Proxy @(FromSchema schema)++toSchemaTypeMapV :: forall pairs. IsSchemaObjectMap pairs => Proxy pairs -> SchemaObjectMapV+toSchemaTypeMapV _ = mapAll @IsSchemaObjectPair @pairs toSchemaTypePairV+ class IsSchemaType (schemaType :: SchemaType) where   toSchemaTypeV :: Proxy schemaType -> SchemaTypeV @@ -148,7 +158,10 @@   toSchemaTypeV _ = SchemaUnion (mapAll @IsSchemaType @schemas toSchemaTypeV)  instance IsSchemaObjectMap pairs => IsSchemaType ('SchemaObject pairs) where-  toSchemaTypeV _ = SchemaObject (mapAll @IsSchemaObjectPair @pairs toSchemaTypePairV)+  toSchemaTypeV _ = SchemaObject (toSchemaTypeMapV $ Proxy @pairs)++instance IsSchemaObjectMap (FromSchema schema) => IsSchemaType ('SchemaInclude ('Right schema)) where+  toSchemaTypeV _ = toSchemaObjectV $ toSchemaV $ Proxy @schema  type IsSchemaObjectMap (pairs :: SchemaObjectMap) = All IsSchemaObjectPair pairs 
+ src/Data/Aeson/Schema/Utils/NameLike.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TemplateHaskell #-}++module Data.Aeson.Schema.Utils.NameLike+  ( NameLike(..)+  , fromName+  , resolveName+  ) where++import Data.Text (Text)+import Language.Haskell.TH.Syntax (Name, Q, lookupTypeName, nameBase)++data NameLike = NameRef String | NameTH Name++instance Eq NameLike where+  ty1 == ty2 = fromName ty1 == fromName ty2++instance Show NameLike where+  show = show . fromName++fromName :: NameLike -> String+fromName = \case+  NameRef s -> s+  NameTH name -> nameBase name++resolveName :: NameLike -> Q Name+resolveName = \case+  -- some hardcoded cases+  NameRef "Bool"   -> pure ''Bool+  NameRef "Int"    -> pure ''Int+  NameRef "Double" -> pure ''Double+  NameRef "Text"   -> pure ''Text++  -- general cases+  NameRef name     -> lookupTypeName name >>= maybe (fail $ "Unknown type: " ++ name) pure+  NameTH name      -> pure name
test/TestUtils/Arbitrary.hs view
@@ -43,16 +43,17 @@ import Data.Aeson.Schema.Key     (IsSchemaKey(..), SchemaKey, SchemaKey'(..), SchemaKeyV, toContext) import Data.Aeson.Schema.Type-    ( NameLike(..)-    , Schema'(..)+    ( Schema'(..)     , SchemaObjectMapV     , SchemaType     , SchemaType'(..)+    , SchemaTypeV     , SchemaV     , showSchemaV     , toSchemaObjectV     ) import Data.Aeson.Schema.Utils.All (All(..))+import Data.Aeson.Schema.Utils.NameLike (NameLike(..), fromName)  data ArbitraryObject where   ArbitraryObject@@ -104,6 +105,8 @@ {- Run time helpers -}  deriving instance Lift NameLike+deriving instance Lift SchemaV+deriving instance Lift SchemaTypeV  genSchema' :: forall schema.   ( ArbitrarySchema ('SchemaObject schema)@@ -134,12 +137,13 @@ getSchemaTypes = getSchemaTypes' . toSchemaObjectV   where     getSchemaTypes' = \case-      SchemaScalar name -> [show name]+      SchemaScalar name -> [fromName name]       SchemaMaybe inner -> "SchemaMaybe" : getSchemaTypes' inner       SchemaTry inner -> "SchemaTry" : getSchemaTypes' inner       SchemaList inner -> "SchemaList" : getSchemaTypes' inner       SchemaUnion schemas -> "SchemaUnion" : concatMap getSchemaTypes' schemas       SchemaObject pairs -> "SchemaObject" : concatMap (getSchemaTypes' . snd) pairs+      SchemaInclude _ -> error "ArbitraryObject unexpectedly generated a schema that includes another schema"  getObjectSizes :: SchemaV -> [Int] getObjectSizes = getObjectSizes' . toSchemaObjectV@@ -151,6 +155,7 @@       SchemaList inner -> getObjectSizes' inner       SchemaUnion schemas -> concatMap getObjectSizes' schemas       SchemaObject pairs -> length pairs : concatMap (getObjectSizes' . snd) pairs+      SchemaInclude _ -> error "ArbitraryObject unexpectedly generated a schema that includes another schema"  getObjectDepth :: SchemaV -> Int getObjectDepth = getObjectDepth' . toSchemaObjectV@@ -162,6 +167,7 @@       SchemaList inner -> getObjectDepth' inner       SchemaUnion schemas -> maximum $ map getObjectDepth' schemas       SchemaObject pairs -> 1 + maximum (map (getObjectDepth' . snd) pairs)+      SchemaInclude _ -> error "ArbitraryObject unexpectedly generated a schema that includes another schema"  tabulate' :: String -> [String] -> Property -> Property #if MIN_VERSION_QuickCheck(2,12,0)
test/Tests/SchemaQQ.hs view
@@ -86,6 +86,11 @@         [schemaRep| { a: #SchemaWithHiddenImport } |]         [r| SchemaObject { "a": { "a": CBool } } |] +  , testCase "Object with an imported schema that itself imports a schema" $+      assertMatches+        [schemaRep| { a: #WithUser } |]+        [r| SchemaObject { "a": { "user": { "name": Text } } } |]+   , testCase "Object with an extended schema" $       assertMatches         [schemaRep| { a: Int, #ExtraSchema } |]
test/Tests/SchemaQQ/TH.hs view
@@ -15,9 +15,7 @@ import Language.Haskell.TH.TestUtils     (MockedMode(..), QMode(..), QState(..), loadNames, runTestQ, runTestQErr) -import Data.Aeson.Schema (schema)-import Data.Aeson.Schema.Internal (showSchemaType)-import Data.Aeson.Schema.Type (ToSchemaObject)+import Data.Aeson.Schema (schema, showSchema) import TestUtils (mkExpQQ) import TestUtils.DeepSeq () @@ -36,6 +34,11 @@ -- Compile above types before reifying $(return []) +type WithUser = [schema| { user: #UserSchema } |]++-- Compile above types before reifying+$(return [])+ qState :: QState 'FullyMocked qState = QState   { mode = MockQ@@ -47,6 +50,7 @@       , ("Tests.SchemaQQ.TH.UserSchema", ''UserSchema)       , ("Tests.SchemaQQ.TH.ExtraSchema", ''ExtraSchema)       , ("SchemaWithHiddenImport", ''SchemaWithHiddenImport)+      , ("WithUser", ''WithUser)       , ("Int", ''Int)       ]   , reifyInfo = $(loadNames@@ -54,6 +58,7 @@       , ''ExtraSchema       , ''ExtraSchema2       , ''SchemaWithHiddenImport+      , ''WithUser       , ''Int       ]     )@@ -65,7 +70,7 @@ schemaRep :: QuasiQuoter schemaRep = mkExpQQ $ \s ->   let schemaType = quoteType schema s-  in [| runTestQ qState (quoteType schema s) `deepseq` showSchemaType @(ToSchemaObject $schemaType) |]+  in [| runTestQ qState (quoteType schema s) `deepseq` showSchema @ $schemaType |]  schemaErr :: QuasiQuoter schemaErr = mkExpQQ $ \s -> [| runTestQErr qState (quoteType schema s) |]