Write JSON test module
parent
21dfa1e77f
commit
ecdc136f9e
|
@ -1,4 +1,11 @@
|
||||||
module Internal.Tools.Json exposing (..)
|
module Internal.Tools.Json exposing
|
||||||
|
( Coder, string, bool, int, float
|
||||||
|
, encode, decode
|
||||||
|
, Docs(..), RequiredField(..), toDocs
|
||||||
|
, list, slowDict, fastDict, maybe
|
||||||
|
, Field, field
|
||||||
|
, object2, object3, object4, object5, object6, object7, object8, object9, object10, object11
|
||||||
|
)
|
||||||
|
|
||||||
{-|
|
{-|
|
||||||
|
|
||||||
|
@ -21,6 +28,36 @@ data types. Because this module uses dynamic builder types, this also means it
|
||||||
is relatively easy to write documentation for any data type that uses this
|
is relatively easy to write documentation for any data type that uses this
|
||||||
module to build its encoders and decoders.
|
module to build its encoders and decoders.
|
||||||
|
|
||||||
|
@docs Coder, string, bool, int, float
|
||||||
|
|
||||||
|
|
||||||
|
## JSON Coding
|
||||||
|
|
||||||
|
@docs encode, decode
|
||||||
|
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
@docs Docs, RequiredField, toDocs
|
||||||
|
|
||||||
|
|
||||||
|
## Data types
|
||||||
|
|
||||||
|
@docs list, slowDict, fastDict, maybe
|
||||||
|
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
|
||||||
|
This section creates objects that can be (re)used in the library's JSON
|
||||||
|
specification. For this, the user needs to construct fields for the object
|
||||||
|
first.
|
||||||
|
|
||||||
|
@docs Field, field
|
||||||
|
|
||||||
|
Once all fields are constructed, the user can create JSON objects.
|
||||||
|
|
||||||
|
@docs object2, object3, object4, object5, object6, object7, object8, object9, object10, object11
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Dict as SlowDict
|
import Dict as SlowDict
|
||||||
|
@ -32,32 +69,55 @@ import Json.Decode as D
|
||||||
import Json.Encode as E
|
import Json.Encode as E
|
||||||
|
|
||||||
|
|
||||||
|
{-| A field of type `a` as a subtype of an object `object`.
|
||||||
|
|
||||||
|
In concrete terms, to construct a data type
|
||||||
|
|
||||||
|
type alias User =
|
||||||
|
{ name : String
|
||||||
|
, age : Int
|
||||||
|
, hobbies : List String
|
||||||
|
}
|
||||||
|
|
||||||
|
The user needs to construct the field types:
|
||||||
|
|
||||||
|
- `Field String User`,
|
||||||
|
- `Field Int User`,
|
||||||
|
- and `Field (List String) User`.
|
||||||
|
|
||||||
|
-}
|
||||||
type Field a object
|
type Field a object
|
||||||
= Field
|
= Field
|
||||||
{ fieldName : String
|
{ fieldName : String
|
||||||
, description : List String
|
, description : List String
|
||||||
, encoder : a -> Maybe E.Value
|
, encoder : a -> Maybe E.Value
|
||||||
, decoder : D.Decoder ( a, List Log )
|
, decoder : D.Decoder ( a, List Log )
|
||||||
, docs : JSONDocs
|
, docs : Docs
|
||||||
, toField : object -> a
|
, toField : object -> a
|
||||||
, requiredness : RequiredField
|
, requiredness : RequiredField
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type JSONCoder a
|
{-| Builder type that helps create JSON encoders, JSON decoders, data type
|
||||||
= JSONCoder
|
documentation and various other data types.
|
||||||
|
-}
|
||||||
|
type Coder a
|
||||||
|
= Coder
|
||||||
{ encoder : a -> E.Value
|
{ encoder : a -> E.Value
|
||||||
, decoder : D.Decoder ( a, List Log )
|
, decoder : D.Decoder ( a, List Log )
|
||||||
, docs : JSONDocs
|
, docs : Docs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type JSONDocs
|
{-| Structure of JSON documentation. It is up to an external module to turn the
|
||||||
|
documentation structure into a readable format.
|
||||||
|
-}
|
||||||
|
type Docs
|
||||||
= DocsBool
|
= DocsBool
|
||||||
| DocsDict JSONDocs
|
| DocsDict Docs
|
||||||
| DocsFloat
|
| DocsFloat
|
||||||
| DocsInt
|
| DocsInt
|
||||||
| DocsList JSONDocs
|
| DocsList Docs
|
||||||
| DocsObject
|
| DocsObject
|
||||||
{ name : String
|
{ name : String
|
||||||
, description : List String
|
, description : List String
|
||||||
|
@ -66,22 +126,28 @@ type JSONDocs
|
||||||
{ field : String
|
{ field : String
|
||||||
, description : List String
|
, description : List String
|
||||||
, required : RequiredField
|
, required : RequiredField
|
||||||
, content : JSONDocs
|
, content : Docs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| DocsOptional JSONDocs
|
| DocsOptional Docs
|
||||||
| DocsString
|
| DocsString
|
||||||
|
|
||||||
|
|
||||||
|
{-| Value that tells whether an object field is required to be included. If it
|
||||||
|
is not required, it can either be omitted - or a given default will be assumed.
|
||||||
|
The given default is a string representation, not the actual value.
|
||||||
|
-}
|
||||||
type RequiredField
|
type RequiredField
|
||||||
= RequiredField
|
= RequiredField
|
||||||
| OptionalField
|
| OptionalField
|
||||||
| OptionalFieldWithDefault String
|
| OptionalFieldWithDefault String
|
||||||
|
|
||||||
|
|
||||||
bool : JSONCoder Bool
|
{-| Define a boolean value.
|
||||||
|
-}
|
||||||
|
bool : Coder Bool
|
||||||
bool =
|
bool =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder = E.bool
|
{ encoder = E.bool
|
||||||
, decoder = D.map empty D.bool
|
, decoder = D.map empty D.bool
|
||||||
, docs = DocsBool
|
, docs = DocsBool
|
||||||
|
@ -90,11 +156,18 @@ bool =
|
||||||
|
|
||||||
{-| Get a JSON coder's decode value
|
{-| Get a JSON coder's decode value
|
||||||
-}
|
-}
|
||||||
decode : JSONCoder a -> D.Decoder ( a, List Log )
|
decode : Coder a -> D.Decoder ( a, List Log )
|
||||||
decode (JSONCoder data) =
|
decode (Coder data) =
|
||||||
data.decoder
|
data.decoder
|
||||||
|
|
||||||
|
|
||||||
|
{-| Generate documentation from a Coder definition.
|
||||||
|
-}
|
||||||
|
toDocs : Coder a -> Docs
|
||||||
|
toDocs (Coder data) =
|
||||||
|
data.docs
|
||||||
|
|
||||||
|
|
||||||
{-| Create a tuple with no logs
|
{-| Create a tuple with no logs
|
||||||
-}
|
-}
|
||||||
empty : a -> ( a, List Log )
|
empty : a -> ( a, List Log )
|
||||||
|
@ -103,17 +176,25 @@ empty x =
|
||||||
|
|
||||||
|
|
||||||
{-| Get a JSON coder's encode value
|
{-| Get a JSON coder's encode value
|
||||||
|
|
||||||
|
|
||||||
|
text : Json.Encode.Value
|
||||||
|
text =
|
||||||
|
encode string "test"
|
||||||
|
|
||||||
|
-- == Json.Encode.string "test"
|
||||||
|
|
||||||
-}
|
-}
|
||||||
encode : JSONCoder a -> (a -> E.Value)
|
encode : Coder a -> (a -> E.Value)
|
||||||
encode (JSONCoder data) =
|
encode (Coder data) =
|
||||||
data.encoder
|
data.encoder
|
||||||
|
|
||||||
|
|
||||||
{-| Define a fast dict. The dict can only have strings as keys.
|
{-| Define a fast dict. The dict can only have strings as keys.
|
||||||
-}
|
-}
|
||||||
fastDict : JSONCoder value -> JSONCoder (FastDict.Dict String value)
|
fastDict : Coder value -> Coder (FastDict.Dict String value)
|
||||||
fastDict (JSONCoder value) =
|
fastDict (Coder value) =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder = FastDict.toCoreDict >> E.dict identity value.encoder
|
{ encoder = FastDict.toCoreDict >> E.dict identity value.encoder
|
||||||
, decoder =
|
, decoder =
|
||||||
value.decoder
|
value.decoder
|
||||||
|
@ -132,26 +213,63 @@ fastDict (JSONCoder value) =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Create a new field
|
{-| Create a new field using any of the three provided options.
|
||||||
|
|
||||||
|
For example, suppose we are creating a `Field String User` to represent the
|
||||||
|
`name` field in
|
||||||
|
|
||||||
|
type alias User =
|
||||||
|
{ name : String
|
||||||
|
, age : Int
|
||||||
|
, hobbies : List String
|
||||||
|
}
|
||||||
|
|
||||||
|
then the following field type would be used:
|
||||||
|
|
||||||
|
field.required
|
||||||
|
{ fieldName = "name" -- Field name when encoded into JSON
|
||||||
|
, toField = .name
|
||||||
|
, description =
|
||||||
|
[ "This description describes this field's information content."
|
||||||
|
, "Here's another paragraph!"
|
||||||
|
]
|
||||||
|
, coder = string
|
||||||
|
}
|
||||||
|
|
||||||
|
Suppose the JSO isn't obligated to provide a list of hobbies, and the list would
|
||||||
|
by default be overriden with an empty list, then we would use the following
|
||||||
|
field type:
|
||||||
|
|
||||||
|
field.optional.withDefault
|
||||||
|
{ fieldName = "hobbies"
|
||||||
|
, toField = .hobbies
|
||||||
|
, description =
|
||||||
|
[ "The hobbies of the person. Can be omitted."
|
||||||
|
]
|
||||||
|
, coder = list string
|
||||||
|
, default = ( [], [] ) -- The `List Log` can be inserted in case you wish to insert a message when relying on a default
|
||||||
|
, defaultToString = always "[]" -- Default converted to a string
|
||||||
|
}
|
||||||
|
|
||||||
-}
|
-}
|
||||||
field :
|
field :
|
||||||
{ required : { fieldName : String, toField : object -> a, description : List String, coder : JSONCoder a } -> Field a object
|
{ required : { fieldName : String, toField : object -> a, description : List String, coder : Coder a } -> Field a object
|
||||||
, optional :
|
, optional :
|
||||||
{ value : { fieldName : String, toField : object -> Maybe a, description : List String, coder : JSONCoder a } -> Field (Maybe a) object
|
{ value : { fieldName : String, toField : object -> Maybe a, description : List String, coder : Coder a } -> Field (Maybe a) object
|
||||||
, withDefault : { fieldName : String, toField : object -> a, description : List String, coder : JSONCoder a, default : ( a, List Log ), defaultToString : a -> String } -> Field a object
|
, withDefault : { fieldName : String, toField : object -> a, description : List String, coder : Coder a, default : ( a, List Log ), defaultToString : a -> String } -> Field a object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field =
|
field =
|
||||||
{ required =
|
{ required =
|
||||||
\{ fieldName, toField, description, coder } ->
|
\{ fieldName, toField, description, coder } ->
|
||||||
case coder of
|
case coder of
|
||||||
JSONCoder { encoder, decoder, docs } ->
|
Coder { encoder, decoder, docs } ->
|
||||||
Field
|
Field
|
||||||
{ fieldName = fieldName
|
{ fieldName = fieldName
|
||||||
, toField = toField
|
, toField = toField
|
||||||
, description = description
|
, description = description
|
||||||
, encoder = encoder >> Maybe.Just
|
, encoder = encoder >> Maybe.Just
|
||||||
, decoder = decoder
|
, decoder = D.field fieldName decoder
|
||||||
, docs = docs
|
, docs = docs
|
||||||
, requiredness = RequiredField
|
, requiredness = RequiredField
|
||||||
}
|
}
|
||||||
|
@ -159,7 +277,7 @@ field =
|
||||||
{ value =
|
{ value =
|
||||||
\{ fieldName, toField, description, coder } ->
|
\{ fieldName, toField, description, coder } ->
|
||||||
case coder of
|
case coder of
|
||||||
JSONCoder { encoder, decoder, docs } ->
|
Coder { encoder, decoder, docs } ->
|
||||||
Field
|
Field
|
||||||
{ fieldName = fieldName
|
{ fieldName = fieldName
|
||||||
, toField = toField
|
, toField = toField
|
||||||
|
@ -183,7 +301,7 @@ field =
|
||||||
, withDefault =
|
, withDefault =
|
||||||
\{ fieldName, toField, description, coder, default, defaultToString } ->
|
\{ fieldName, toField, description, coder, default, defaultToString } ->
|
||||||
case coder of
|
case coder of
|
||||||
JSONCoder { encoder, decoder, docs } ->
|
Coder { encoder, decoder, docs } ->
|
||||||
Field
|
Field
|
||||||
{ fieldName = fieldName
|
{ fieldName = fieldName
|
||||||
, toField = toField
|
, toField = toField
|
||||||
|
@ -201,22 +319,22 @@ field =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Define a float.
|
{-| Define a float value.
|
||||||
-}
|
-}
|
||||||
float : JSONCoder Float
|
float : Coder Float
|
||||||
float =
|
float =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder = E.float
|
{ encoder = E.float
|
||||||
, decoder = D.map empty D.float
|
, decoder = D.map empty D.float
|
||||||
, docs = DocsFloat
|
, docs = DocsFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Define an int.
|
{-| Define an int value.
|
||||||
-}
|
-}
|
||||||
int : JSONCoder Int
|
int : Coder Int
|
||||||
int =
|
int =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder = E.int
|
{ encoder = E.int
|
||||||
, decoder = D.map empty D.int
|
, decoder = D.map empty D.int
|
||||||
, docs = DocsInt
|
, docs = DocsInt
|
||||||
|
@ -225,9 +343,9 @@ int =
|
||||||
|
|
||||||
{-| Define a list.
|
{-| Define a list.
|
||||||
-}
|
-}
|
||||||
list : JSONCoder a -> JSONCoder (List a)
|
list : Coder a -> Coder (List a)
|
||||||
list (JSONCoder old) =
|
list (Coder old) =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder = E.list old.encoder
|
{ encoder = E.list old.encoder
|
||||||
, decoder =
|
, decoder =
|
||||||
old.decoder
|
old.decoder
|
||||||
|
@ -242,9 +360,15 @@ list (JSONCoder old) =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
maybe : JSONCoder a -> JSONCoder (Maybe a)
|
{-| Define a maybe value.
|
||||||
maybe (JSONCoder old) =
|
|
||||||
JSONCoder
|
NOTE: most of the time, you wish to avoid this function! Make sure to look at
|
||||||
|
objects instead.
|
||||||
|
|
||||||
|
-}
|
||||||
|
maybe : Coder a -> Coder (Maybe a)
|
||||||
|
maybe (Coder old) =
|
||||||
|
Coder
|
||||||
{ encoder = Maybe.map old.encoder >> Maybe.withDefault E.null
|
{ encoder = Maybe.map old.encoder >> Maybe.withDefault E.null
|
||||||
, decoder =
|
, decoder =
|
||||||
old.decoder
|
old.decoder
|
||||||
|
@ -272,14 +396,46 @@ objectEncoder items object =
|
||||||
|
|
||||||
|
|
||||||
{-| Define an object with 2 keys
|
{-| Define an object with 2 keys
|
||||||
|
|
||||||
|
type alias Human =
|
||||||
|
{ name : String, age : Maybe Int }
|
||||||
|
|
||||||
|
humanCoder : Coder Human
|
||||||
|
humanCoder =
|
||||||
|
object2
|
||||||
|
{ name = "Human"
|
||||||
|
, description =
|
||||||
|
[ "Documentation description of the human type."
|
||||||
|
]
|
||||||
|
, init = Human
|
||||||
|
}
|
||||||
|
(field.required
|
||||||
|
{ fieldName = "name"
|
||||||
|
, toField = .name
|
||||||
|
, description =
|
||||||
|
[ "Human's name."
|
||||||
|
]
|
||||||
|
, coder = string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
(field.optional.value
|
||||||
|
{ fieldName = "age"
|
||||||
|
, toField = .age
|
||||||
|
, description =
|
||||||
|
[ "(Optional) human's age"
|
||||||
|
]
|
||||||
|
, coder = int
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
-}
|
-}
|
||||||
object2 :
|
object2 :
|
||||||
{ name : String, description : List String, init : a -> b -> object }
|
{ name : String, description : List String, init : a -> b -> object }
|
||||||
-> Field a object
|
-> Field a object
|
||||||
-> Field b object
|
-> Field b object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object2 { name, description, init } fa fb =
|
object2 { name, description, init } fa fb =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -313,9 +469,9 @@ object3 :
|
||||||
-> Field a object
|
-> Field a object
|
||||||
-> Field b object
|
-> Field b object
|
||||||
-> Field c object
|
-> Field c object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object3 { name, description, init } fa fb fc =
|
object3 { name, description, init } fa fb fc =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -353,9 +509,9 @@ object4 :
|
||||||
-> Field b object
|
-> Field b object
|
||||||
-> Field c object
|
-> Field c object
|
||||||
-> Field d object
|
-> Field d object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object4 { name, description, init } fa fb fc fd =
|
object4 { name, description, init } fa fb fc fd =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -397,9 +553,9 @@ object5 :
|
||||||
-> Field c object
|
-> Field c object
|
||||||
-> Field d object
|
-> Field d object
|
||||||
-> Field e object
|
-> Field e object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object5 { name, description, init } fa fb fc fd fe =
|
object5 { name, description, init } fa fb fc fd fe =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -445,9 +601,9 @@ object6 :
|
||||||
-> Field d object
|
-> Field d object
|
||||||
-> Field e object
|
-> Field e object
|
||||||
-> Field f object
|
-> Field f object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object6 { name, description, init } fa fb fc fd fe ff =
|
object6 { name, description, init } fa fb fc fd fe ff =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -497,9 +653,9 @@ object7 :
|
||||||
-> Field e object
|
-> Field e object
|
||||||
-> Field f object
|
-> Field f object
|
||||||
-> Field g object
|
-> Field g object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object7 { name, description, init } fa fb fc fd fe ff fg =
|
object7 { name, description, init } fa fb fc fd fe ff fg =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -553,9 +709,9 @@ object8 :
|
||||||
-> Field f object
|
-> Field f object
|
||||||
-> Field g object
|
-> Field g object
|
||||||
-> Field h object
|
-> Field h object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object8 { name, description, init } fa fb fc fd fe ff fg fh =
|
object8 { name, description, init } fa fb fc fd fe ff fg fh =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -613,9 +769,9 @@ object9 :
|
||||||
-> Field g object
|
-> Field g object
|
||||||
-> Field h object
|
-> Field h object
|
||||||
-> Field i object
|
-> Field i object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object9 { name, description, init } fa fb fc fd fe ff fg fh fi =
|
object9 { name, description, init } fa fb fc fd fe ff fg fh fi =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -677,9 +833,9 @@ object10 :
|
||||||
-> Field h object
|
-> Field h object
|
||||||
-> Field i object
|
-> Field i object
|
||||||
-> Field j object
|
-> Field j object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object10 { name, description, init } fa fb fc fd fe ff fg fh fi fj =
|
object10 { name, description, init } fa fb fc fd fe ff fg fh fi fj =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -745,9 +901,9 @@ object11 :
|
||||||
-> Field i object
|
-> Field i object
|
||||||
-> Field j object
|
-> Field j object
|
||||||
-> Field k object
|
-> Field k object
|
||||||
-> JSONCoder object
|
-> Coder object
|
||||||
object11 { name, description, init } fa fb fc fd fe ff fg fh fi fj fk =
|
object11 { name, description, init } fa fb fc fd fe ff fg fh fi fj fk =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder =
|
{ encoder =
|
||||||
objectEncoder
|
objectEncoder
|
||||||
[ toEncodeField fa
|
[ toEncodeField fa
|
||||||
|
@ -801,11 +957,11 @@ object11 { name, description, init } fa fb fc fd fe ff fg fh fi fj fk =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Define a slow dict from the elm/core library.
|
{-| Define a slow dict from the `elm/core` library.
|
||||||
-}
|
-}
|
||||||
slowDict : JSONCoder value -> JSONCoder (SlowDict.Dict String value)
|
slowDict : Coder value -> Coder (SlowDict.Dict String value)
|
||||||
slowDict (JSONCoder data) =
|
slowDict (Coder data) =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder = E.dict identity data.encoder
|
{ encoder = E.dict identity data.encoder
|
||||||
, decoder =
|
, decoder =
|
||||||
data.decoder
|
data.decoder
|
||||||
|
@ -824,11 +980,11 @@ slowDict (JSONCoder data) =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Define a string.
|
{-| Define a string value.
|
||||||
-}
|
-}
|
||||||
string : JSONCoder String
|
string : Coder String
|
||||||
string =
|
string =
|
||||||
JSONCoder
|
Coder
|
||||||
{ encoder = E.string
|
{ encoder = E.string
|
||||||
, decoder = D.map empty D.string
|
, decoder = D.map empty D.string
|
||||||
, docs = DocsString
|
, docs = DocsString
|
||||||
|
@ -844,7 +1000,7 @@ toDecoderField (Field data) =
|
||||||
|
|
||||||
{-| Turn a Field type into a descriptive field documentation
|
{-| Turn a Field type into a descriptive field documentation
|
||||||
-}
|
-}
|
||||||
toDocsField : Field a object -> { field : String, description : List String, required : RequiredField, content : JSONDocs }
|
toDocsField : Field a object -> { field : String, description : List String, required : RequiredField, content : Docs }
|
||||||
toDocsField x =
|
toDocsField x =
|
||||||
case x of
|
case x of
|
||||||
Field { fieldName, description, docs, requiredness } ->
|
Field { fieldName, description, docs, requiredness } ->
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
module Test.Tools.Json exposing (..)
|
||||||
|
|
||||||
|
import Expect
|
||||||
|
import Fuzz exposing (Fuzzer)
|
||||||
|
import Internal.Tools.Json as Json
|
||||||
|
import Json.Decode as D
|
||||||
|
import Json.Encode as E
|
||||||
|
import Test exposing (..)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Human2 =
|
||||||
|
{ name : String, age : Maybe Int }
|
||||||
|
|
||||||
|
|
||||||
|
type alias Human3 =
|
||||||
|
{ name : String, age : Maybe Int, hobbies : List String }
|
||||||
|
|
||||||
|
|
||||||
|
type alias Human4 =
|
||||||
|
{ name : String
|
||||||
|
, age : Maybe Int
|
||||||
|
, hobbies : List String
|
||||||
|
, weight : Maybe Float
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Human5 =
|
||||||
|
{ name : String
|
||||||
|
, age : Maybe Int
|
||||||
|
, hobbies : List String
|
||||||
|
, weight : Maybe Float
|
||||||
|
, height : Float
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ageField : Json.Field (Maybe Int) { a | age : Maybe Int }
|
||||||
|
ageField =
|
||||||
|
Json.field.optional.value
|
||||||
|
{ fieldName = "age"
|
||||||
|
, toField = .age
|
||||||
|
, description = []
|
||||||
|
, coder = Json.int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ageFuzzer : Fuzzer (Maybe Int)
|
||||||
|
ageFuzzer =
|
||||||
|
Fuzz.maybe Fuzz.int
|
||||||
|
|
||||||
|
|
||||||
|
heightField : Json.Field Float { a | height : Float }
|
||||||
|
heightField =
|
||||||
|
Json.field.required
|
||||||
|
{ fieldName = "height"
|
||||||
|
, toField = .height
|
||||||
|
, description = []
|
||||||
|
, coder = Json.float
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
heightFuzzer : Fuzzer Float
|
||||||
|
heightFuzzer =
|
||||||
|
Fuzz.niceFloat
|
||||||
|
|
||||||
|
|
||||||
|
hobbiesField : Json.Field (List String) { a | hobbies : List String }
|
||||||
|
hobbiesField =
|
||||||
|
Json.field.optional.withDefault
|
||||||
|
{ fieldName = "hobbies"
|
||||||
|
, toField = .hobbies
|
||||||
|
, description = []
|
||||||
|
, coder = Json.list Json.string
|
||||||
|
, default = ( [], [] )
|
||||||
|
, defaultToString = always "[]"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hobbiesFuzzer : Fuzzer (List String)
|
||||||
|
hobbiesFuzzer =
|
||||||
|
Fuzz.list Fuzz.string
|
||||||
|
|
||||||
|
|
||||||
|
nameField : Json.Field String { a | name : String }
|
||||||
|
nameField =
|
||||||
|
Json.field.required
|
||||||
|
{ fieldName = "name"
|
||||||
|
, toField = .name
|
||||||
|
, description = []
|
||||||
|
, coder = Json.string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nameFuzzer : Fuzzer String
|
||||||
|
nameFuzzer =
|
||||||
|
Fuzz.string
|
||||||
|
|
||||||
|
|
||||||
|
weightField : Json.Field (Maybe Float) { a | weight : Maybe Float }
|
||||||
|
weightField =
|
||||||
|
Json.field.optional.value
|
||||||
|
{ fieldName = "weight"
|
||||||
|
, toField = .weight
|
||||||
|
, description = []
|
||||||
|
, coder = Json.float
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
weightFuzzer : Fuzzer (Maybe Float)
|
||||||
|
weightFuzzer =
|
||||||
|
-- TODO: Maybe make Float not so nice?
|
||||||
|
Fuzz.maybe Fuzz.niceFloat
|
||||||
|
|
||||||
|
|
||||||
|
human2Coder : Json.Coder Human2
|
||||||
|
human2Coder =
|
||||||
|
Json.object2
|
||||||
|
{ name = "Human2"
|
||||||
|
, description = []
|
||||||
|
, init = Human2
|
||||||
|
}
|
||||||
|
nameField
|
||||||
|
ageField
|
||||||
|
|
||||||
|
|
||||||
|
human2Fuzzer : Fuzzer Human2
|
||||||
|
human2Fuzzer =
|
||||||
|
Fuzz.map2 Human2
|
||||||
|
nameFuzzer
|
||||||
|
ageFuzzer
|
||||||
|
|
||||||
|
|
||||||
|
human3Coder : Json.Coder Human3
|
||||||
|
human3Coder =
|
||||||
|
Json.object3
|
||||||
|
{ name = "Human3"
|
||||||
|
, description = []
|
||||||
|
, init = Human3
|
||||||
|
}
|
||||||
|
nameField
|
||||||
|
ageField
|
||||||
|
hobbiesField
|
||||||
|
|
||||||
|
|
||||||
|
human3Fuzzer : Fuzzer Human3
|
||||||
|
human3Fuzzer =
|
||||||
|
Fuzz.map3 Human3
|
||||||
|
nameFuzzer
|
||||||
|
ageFuzzer
|
||||||
|
hobbiesFuzzer
|
||||||
|
|
||||||
|
|
||||||
|
human4Coder : Json.Coder Human4
|
||||||
|
human4Coder =
|
||||||
|
Json.object4
|
||||||
|
{ name = "Human4"
|
||||||
|
, description = []
|
||||||
|
, init = Human4
|
||||||
|
}
|
||||||
|
nameField
|
||||||
|
ageField
|
||||||
|
hobbiesField
|
||||||
|
weightField
|
||||||
|
|
||||||
|
|
||||||
|
human4Fuzzer : Fuzzer Human4
|
||||||
|
human4Fuzzer =
|
||||||
|
Fuzz.map4 Human4
|
||||||
|
nameFuzzer
|
||||||
|
ageFuzzer
|
||||||
|
hobbiesFuzzer
|
||||||
|
weightFuzzer
|
||||||
|
|
||||||
|
|
||||||
|
human5Coder : Json.Coder Human5
|
||||||
|
human5Coder =
|
||||||
|
Json.object5
|
||||||
|
{ name = "Human5"
|
||||||
|
, description = []
|
||||||
|
, init = Human5
|
||||||
|
}
|
||||||
|
nameField
|
||||||
|
ageField
|
||||||
|
hobbiesField
|
||||||
|
weightField
|
||||||
|
heightField
|
||||||
|
|
||||||
|
|
||||||
|
human5Fuzzer : Fuzzer Human5
|
||||||
|
human5Fuzzer =
|
||||||
|
Fuzz.map5 Human5
|
||||||
|
nameFuzzer
|
||||||
|
ageFuzzer
|
||||||
|
hobbiesFuzzer
|
||||||
|
weightFuzzer
|
||||||
|
heightFuzzer
|
||||||
|
|
||||||
|
|
||||||
|
suite : Test
|
||||||
|
suite =
|
||||||
|
describe "JSON module"
|
||||||
|
[ describe "Human2"
|
||||||
|
[ fuzz human2Fuzzer
|
||||||
|
"Recoding succeeds"
|
||||||
|
(\human ->
|
||||||
|
human
|
||||||
|
|> Json.encode human2Coder
|
||||||
|
|> E.encode 0
|
||||||
|
|> D.decodeString (Json.decode human2Coder)
|
||||||
|
|> Result.map Tuple.first
|
||||||
|
|> Expect.equal (Ok human)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
, describe "Human3"
|
||||||
|
[ fuzz human3Fuzzer
|
||||||
|
"Recoding succeeds"
|
||||||
|
(\human ->
|
||||||
|
human
|
||||||
|
|> Json.encode human3Coder
|
||||||
|
|> E.encode 0
|
||||||
|
|> D.decodeString (Json.decode human3Coder)
|
||||||
|
|> Result.map Tuple.first
|
||||||
|
|> Expect.equal (Ok human)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
, describe "Human4"
|
||||||
|
[ fuzz human4Fuzzer
|
||||||
|
"Recoding succeeds"
|
||||||
|
(\human ->
|
||||||
|
human
|
||||||
|
|> Json.encode human4Coder
|
||||||
|
|> E.encode 0
|
||||||
|
|> D.decodeString (Json.decode human4Coder)
|
||||||
|
|> Result.map Tuple.first
|
||||||
|
|> Expect.equal (Ok human)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
, describe "Human5"
|
||||||
|
[ fuzz human5Fuzzer
|
||||||
|
"Recoding succeeds"
|
||||||
|
(\human ->
|
||||||
|
human
|
||||||
|
|> Json.encode human5Coder
|
||||||
|
|> E.encode 0
|
||||||
|
|> D.decodeString (Json.decode human5Coder)
|
||||||
|
|> Result.map Tuple.first
|
||||||
|
|> Expect.equal (Ok human)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
Loading…
Reference in New Issue