Thread the path through the JSON Schema->IR transformer

We shall also need this later.
This commit is contained in:
Mark Probst 2017-09-06 07:12:05 -07:00
Родитель 0de63df347
Коммит f1dfe826fe
2 изменённых файлов: 58 добавлений и 21 удалений

Просмотреть файл

@ -31,7 +31,7 @@ import Data.String.Util (camelCase, capitalize, singular)
import Data.Traversable (class Traversable, traverse)
import Data.Tuple (Tuple(..))
import IR (IR, addClass, unifyTypes)
import Utils (mapM, mapMapM)
import Utils (mapM, mapMapM, mapWithIndexM)
data JSONType
= JSONObject
@ -55,6 +55,10 @@ jsonTypeEnumMap = SM.fromFoldable [
data PathElement
= Root
| Definition String
| OneOf Int
| Property String
| AdditionalProperty
| Items
newtype JSONSchemaRef = JSONSchemaRef
{ path :: NEL.NonEmptyList PathElement
@ -147,55 +151,82 @@ instance decodeJsonSchema :: DecodeJson JSONSchema where
title <- obj .?? "title"
pure $ JSONSchema { definitions, ref, types, oneOf, properties, additionalProperties, items, required, title }
lookupRef :: JSONSchema -> List PathElement -> JSONSchema -> Either Error JSONSchema
lookupRef root ref local@(JSONSchema { definitions }) =
lookupRef :: JSONSchema -> List PathElement -> List PathElement -> JSONSchema -> Either Error (Tuple JSONSchema (List PathElement))
-- FIXME: don't deconstruct all the properties
lookupRef root reversePath ref local@(JSONSchema { definitions, oneOf, properties, additionalProperties, items }) =
case ref of
L.Nil -> Right local
Root : rest -> lookupRef root rest root
L.Nil -> Right $ Tuple local reversePath
Root : rest -> lookupRef root (L.singleton Root) rest root
Definition name : rest ->
-- FIXME: use >>= for all these maybes
case definitions of
Just sm ->
case SM.lookup name sm of
Just js -> lookupRef root rest js
Just js -> recur (Definition name) rest js
Nothing -> Left "Reference not found"
Nothing -> Left "Definitions not found"
OneOf i : rest ->
case oneOf of
Just oo ->
case A.index oo i of
Just js -> recur (OneOf i) rest js
Nothing -> Left "Invalid OneOf index"
Nothing -> Left "OneOf not found"
Property name : rest ->
case properties of
Just props ->
case SM.lookup name props of
Just js -> recur (Property name) rest js
Nothing -> Left "Property not found"
Nothing -> Left "Properties not found"
AdditionalProperty : rest ->
case additionalProperties of
Right js -> recur AdditionalProperty rest js
_ -> Left "AdditionalProperties not found"
Items : rest ->
case items of
Just js -> recur Items rest js
Nothing -> Left "Items not found"
where
recur pathElement rest js = lookupRef root (pathElement : reversePath) rest js
jsonUnifyTypes :: IRType -> IRType -> JsonIR IRType
jsonUnifyTypes a b = StateT.lift $ unifyTypes a b
toIRAndUnify :: forall a f. Foldable f => (a -> JsonIR IRType) -> f a -> JsonIR IRType
toIRAndUnify :: forall a f. Foldable f => (Int -> a -> JsonIR IRType) -> f a -> JsonIR IRType
toIRAndUnify toIR l = do
irs <- mapM toIR $ L.fromFoldable l
irs <- mapWithIndexM toIR $ A.fromFoldable l
foldM jsonUnifyTypes IRAnything irs
jsonSchemaToIR :: JSONSchema -> Named String -> JSONSchema -> JsonIR IRType
jsonSchemaToIR root name schema@(JSONSchema { definitions, ref, types, oneOf, properties, additionalProperties, items, required })
jsonSchemaToIR :: JSONSchema -> List PathElement -> Named String -> JSONSchema -> JsonIR IRType
-- FIXME: don't deconstruct all the properties
jsonSchemaToIR root reversePath name schema@(JSONSchema { definitions, ref, types, oneOf, properties, additionalProperties, items, required })
| Just (JSONSchemaRef { path, name }) <- ref =
case lookupRef root (NEL.toList path) schema of
case lookupRef root reversePath (NEL.toList path) schema of
Left err -> throwError err
Right js -> jsonSchemaToIR root (maybe (Inferred "Something") Given name) js
Right (Tuple js jsReversePath) -> jsonSchemaToIR root jsReversePath (maybe (Inferred "Something") Given name) js
| Just (Left jt) <- types =
jsonTypeToIR root name jt schema
jsonTypeToIR root reversePath name jt schema
| Just (Right jts) <- types =
toIRAndUnify (\jt -> jsonTypeToIR root name jt schema) jts
toIRAndUnify (\_ jt -> jsonTypeToIR root reversePath name jt schema) jts
| Just jss <- oneOf =
toIRAndUnify (jsonSchemaToIR root name) jss
toIRAndUnify (\i -> jsonSchemaToIR root (OneOf i : reversePath) name) jss
| otherwise =
pure IRAnything
jsonSchemaListToIR :: forall t. Traversable t => Named String -> t JSONSchema -> IR IRType
jsonSchemaListToIR name l = do
irTypes <- StateT.evalStateT (mapM (\js -> jsonSchemaToIR js name js) l) unit
irTypes <- StateT.evalStateT (mapM (\js -> jsonSchemaToIR js (L.singleton Root) name js) l) unit
foldM unifyTypes IRAnything irTypes
jsonTypeToIR :: JSONSchema -> Named String -> JSONType -> JSONSchema -> JsonIR IRType
jsonTypeToIR root name jsonType (JSONSchema schema) =
jsonTypeToIR :: JSONSchema -> List PathElement -> Named String -> JSONType -> JSONSchema -> JsonIR IRType
jsonTypeToIR root reversePath name jsonType (JSONSchema schema) =
case jsonType of
JSONObject ->
case schema.properties of
Just sm -> do
let propMap = M.fromFoldable $ SM.toUnfoldable sm :: Array (Tuple String JSONSchema)
props <- mapMapM (\n -> jsonSchemaToIR root $ Inferred n) propMap
props <- mapMapM (\n -> jsonSchemaToIR root (Property n : reversePath) $ Inferred n) propMap
let required = maybe S.empty S.fromFoldable schema.required
let title = maybe name Given schema.title
nulled <- mapMapM (\n -> if S.member n required then pure else jsonUnifyTypes IRNull) props
@ -207,12 +238,12 @@ jsonTypeToIR root name jsonType (JSONSchema schema) =
Left false ->
pure $ IRAnything
Right js -> do
ir <- jsonSchemaToIR root singularName js
ir <- jsonSchemaToIR root (AdditionalProperty : reversePath) singularName js
pure $ IRMap ir
JSONArray ->
case schema.items of
Just js -> do
ir <- jsonSchemaToIR root singularName js
ir <- jsonSchemaToIR root (Items : reversePath) singularName js
pure $ IRArray ir
Nothing -> pure $ IRArray IRAnything
JSONBoolean -> pure IRBool

Просмотреть файл

@ -1,5 +1,6 @@
module Utils
( mapM
, mapWithIndexM
, mapMapM
, mapStrMapM
, mapMaybeM
@ -23,10 +24,15 @@ import Data.StrMap (StrMap)
import Data.StrMap as SM
import Data.Traversable (class Traversable, for_, traverse)
import Data.Tuple (Tuple(..))
import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex)
mapM :: forall m a b t. Applicative m => Traversable t => (a -> m b) -> t a -> m (t b)
mapM = traverse
mapWithIndexM :: forall m a b f i. Applicative m => FunctorWithIndex i f => Traversable f => (i -> a -> m b) -> f a -> m (f b)
mapWithIndexM f l =
mapM (\(Tuple i x) -> f i x) $ mapWithIndex Tuple l
forMapM :: forall a v k m. Monad m => Ord k => Map k v -> (k -> v -> m a) -> m (Map k a)
forMapM = flip mapMapM