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