@f1studio/form-spec 5.0.0-alpha.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Designer.d.ts +89 -0
- package/FormSpec.TS/Components/HelloComponent.js +68 -0
- package/FormSpec.TS/Components/HelloComponent.js.map +1 -0
- package/FormSpec.TS/Components/HelloComponent.ts.map +1 -0
- package/FormSpec.TS/Designer.js +526 -0
- package/FormSpec.TS/Designer.js.map +1 -0
- package/FormSpec.TS/Designer.ts.map +1 -0
- package/FormSpec.TS/FormSpec.js +5400 -0
- package/FormSpec.TS/FormSpec.js.map +1 -0
- package/FormSpec.TS/FormSpec.ts.map +1 -0
- package/FormSpec.TS/FormSpecHelpers.js +382 -0
- package/FormSpec.TS/FormSpecHelpers.js.map +1 -0
- package/FormSpec.TS/FormSpecHelpers.ts.map +1 -0
- package/FormSpec.TS/Helpers.js +732 -0
- package/FormSpec.TS/Helpers.js.map +1 -0
- package/FormSpec.TS/Helpers.ts.map +1 -0
- package/FormSpec.TS/Interfaces.js +257 -0
- package/FormSpec.TS/Interfaces.js.map +1 -0
- package/FormSpec.TS/Interfaces.ts.map +1 -0
- package/FormSpec.TS/Interop/FormSpec.Api.Helpers.js +854 -0
- package/FormSpec.TS/Interop/FormSpec.Api.Helpers.js.map +1 -0
- package/FormSpec.TS/Interop/FormSpec.Api.Helpers.ts.map +1 -0
- package/FormSpec.TS/Interop/FormSpec.Api.Option.js +1961 -0
- package/FormSpec.TS/Interop/FormSpec.Api.Option.js.map +1 -0
- package/FormSpec.TS/Interop/FormSpec.Api.Option.ts.map +1 -0
- package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.js +1367 -0
- package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.js.map +1 -0
- package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.ts.map +1 -0
- package/FormSpec.TS/Logging/LogTypes.js +347 -0
- package/FormSpec.TS/Logging/LogTypes.js.map +1 -0
- package/FormSpec.TS/Logging/LogTypes.ts.map +1 -0
- package/FormSpec.TS/Migrator.js +230 -0
- package/FormSpec.TS/Migrator.js.map +1 -0
- package/FormSpec.TS/Migrator.ts.map +1 -0
- package/FormSpec.TS/PathwayDataExtractor.js +361 -0
- package/FormSpec.TS/PathwayDataExtractor.js.map +1 -0
- package/FormSpec.TS/PathwayDataExtractor.ts.map +1 -0
- package/FormSpec.TS/PathwayExecutor.js +1321 -0
- package/FormSpec.TS/PathwayExecutor.js.map +1 -0
- package/FormSpec.TS/PathwayExecutor.ts.map +1 -0
- package/FormSpec.TS/PathwayValidator.js +346 -0
- package/FormSpec.TS/PathwayValidator.js.map +1 -0
- package/FormSpec.TS/PathwayValidator.ts.map +1 -0
- package/FormSpec.TS/PluginInterface.js +171 -0
- package/FormSpec.TS/PluginInterface.js.map +1 -0
- package/FormSpec.TS/PluginInterface.ts.map +1 -0
- package/FormSpec.TS/Prelude.js +59 -0
- package/FormSpec.TS/Prelude.js.map +1 -0
- package/FormSpec.TS/Prelude.ts.map +1 -0
- package/FormSpec.TS/Renderers/FormSpecMarkdownRenderer.js +958 -0
- package/FormSpec.TS/Renderers/FormSpecMarkdownRenderer.js.map +1 -0
- package/FormSpec.TS/Renderers/FormSpecMarkdownRenderer.ts.map +1 -0
- package/FormSpec.TS/Renderers/MermaidRenderer.js +228 -0
- package/FormSpec.TS/Renderers/MermaidRenderer.js.map +1 -0
- package/FormSpec.TS/Renderers/MermaidRenderer.ts.map +1 -0
- package/FormSpec.TS/Renderers/PathwayRenderers.js +190 -0
- package/FormSpec.TS/Renderers/PathwayRenderers.js.map +1 -0
- package/FormSpec.TS/Renderers/PathwayRenderers.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs +511 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs.js +437 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs +16 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs.js +73 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs +50 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs.js +72 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Fable.Form.fableproj +28 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs +24 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs.js +56 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Fable.Form.Simple.fableproj +31 -0
- package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs +178 -0
- package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs.js +464 -0
- package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Extensions.fs +17 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs +152 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs.js +25 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Types.fableproj +28 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs +218 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs.js +37 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.Types.fableproj +27 -0
- package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs +82 -0
- package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs.js +7 -0
- package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/BorderStyle.fs +59 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs +154 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs.js +32 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Feliz.fableproj +42 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Fonts.fs +240 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/GridTypes.fs +24 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Html.fs +826 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs +83 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs.js +292 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs +65 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs.js +229 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Length.fs +91 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Locale.fs +876 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs +4080 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs.js +133 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs +656 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs.js +561 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs +25 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs.js +46 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactInterop.js +63 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactInterop.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs +41 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs.js +7 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs +53 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs.js +7 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Styles.fs +5740 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs +1455 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs.js +39 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/TextDecorationLine.fs +13 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/TextDecorationStyle.fs +33 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Transform.fs +181 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/TransformOrigin.fs +17 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/TransitionProperty.fs +162 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs +13 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs.js +7 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs +1768 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs.js +2337 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs +811 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.js +465 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs +47 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs.js +18 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Thoth.Json.fableproj +34 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs +68 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs.js +355 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs.js.map +1 -0
- package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs.ts.map +1 -0
- package/FormSpec.TS/fable_modules/project_cracked.json +1 -0
- package/FormSpec.d.ts +1257 -0
- package/FormSpecHelpers.d.ts +50 -0
- package/Helpers.d.ts +147 -0
- package/Interfaces.d.ts +70 -0
- package/Interop/FormSpec.Api.Helpers.d.ts +247 -0
- package/Interop/FormSpec.Api.Helpers.d.ts.map +1 -0
- package/Interop/FormSpec.Api.Option.d.ts +384 -0
- package/Interop/FormSpec.Api.Option.d.ts.map +1 -0
- package/Interop/FormSpec.Values.Api.Option.d.ts +324 -0
- package/Interop/FormSpec.Values.Api.Option.d.ts.map +1 -0
- package/Migrator.d.ts +59 -0
- package/PathwayDataExtractor.d.ts +19 -0
- package/PathwayExecutor.d.ts +210 -0
- package/PathwayValidator.d.ts +52 -0
- package/PluginInterface.d.ts +36 -0
- package/Prelude.d.ts +11 -0
- package/README.TS.md +622 -0
- package/README.md +85 -0
- package/package.json +39 -0
- package/src/Components/HelloComponent.ts +48 -0
- package/src/Components/HelloComponent.ts.map +1 -0
- package/src/Designer.ts +389 -0
- package/src/Designer.ts.map +1 -0
- package/src/FormSpec.ts +3114 -0
- package/src/FormSpec.ts.map +1 -0
- package/src/FormSpecHelpers.ts +374 -0
- package/src/FormSpecHelpers.ts.map +1 -0
- package/src/Helpers.ts +765 -0
- package/src/Helpers.ts.map +1 -0
- package/src/Interfaces.ts +166 -0
- package/src/Interfaces.ts.map +1 -0
- package/src/Interop/FormSpec.Api.Helpers.ts +872 -0
- package/src/Interop/FormSpec.Api.Helpers.ts.map +1 -0
- package/src/Interop/FormSpec.Api.Option.ts +1618 -0
- package/src/Interop/FormSpec.Api.Option.ts.map +1 -0
- package/src/Interop/FormSpec.Values.Api.Option.ts +1214 -0
- package/src/Interop/FormSpec.Values.Api.Option.ts.map +1 -0
- package/src/Logging/LogTypes.ts +212 -0
- package/src/Logging/LogTypes.ts.map +1 -0
- package/src/Migrator.ts +156 -0
- package/src/Migrator.ts.map +1 -0
- package/src/PathwayDataExtractor.ts +290 -0
- package/src/PathwayDataExtractor.ts.map +1 -0
- package/src/PathwayExecutor.ts +1102 -0
- package/src/PathwayExecutor.ts.map +1 -0
- package/src/PathwayValidator.ts +244 -0
- package/src/PathwayValidator.ts.map +1 -0
- package/src/PluginInterface.ts +79 -0
- package/src/PluginInterface.ts.map +1 -0
- package/src/Prelude.ts +21 -0
- package/src/Prelude.ts.map +1 -0
- package/src/Renderers/FormSpecMarkdownRenderer.ts +874 -0
- package/src/Renderers/FormSpecMarkdownRenderer.ts.map +1 -0
- package/src/Renderers/MermaidRenderer.ts +218 -0
- package/src/Renderers/MermaidRenderer.ts.map +1 -0
- package/src/Renderers/PathwayRenderers.ts +200 -0
- package/src/Renderers/PathwayRenderers.ts.map +1 -0
|
@@ -0,0 +1,1768 @@
|
|
|
1
|
+
namespace Thoth.Json
|
|
2
|
+
|
|
3
|
+
open System.Text.RegularExpressions
|
|
4
|
+
|
|
5
|
+
[<RequireQualifiedAccess>]
|
|
6
|
+
module Decode =
|
|
7
|
+
|
|
8
|
+
open System.Globalization
|
|
9
|
+
open Fable.Core
|
|
10
|
+
open Fable.Core.JsInterop
|
|
11
|
+
|
|
12
|
+
module Helpers =
|
|
13
|
+
[<Emit("typeof $0")>]
|
|
14
|
+
let jsTypeof (_: JsonValue) : string = jsNative
|
|
15
|
+
|
|
16
|
+
[<Emit("$0 instanceof SyntaxError")>]
|
|
17
|
+
let isSyntaxError (_: JsonValue) : bool = jsNative
|
|
18
|
+
|
|
19
|
+
let inline getField (fieldName: string) (o: JsonValue) = o?(fieldName)
|
|
20
|
+
let inline isString (o: JsonValue) : bool = o :? string
|
|
21
|
+
|
|
22
|
+
let inline isBoolean (o: JsonValue) : bool = o :? bool
|
|
23
|
+
|
|
24
|
+
let inline isNumber (o: JsonValue) : bool = jsTypeof o = "number"
|
|
25
|
+
|
|
26
|
+
let inline isArray (o: JsonValue) : bool =
|
|
27
|
+
JS.Constructors.Array.isArray (o)
|
|
28
|
+
|
|
29
|
+
[<Emit("$0 === null ? false : (Object.getPrototypeOf($0 || false) === Object.prototype)")>]
|
|
30
|
+
let isObject (_: JsonValue) : bool = jsNative
|
|
31
|
+
|
|
32
|
+
let inline isNaN (o: JsonValue) : bool =
|
|
33
|
+
JS.Constructors.Number.isNaN (!!o)
|
|
34
|
+
|
|
35
|
+
let inline isNullValue (o: JsonValue) : bool = isNull o
|
|
36
|
+
|
|
37
|
+
/// is the value an integer? This returns false for 1.1, NaN, Infinite, ...
|
|
38
|
+
[<Emit("isFinite($0) && Math.floor($0) === $0")>]
|
|
39
|
+
let isIntegralValue (_: JsonValue) : bool = jsNative
|
|
40
|
+
|
|
41
|
+
[<Emit("$1 <= $0 && $0 < $2")>]
|
|
42
|
+
let isBetweenInclusive (_v: JsonValue, _min: obj, _max: obj) = jsNative
|
|
43
|
+
|
|
44
|
+
[<Emit("isFinite($0) && !($0 % 1)")>]
|
|
45
|
+
let isIntFinite (_: JsonValue) : bool = jsNative
|
|
46
|
+
|
|
47
|
+
let isUndefined (o: JsonValue) : bool = jsTypeof o = "undefined"
|
|
48
|
+
|
|
49
|
+
[<Emit("JSON.stringify($0, null, 4) + ''")>]
|
|
50
|
+
let anyToString (_: JsonValue) : string = jsNative
|
|
51
|
+
|
|
52
|
+
let inline isFunction (o: JsonValue) : bool = jsTypeof o = "function"
|
|
53
|
+
|
|
54
|
+
let inline objectKeys (o: JsonValue) : string seq =
|
|
55
|
+
upcast JS.Constructors.Object.keys (o)
|
|
56
|
+
|
|
57
|
+
let inline asBool (o: JsonValue) : bool = unbox o
|
|
58
|
+
let inline asInt (o: JsonValue) : int = unbox o
|
|
59
|
+
let inline asFloat (o: JsonValue) : float = unbox o
|
|
60
|
+
let inline asFloat32 (o: JsonValue) : float32 = unbox o
|
|
61
|
+
let inline asString (o: JsonValue) : string = unbox o
|
|
62
|
+
let inline asArray (o: JsonValue) : JsonValue[] = unbox o
|
|
63
|
+
|
|
64
|
+
let private genericMsg msg value newLine =
|
|
65
|
+
try
|
|
66
|
+
"Expecting "
|
|
67
|
+
+ msg
|
|
68
|
+
+ " but instead got:"
|
|
69
|
+
+ (if newLine then
|
|
70
|
+
"\n"
|
|
71
|
+
else
|
|
72
|
+
" ")
|
|
73
|
+
+ (Helpers.anyToString value)
|
|
74
|
+
with _ ->
|
|
75
|
+
"Expecting "
|
|
76
|
+
+ msg
|
|
77
|
+
+ " but decoder failed. Couldn't report given value due to circular structure."
|
|
78
|
+
+ (if newLine then
|
|
79
|
+
"\n"
|
|
80
|
+
else
|
|
81
|
+
" ")
|
|
82
|
+
|
|
83
|
+
let private errorToString (path: string, error) =
|
|
84
|
+
let reason =
|
|
85
|
+
match error with
|
|
86
|
+
| BadPrimitive(msg, value) -> genericMsg msg value false
|
|
87
|
+
| BadType(msg, value) -> genericMsg msg value true
|
|
88
|
+
| BadPrimitiveExtra(msg, value, reason) ->
|
|
89
|
+
genericMsg msg value false + "\nReason: " + reason
|
|
90
|
+
| BadField(msg, value) -> genericMsg msg value true
|
|
91
|
+
| BadPath(msg, value, fieldName) ->
|
|
92
|
+
genericMsg msg value true
|
|
93
|
+
+ ("\nNode `" + fieldName + "` is unkown.")
|
|
94
|
+
| TooSmallArray(msg, value) ->
|
|
95
|
+
"Expecting " + msg + ".\n" + (Helpers.anyToString value)
|
|
96
|
+
| BadOneOf messages ->
|
|
97
|
+
"The following errors were found:\n\n"
|
|
98
|
+
+ String.concat "\n\n" messages
|
|
99
|
+
| FailMessage msg ->
|
|
100
|
+
"The following `failure` occurred with the decoder: " + msg
|
|
101
|
+
|
|
102
|
+
match error with
|
|
103
|
+
| BadOneOf _ ->
|
|
104
|
+
// Don't need to show the path here because each error case will show it's own path
|
|
105
|
+
reason
|
|
106
|
+
| _ -> "Error at: `" + path + "`\n" + reason
|
|
107
|
+
|
|
108
|
+
///////////////
|
|
109
|
+
// Runners ///
|
|
110
|
+
/////////////
|
|
111
|
+
|
|
112
|
+
/// <summary>
|
|
113
|
+
/// Runs the decoder against the given JSON value.
|
|
114
|
+
///
|
|
115
|
+
/// If the decoder fails, it reports the error prefixed with the given path.
|
|
116
|
+
///
|
|
117
|
+
/// </summary>
|
|
118
|
+
/// <example>
|
|
119
|
+
/// <code lang="fsharp">
|
|
120
|
+
/// module Decode =
|
|
121
|
+
/// let fromRootValue (decoder : Decoder<'T>) =
|
|
122
|
+
/// Decode.fromValue "$" decoder
|
|
123
|
+
/// </code>
|
|
124
|
+
/// </example>
|
|
125
|
+
/// <param name="path">Path used to report the error</param>
|
|
126
|
+
/// <param name="decoder">Decoder to apply</param>
|
|
127
|
+
/// <param name="value">JSON value to decoder</param>
|
|
128
|
+
/// <returns>
|
|
129
|
+
/// Returns <c>Ok</c> if the decoder succeeds, otherwise <c>Error</c> with the error message.
|
|
130
|
+
/// </returns>
|
|
131
|
+
let fromValue (path: string) (decoder: Decoder<'T>) =
|
|
132
|
+
fun value ->
|
|
133
|
+
match decoder path value with
|
|
134
|
+
| Ok success -> Ok success
|
|
135
|
+
| Error error -> Error(errorToString error)
|
|
136
|
+
|
|
137
|
+
/// <summary>
|
|
138
|
+
/// Parse the provided string in as JSON and then run the decoder against it.
|
|
139
|
+
/// </summary>
|
|
140
|
+
/// <param name="decoder">Decoder to apply</param>
|
|
141
|
+
/// <param name="value">JSON string to decode</param>
|
|
142
|
+
/// <returns>
|
|
143
|
+
/// Returns <c>Ok</c> if the decoder succeeds, otherwise <c>Error</c> with the error message.
|
|
144
|
+
/// </returns>
|
|
145
|
+
let fromString (decoder: Decoder<'T>) =
|
|
146
|
+
fun value ->
|
|
147
|
+
try
|
|
148
|
+
let json = JS.JSON.parse value
|
|
149
|
+
fromValue "$" decoder json
|
|
150
|
+
with ex when Helpers.isSyntaxError ex ->
|
|
151
|
+
Error("Given an invalid JSON: " + ex.Message)
|
|
152
|
+
|
|
153
|
+
/// <summary>
|
|
154
|
+
/// Parse the provided string in as JSON and then run the decoder against it.
|
|
155
|
+
/// </summary>
|
|
156
|
+
/// <param name="decoder">Decoder to apply</param>
|
|
157
|
+
/// <param name="value">JSON string to decode</param>
|
|
158
|
+
/// <returns>
|
|
159
|
+
/// Return the decoded value if the decoder succeeds, otherwise throws an exception.
|
|
160
|
+
/// </returns>
|
|
161
|
+
let unsafeFromString (decoder: Decoder<'T>) =
|
|
162
|
+
fun value ->
|
|
163
|
+
match fromString decoder value with
|
|
164
|
+
| Ok x -> x
|
|
165
|
+
| Error msg -> failwith msg
|
|
166
|
+
|
|
167
|
+
[<System.Obsolete("Please use fromValue instead")>]
|
|
168
|
+
let decodeValue (path: string) (decoder: Decoder<'T>) =
|
|
169
|
+
fromValue path decoder
|
|
170
|
+
|
|
171
|
+
[<System.Obsolete("Please use fromString instead")>]
|
|
172
|
+
let decodeString (decoder: Decoder<'T>) = fromString decoder
|
|
173
|
+
|
|
174
|
+
//////////////////
|
|
175
|
+
// Primitives ///
|
|
176
|
+
////////////////
|
|
177
|
+
|
|
178
|
+
let string: Decoder<string> =
|
|
179
|
+
fun path value ->
|
|
180
|
+
if Helpers.isString value then
|
|
181
|
+
Ok(Helpers.asString value)
|
|
182
|
+
else
|
|
183
|
+
(path, BadPrimitive("a string", value)) |> Error
|
|
184
|
+
|
|
185
|
+
let char: Decoder<char> =
|
|
186
|
+
fun path value ->
|
|
187
|
+
if Helpers.isString value then
|
|
188
|
+
let str = Helpers.asString value
|
|
189
|
+
|
|
190
|
+
if str.Length = 1 then
|
|
191
|
+
Ok(str.[0])
|
|
192
|
+
else
|
|
193
|
+
(path, BadPrimitive("a single character string", value))
|
|
194
|
+
|> Error
|
|
195
|
+
else
|
|
196
|
+
(path, BadPrimitive("a char", value)) |> Error
|
|
197
|
+
|
|
198
|
+
let guid: Decoder<System.Guid> =
|
|
199
|
+
fun path value ->
|
|
200
|
+
if Helpers.isString value then
|
|
201
|
+
match System.Guid.TryParse(Helpers.asString value) with
|
|
202
|
+
| true, x -> Ok x
|
|
203
|
+
| _ -> (path, BadPrimitive("a guid", value)) |> Error
|
|
204
|
+
else
|
|
205
|
+
(path, BadPrimitive("a guid", value)) |> Error
|
|
206
|
+
|
|
207
|
+
let unit: Decoder<unit> =
|
|
208
|
+
fun path value ->
|
|
209
|
+
if Helpers.isNullValue value then
|
|
210
|
+
Ok()
|
|
211
|
+
else
|
|
212
|
+
(path, BadPrimitive("null", value)) |> Error
|
|
213
|
+
|
|
214
|
+
let inline private integral
|
|
215
|
+
(name: string)
|
|
216
|
+
(tryParse: (string -> bool * 'T))
|
|
217
|
+
(min: unit -> 'T)
|
|
218
|
+
(max: unit -> 'T)
|
|
219
|
+
(conv: float -> 'T)
|
|
220
|
+
: Decoder<'T>
|
|
221
|
+
=
|
|
222
|
+
|
|
223
|
+
fun path value ->
|
|
224
|
+
if Helpers.isNumber value then
|
|
225
|
+
let value: float = unbox value
|
|
226
|
+
|
|
227
|
+
if Helpers.isIntegralValue value then
|
|
228
|
+
if
|
|
229
|
+
(float (min ())) <= value && value <= (float (max ()))
|
|
230
|
+
then
|
|
231
|
+
Ok(conv value)
|
|
232
|
+
else
|
|
233
|
+
(path,
|
|
234
|
+
BadPrimitiveExtra(
|
|
235
|
+
name,
|
|
236
|
+
value,
|
|
237
|
+
"Value was either too large or too small for "
|
|
238
|
+
+ name
|
|
239
|
+
))
|
|
240
|
+
|> Error
|
|
241
|
+
else
|
|
242
|
+
(path,
|
|
243
|
+
BadPrimitiveExtra(
|
|
244
|
+
name,
|
|
245
|
+
value,
|
|
246
|
+
"Value is not an integral value"
|
|
247
|
+
))
|
|
248
|
+
|> Error
|
|
249
|
+
elif Helpers.isString value then
|
|
250
|
+
match tryParse (Helpers.asString value) with
|
|
251
|
+
| true, x -> Ok x
|
|
252
|
+
| _ -> (path, BadPrimitive(name, value)) |> Error
|
|
253
|
+
else
|
|
254
|
+
(path, BadPrimitive(name, value)) |> Error
|
|
255
|
+
|
|
256
|
+
let sbyte: Decoder<sbyte> =
|
|
257
|
+
integral
|
|
258
|
+
"a sbyte"
|
|
259
|
+
System.SByte.TryParse
|
|
260
|
+
(fun () -> System.SByte.MinValue)
|
|
261
|
+
(fun () -> System.SByte.MaxValue)
|
|
262
|
+
sbyte
|
|
263
|
+
|
|
264
|
+
/// Alias to Decode.uint8
|
|
265
|
+
let byte: Decoder<byte> =
|
|
266
|
+
integral
|
|
267
|
+
"a byte"
|
|
268
|
+
System.Byte.TryParse
|
|
269
|
+
(fun () -> System.Byte.MinValue)
|
|
270
|
+
(fun () -> System.Byte.MaxValue)
|
|
271
|
+
byte
|
|
272
|
+
|
|
273
|
+
let int16: Decoder<int16> =
|
|
274
|
+
integral
|
|
275
|
+
"an int16"
|
|
276
|
+
System.Int16.TryParse
|
|
277
|
+
(fun () -> System.Int16.MinValue)
|
|
278
|
+
(fun () -> System.Int16.MaxValue)
|
|
279
|
+
int16
|
|
280
|
+
|
|
281
|
+
let uint16: Decoder<uint16> =
|
|
282
|
+
integral
|
|
283
|
+
"an uint16"
|
|
284
|
+
System.UInt16.TryParse
|
|
285
|
+
(fun () -> System.UInt16.MinValue)
|
|
286
|
+
(fun () -> System.UInt16.MaxValue)
|
|
287
|
+
uint16
|
|
288
|
+
|
|
289
|
+
let int: Decoder<int> =
|
|
290
|
+
integral
|
|
291
|
+
"an int"
|
|
292
|
+
System.Int32.TryParse
|
|
293
|
+
(fun () -> System.Int32.MinValue)
|
|
294
|
+
(fun () -> System.Int32.MaxValue)
|
|
295
|
+
int
|
|
296
|
+
|
|
297
|
+
let uint32: Decoder<uint32> =
|
|
298
|
+
integral
|
|
299
|
+
"an uint32"
|
|
300
|
+
System.UInt32.TryParse
|
|
301
|
+
(fun () -> System.UInt32.MinValue)
|
|
302
|
+
(fun () -> System.UInt32.MaxValue)
|
|
303
|
+
uint32
|
|
304
|
+
|
|
305
|
+
let int64: Decoder<int64> =
|
|
306
|
+
integral
|
|
307
|
+
"an int64"
|
|
308
|
+
System.Int64.TryParse
|
|
309
|
+
(fun () -> System.Int64.MinValue)
|
|
310
|
+
(fun () -> System.Int64.MaxValue)
|
|
311
|
+
int64
|
|
312
|
+
|
|
313
|
+
let uint64: Decoder<uint64> =
|
|
314
|
+
integral
|
|
315
|
+
"an uint64"
|
|
316
|
+
System.UInt64.TryParse
|
|
317
|
+
(fun () -> System.UInt64.MinValue)
|
|
318
|
+
(fun () -> System.UInt64.MaxValue)
|
|
319
|
+
uint64
|
|
320
|
+
|
|
321
|
+
let bigint: Decoder<bigint> =
|
|
322
|
+
fun path value ->
|
|
323
|
+
if Helpers.isNumber value then
|
|
324
|
+
Helpers.asInt value |> bigint |> Ok
|
|
325
|
+
elif Helpers.isString value then
|
|
326
|
+
// TODO: BigInt.TryParse has been added in Fable 2.1.4
|
|
327
|
+
// Don't use it for now to support lower Fable versions
|
|
328
|
+
try
|
|
329
|
+
bigint.Parse(Helpers.asString value) |> Ok
|
|
330
|
+
with _ ->
|
|
331
|
+
(path, BadPrimitive("a bigint", value)) |> Error
|
|
332
|
+
else
|
|
333
|
+
(path, BadPrimitive("a bigint", value)) |> Error
|
|
334
|
+
|
|
335
|
+
let bool: Decoder<bool> =
|
|
336
|
+
fun path value ->
|
|
337
|
+
if Helpers.isBoolean value then
|
|
338
|
+
Ok(Helpers.asBool value)
|
|
339
|
+
else
|
|
340
|
+
(path, BadPrimitive("a boolean", value)) |> Error
|
|
341
|
+
|
|
342
|
+
let float: Decoder<float> =
|
|
343
|
+
fun path value ->
|
|
344
|
+
if Helpers.isNumber value then
|
|
345
|
+
Ok(Helpers.asFloat value)
|
|
346
|
+
else
|
|
347
|
+
(path, BadPrimitive("a float", value)) |> Error
|
|
348
|
+
|
|
349
|
+
let float32: Decoder<float32> =
|
|
350
|
+
fun path value ->
|
|
351
|
+
if Helpers.isNumber value then
|
|
352
|
+
Ok(Helpers.asFloat32 value)
|
|
353
|
+
else
|
|
354
|
+
(path, BadPrimitive("a float32", value)) |> Error
|
|
355
|
+
|
|
356
|
+
let decimal: Decoder<decimal> =
|
|
357
|
+
fun path value ->
|
|
358
|
+
if Helpers.isNumber value then
|
|
359
|
+
Helpers.asFloat value |> decimal |> Ok
|
|
360
|
+
elif Helpers.isString value then
|
|
361
|
+
match System.Decimal.TryParse(Helpers.asString value) with
|
|
362
|
+
| true, x -> Ok x
|
|
363
|
+
| _ -> (path, BadPrimitive("a decimal", value)) |> Error
|
|
364
|
+
else
|
|
365
|
+
(path, BadPrimitive("a decimal", value)) |> Error
|
|
366
|
+
|
|
367
|
+
[<System.Obsolete("Please use datetimeUtc instead.")>]
|
|
368
|
+
let datetime: Decoder<System.DateTime> =
|
|
369
|
+
fun path value ->
|
|
370
|
+
if Helpers.isString value then
|
|
371
|
+
match System.DateTime.TryParse(Helpers.asString value) with
|
|
372
|
+
| true, x -> x.ToUniversalTime() |> Ok
|
|
373
|
+
| _ -> (path, BadPrimitive("a datetime", value)) |> Error
|
|
374
|
+
else
|
|
375
|
+
(path, BadPrimitive("a datetime", value)) |> Error
|
|
376
|
+
|
|
377
|
+
/// Decode a System.DateTime value using Sytem.DateTime.TryParse, then convert it to UTC.
|
|
378
|
+
let datetimeUtc: Decoder<System.DateTime> =
|
|
379
|
+
fun path value ->
|
|
380
|
+
if Helpers.isString value then
|
|
381
|
+
match System.DateTime.TryParse(Helpers.asString value) with
|
|
382
|
+
| true, x -> x.ToUniversalTime() |> Ok
|
|
383
|
+
| _ -> (path, BadPrimitive("a datetime", value)) |> Error
|
|
384
|
+
else
|
|
385
|
+
(path, BadPrimitive("a datetime", value)) |> Error
|
|
386
|
+
|
|
387
|
+
/// Decode a System.DateTime with DateTime.TryParse; uses default System.DateTimeStyles.
|
|
388
|
+
let datetimeLocal: Decoder<System.DateTime> =
|
|
389
|
+
fun path value ->
|
|
390
|
+
if Helpers.isString value then
|
|
391
|
+
match System.DateTime.TryParse(Helpers.asString value) with
|
|
392
|
+
| true, x -> x |> Ok
|
|
393
|
+
| _ -> (path, BadPrimitive("a datetime", value)) |> Error
|
|
394
|
+
else
|
|
395
|
+
(path, BadPrimitive("a datetime", value)) |> Error
|
|
396
|
+
|
|
397
|
+
let datetimeOffset: Decoder<System.DateTimeOffset> =
|
|
398
|
+
fun path value ->
|
|
399
|
+
if Helpers.isString value then
|
|
400
|
+
match
|
|
401
|
+
System.DateTimeOffset.TryParse(Helpers.asString value)
|
|
402
|
+
with
|
|
403
|
+
| true, x -> Ok x
|
|
404
|
+
| _ -> (path, BadPrimitive("a datetimeoffset", value)) |> Error
|
|
405
|
+
else
|
|
406
|
+
(path, BadPrimitive("a datetime", value)) |> Error
|
|
407
|
+
|
|
408
|
+
let timespan: Decoder<System.TimeSpan> =
|
|
409
|
+
fun path value ->
|
|
410
|
+
if Helpers.isString value then
|
|
411
|
+
match System.TimeSpan.TryParse(Helpers.asString value) with
|
|
412
|
+
| true, x -> Ok x
|
|
413
|
+
| _ -> (path, BadPrimitive("a timespan", value)) |> Error
|
|
414
|
+
else
|
|
415
|
+
(path, BadPrimitive("a timespan", value)) |> Error
|
|
416
|
+
|
|
417
|
+
/////////////////////////
|
|
418
|
+
// Object primitives ///
|
|
419
|
+
///////////////////////
|
|
420
|
+
|
|
421
|
+
let private decodeMaybeNull path (decoder: Decoder<'T>) value =
|
|
422
|
+
// The decoder may be an option decoder so give it an opportunity to check null values
|
|
423
|
+
|
|
424
|
+
// We catch the null value case first to avoid executing the decoder logic
|
|
425
|
+
// Indeed, if the decoder logic try to access the value to do something with it,
|
|
426
|
+
// it can throw an exception about the value being null
|
|
427
|
+
if Helpers.isNullValue value then
|
|
428
|
+
Ok None
|
|
429
|
+
else
|
|
430
|
+
match decoder path value with
|
|
431
|
+
| Ok v -> Ok(Some v)
|
|
432
|
+
| Error er -> Error er
|
|
433
|
+
|
|
434
|
+
let optional
|
|
435
|
+
(fieldName: string)
|
|
436
|
+
(decoder: Decoder<'value>)
|
|
437
|
+
: Decoder<'value option>
|
|
438
|
+
=
|
|
439
|
+
fun path value ->
|
|
440
|
+
if Helpers.isObject value then
|
|
441
|
+
let fieldValue = Helpers.getField fieldName value
|
|
442
|
+
|
|
443
|
+
if Helpers.isUndefined fieldValue then
|
|
444
|
+
Ok None
|
|
445
|
+
else
|
|
446
|
+
decodeMaybeNull (path + "." + fieldName) decoder fieldValue
|
|
447
|
+
else
|
|
448
|
+
Error(path, BadType("an object", value))
|
|
449
|
+
|
|
450
|
+
let private badPathError fieldNames currentPath value =
|
|
451
|
+
let currentPath =
|
|
452
|
+
defaultArg currentPath ("$" :: fieldNames |> String.concat ".")
|
|
453
|
+
|
|
454
|
+
let msg = "an object with path `" + (String.concat "." fieldNames) + "`"
|
|
455
|
+
|
|
456
|
+
Error(
|
|
457
|
+
currentPath,
|
|
458
|
+
BadPath(
|
|
459
|
+
msg,
|
|
460
|
+
value,
|
|
461
|
+
List.tryLast fieldNames |> Option.defaultValue ""
|
|
462
|
+
)
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
let optionalAt
|
|
466
|
+
(fieldNames: string list)
|
|
467
|
+
(decoder: Decoder<'value>)
|
|
468
|
+
: Decoder<'value option>
|
|
469
|
+
=
|
|
470
|
+
fun firstPath firstValue ->
|
|
471
|
+
((firstPath, firstValue, None), fieldNames)
|
|
472
|
+
||> List.fold (fun (curPath, curValue, res) field ->
|
|
473
|
+
match res with
|
|
474
|
+
| Some _ -> curPath, curValue, res
|
|
475
|
+
| None ->
|
|
476
|
+
if Helpers.isNullValue curValue then
|
|
477
|
+
curPath, curValue, Some(Ok None)
|
|
478
|
+
elif Helpers.isObject curValue then
|
|
479
|
+
let curValue = Helpers.getField field curValue
|
|
480
|
+
curPath + "." + field, curValue, None
|
|
481
|
+
else
|
|
482
|
+
let res =
|
|
483
|
+
Error(curPath, BadType("an object", curValue))
|
|
484
|
+
|
|
485
|
+
curPath, curValue, Some res
|
|
486
|
+
)
|
|
487
|
+
|> function
|
|
488
|
+
| _, _, Some res -> res
|
|
489
|
+
| lastPath, lastValue, None ->
|
|
490
|
+
if Helpers.isUndefined lastValue then
|
|
491
|
+
Ok None
|
|
492
|
+
else
|
|
493
|
+
decodeMaybeNull lastPath decoder lastValue
|
|
494
|
+
|
|
495
|
+
let field (fieldName: string) (decoder: Decoder<'value>) : Decoder<'value> =
|
|
496
|
+
fun path value ->
|
|
497
|
+
if Helpers.isObject value then
|
|
498
|
+
let fieldValue = Helpers.getField fieldName value
|
|
499
|
+
|
|
500
|
+
if Helpers.isUndefined fieldValue then
|
|
501
|
+
Error(
|
|
502
|
+
path,
|
|
503
|
+
BadField(
|
|
504
|
+
"an object with a field named `" + fieldName + "`",
|
|
505
|
+
value
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
else
|
|
509
|
+
decoder (path + "." + fieldName) fieldValue
|
|
510
|
+
else
|
|
511
|
+
Error(path, BadType("an object", value))
|
|
512
|
+
|
|
513
|
+
let at
|
|
514
|
+
(fieldNames: string list)
|
|
515
|
+
(decoder: Decoder<'value>)
|
|
516
|
+
: Decoder<'value>
|
|
517
|
+
=
|
|
518
|
+
fun firstPath firstValue ->
|
|
519
|
+
((firstPath, firstValue, None), fieldNames)
|
|
520
|
+
||> List.fold (fun (curPath, curValue, res) field ->
|
|
521
|
+
match res with
|
|
522
|
+
| Some _ -> curPath, curValue, res
|
|
523
|
+
| None ->
|
|
524
|
+
if Helpers.isNullValue curValue then
|
|
525
|
+
let res =
|
|
526
|
+
badPathError fieldNames (Some curPath) firstValue
|
|
527
|
+
|
|
528
|
+
curPath, curValue, Some res
|
|
529
|
+
elif Helpers.isObject curValue then
|
|
530
|
+
let curValue = Helpers.getField field curValue
|
|
531
|
+
|
|
532
|
+
if Helpers.isUndefined curValue then
|
|
533
|
+
let res = badPathError fieldNames None firstValue
|
|
534
|
+
curPath, curValue, Some res
|
|
535
|
+
else
|
|
536
|
+
curPath + "." + field, curValue, None
|
|
537
|
+
else
|
|
538
|
+
let res =
|
|
539
|
+
Error(curPath, BadType("an object", curValue))
|
|
540
|
+
|
|
541
|
+
curPath, curValue, Some res
|
|
542
|
+
)
|
|
543
|
+
|> function
|
|
544
|
+
| _, _, Some res -> res
|
|
545
|
+
| lastPath, lastValue, None -> decoder lastPath lastValue
|
|
546
|
+
|
|
547
|
+
let index
|
|
548
|
+
(requestedIndex: int)
|
|
549
|
+
(decoder: Decoder<'value>)
|
|
550
|
+
: Decoder<'value>
|
|
551
|
+
=
|
|
552
|
+
fun path value ->
|
|
553
|
+
let currentPath =
|
|
554
|
+
path + ".[" + (Operators.string requestedIndex) + "]"
|
|
555
|
+
|
|
556
|
+
if Helpers.isArray value then
|
|
557
|
+
let vArray = Helpers.asArray value
|
|
558
|
+
|
|
559
|
+
if requestedIndex < vArray.Length then
|
|
560
|
+
decoder currentPath (vArray.[requestedIndex])
|
|
561
|
+
else
|
|
562
|
+
let msg =
|
|
563
|
+
"a longer array. Need index `"
|
|
564
|
+
+ (requestedIndex.ToString())
|
|
565
|
+
+ "` but there are only `"
|
|
566
|
+
+ (vArray.Length.ToString())
|
|
567
|
+
+ "` entries"
|
|
568
|
+
|
|
569
|
+
(currentPath, TooSmallArray(msg, value)) |> Error
|
|
570
|
+
else
|
|
571
|
+
(currentPath, BadPrimitive("an array", value)) |> Error
|
|
572
|
+
|
|
573
|
+
let option (decoder: Decoder<'value>) : Decoder<'value option> =
|
|
574
|
+
fun path value ->
|
|
575
|
+
if Helpers.isNullValue value then
|
|
576
|
+
Ok None
|
|
577
|
+
else
|
|
578
|
+
decoder path value |> Result.map Some
|
|
579
|
+
|
|
580
|
+
//////////////////////
|
|
581
|
+
// Data structure ///
|
|
582
|
+
////////////////////
|
|
583
|
+
|
|
584
|
+
let list (decoder: Decoder<'value>) : Decoder<'value list> =
|
|
585
|
+
fun path value ->
|
|
586
|
+
if Helpers.isArray value then
|
|
587
|
+
let mutable i = -1
|
|
588
|
+
let tokens = Helpers.asArray value
|
|
589
|
+
|
|
590
|
+
(Ok [], tokens)
|
|
591
|
+
||> Array.fold (fun acc value ->
|
|
592
|
+
i <- i + 1
|
|
593
|
+
|
|
594
|
+
match acc with
|
|
595
|
+
| Error _ -> acc
|
|
596
|
+
| Ok acc ->
|
|
597
|
+
match
|
|
598
|
+
decoder (path + ".[" + (i.ToString()) + "]") value
|
|
599
|
+
with
|
|
600
|
+
| Error er -> Error er
|
|
601
|
+
| Ok value -> Ok(value :: acc)
|
|
602
|
+
)
|
|
603
|
+
|> Result.map List.rev
|
|
604
|
+
else
|
|
605
|
+
(path, BadPrimitive("a list", value)) |> Error
|
|
606
|
+
|
|
607
|
+
let seq (decoder: Decoder<'value>) : Decoder<'value seq> =
|
|
608
|
+
fun path value ->
|
|
609
|
+
if Helpers.isArray value then
|
|
610
|
+
let mutable i = -1
|
|
611
|
+
let tokens = Helpers.asArray value
|
|
612
|
+
|
|
613
|
+
(Ok(seq []), tokens)
|
|
614
|
+
||> Array.fold (fun acc value ->
|
|
615
|
+
i <- i + 1
|
|
616
|
+
|
|
617
|
+
match acc with
|
|
618
|
+
| Error _ -> acc
|
|
619
|
+
| Ok acc ->
|
|
620
|
+
match
|
|
621
|
+
decoder (path + ".[" + (i.ToString()) + "]") value
|
|
622
|
+
with
|
|
623
|
+
| Error er -> Error er
|
|
624
|
+
| Ok value -> Ok(Seq.append [ value ] acc)
|
|
625
|
+
)
|
|
626
|
+
|> Result.map Seq.rev
|
|
627
|
+
else
|
|
628
|
+
(path, BadPrimitive("a seq", value)) |> Error
|
|
629
|
+
|
|
630
|
+
let array (decoder: Decoder<'value>) : Decoder<'value array> =
|
|
631
|
+
fun path value ->
|
|
632
|
+
if Helpers.isArray value then
|
|
633
|
+
let mutable i = -1
|
|
634
|
+
let tokens = Helpers.asArray value
|
|
635
|
+
let arr = Array.zeroCreate tokens.Length
|
|
636
|
+
|
|
637
|
+
(Ok arr, tokens)
|
|
638
|
+
||> Array.fold (fun acc value ->
|
|
639
|
+
i <- i + 1
|
|
640
|
+
|
|
641
|
+
match acc with
|
|
642
|
+
| Error _ -> acc
|
|
643
|
+
| Ok acc ->
|
|
644
|
+
match
|
|
645
|
+
decoder (path + ".[" + (i.ToString()) + "]") value
|
|
646
|
+
with
|
|
647
|
+
| Error er -> Error er
|
|
648
|
+
| Ok value ->
|
|
649
|
+
acc.[i] <- value
|
|
650
|
+
Ok acc
|
|
651
|
+
)
|
|
652
|
+
else
|
|
653
|
+
(path, BadPrimitive("an array", value)) |> Error
|
|
654
|
+
|
|
655
|
+
let keys: Decoder<string list> =
|
|
656
|
+
fun path value ->
|
|
657
|
+
if Helpers.isObject value then
|
|
658
|
+
Helpers.objectKeys value |> List.ofSeq |> Ok
|
|
659
|
+
else
|
|
660
|
+
(path, BadPrimitive("an object", value)) |> Error
|
|
661
|
+
|
|
662
|
+
let keyValuePairs
|
|
663
|
+
(decoder: Decoder<'value>)
|
|
664
|
+
: Decoder<(string * 'value) list>
|
|
665
|
+
=
|
|
666
|
+
fun path value ->
|
|
667
|
+
match keys path value with
|
|
668
|
+
| Ok objectKeys ->
|
|
669
|
+
(Ok [], objectKeys)
|
|
670
|
+
||> List.fold (fun acc prop ->
|
|
671
|
+
match acc with
|
|
672
|
+
| Error _ -> acc
|
|
673
|
+
| Ok acc ->
|
|
674
|
+
match Helpers.getField prop value |> decoder path with
|
|
675
|
+
| Error er -> Error er
|
|
676
|
+
| Ok value -> (prop, value) :: acc |> Ok
|
|
677
|
+
)
|
|
678
|
+
|> Result.map List.rev
|
|
679
|
+
| Error e -> Error e
|
|
680
|
+
|
|
681
|
+
//////////////////////////////
|
|
682
|
+
// Inconsistent Structure ///
|
|
683
|
+
////////////////////////////
|
|
684
|
+
|
|
685
|
+
let oneOf (decoders: Decoder<'value> list) : Decoder<'value> =
|
|
686
|
+
fun path value ->
|
|
687
|
+
let rec runner
|
|
688
|
+
(decoders: Decoder<'value> list)
|
|
689
|
+
(errors: string list)
|
|
690
|
+
=
|
|
691
|
+
match decoders with
|
|
692
|
+
| head :: tail ->
|
|
693
|
+
match fromValue path head value with
|
|
694
|
+
| Ok v -> Ok v
|
|
695
|
+
| Error error -> runner tail (errors @ [ error ])
|
|
696
|
+
| [] -> (path, BadOneOf errors) |> Error
|
|
697
|
+
|
|
698
|
+
runner decoders []
|
|
699
|
+
|
|
700
|
+
//////////////////////
|
|
701
|
+
// Fancy decoding ///
|
|
702
|
+
////////////////////
|
|
703
|
+
|
|
704
|
+
let nil (output: 'a) : Decoder<'a> =
|
|
705
|
+
fun path value ->
|
|
706
|
+
if Helpers.isNullValue value then
|
|
707
|
+
Ok output
|
|
708
|
+
else
|
|
709
|
+
(path, BadPrimitive("null", value)) |> Error
|
|
710
|
+
|
|
711
|
+
let value _ v = Ok v
|
|
712
|
+
|
|
713
|
+
let succeed (output: 'a) : Decoder<'a> = fun _ _ -> Ok output
|
|
714
|
+
|
|
715
|
+
let fail (msg: string) : Decoder<'a> =
|
|
716
|
+
fun path _ -> (path, FailMessage msg) |> Error
|
|
717
|
+
|
|
718
|
+
let andThen (cb: 'a -> Decoder<'b>) (decoder: Decoder<'a>) : Decoder<'b> =
|
|
719
|
+
fun path value ->
|
|
720
|
+
match decoder path value with
|
|
721
|
+
| Error error -> Error error
|
|
722
|
+
| Ok result -> cb result path value
|
|
723
|
+
|
|
724
|
+
let all (decoders: Decoder<'a> list) : Decoder<'a list> =
|
|
725
|
+
fun path value ->
|
|
726
|
+
let rec runner (decoders: Decoder<'a> list) (values: 'a list) =
|
|
727
|
+
match decoders with
|
|
728
|
+
| decoder :: tail ->
|
|
729
|
+
match decoder path value with
|
|
730
|
+
| Ok value -> runner tail (values @ [ value ])
|
|
731
|
+
| Error error -> Error error
|
|
732
|
+
| [] -> Ok values
|
|
733
|
+
|
|
734
|
+
runner decoders []
|
|
735
|
+
|
|
736
|
+
/////////////////////
|
|
737
|
+
// Map functions ///
|
|
738
|
+
///////////////////
|
|
739
|
+
|
|
740
|
+
let map (ctor: 'a -> 'value) (d1: Decoder<'a>) : Decoder<'value> =
|
|
741
|
+
fun path value ->
|
|
742
|
+
match d1 path value with
|
|
743
|
+
| Ok v1 -> Ok(ctor v1)
|
|
744
|
+
| Error er -> Error er
|
|
745
|
+
|
|
746
|
+
let map2
|
|
747
|
+
(ctor: 'a -> 'b -> 'value)
|
|
748
|
+
(d1: Decoder<'a>)
|
|
749
|
+
(d2: Decoder<'b>)
|
|
750
|
+
: Decoder<'value>
|
|
751
|
+
=
|
|
752
|
+
fun path value ->
|
|
753
|
+
match d1 path value, d2 path value with
|
|
754
|
+
| Ok v1, Ok v2 -> Ok(ctor v1 v2)
|
|
755
|
+
| Error er, _ -> Error er
|
|
756
|
+
| _, Error er -> Error er
|
|
757
|
+
|
|
758
|
+
let map3
|
|
759
|
+
(ctor: 'a -> 'b -> 'c -> 'value)
|
|
760
|
+
(d1: Decoder<'a>)
|
|
761
|
+
(d2: Decoder<'b>)
|
|
762
|
+
(d3: Decoder<'c>)
|
|
763
|
+
: Decoder<'value>
|
|
764
|
+
=
|
|
765
|
+
fun path value ->
|
|
766
|
+
match d1 path value, d2 path value, d3 path value with
|
|
767
|
+
| Ok v1, Ok v2, Ok v3 -> Ok(ctor v1 v2 v3)
|
|
768
|
+
| Error er, _, _ -> Error er
|
|
769
|
+
| _, Error er, _ -> Error er
|
|
770
|
+
| _, _, Error er -> Error er
|
|
771
|
+
|
|
772
|
+
let map4
|
|
773
|
+
(ctor: 'a -> 'b -> 'c -> 'd -> 'value)
|
|
774
|
+
(d1: Decoder<'a>)
|
|
775
|
+
(d2: Decoder<'b>)
|
|
776
|
+
(d3: Decoder<'c>)
|
|
777
|
+
(d4: Decoder<'d>)
|
|
778
|
+
: Decoder<'value>
|
|
779
|
+
=
|
|
780
|
+
fun path value ->
|
|
781
|
+
match
|
|
782
|
+
d1 path value, d2 path value, d3 path value, d4 path value
|
|
783
|
+
with
|
|
784
|
+
| Ok v1, Ok v2, Ok v3, Ok v4 -> Ok(ctor v1 v2 v3 v4)
|
|
785
|
+
| Error er, _, _, _ -> Error er
|
|
786
|
+
| _, Error er, _, _ -> Error er
|
|
787
|
+
| _, _, Error er, _ -> Error er
|
|
788
|
+
| _, _, _, Error er -> Error er
|
|
789
|
+
|
|
790
|
+
let map5
|
|
791
|
+
(ctor: 'a -> 'b -> 'c -> 'd -> 'e -> 'value)
|
|
792
|
+
(d1: Decoder<'a>)
|
|
793
|
+
(d2: Decoder<'b>)
|
|
794
|
+
(d3: Decoder<'c>)
|
|
795
|
+
(d4: Decoder<'d>)
|
|
796
|
+
(d5: Decoder<'e>)
|
|
797
|
+
: Decoder<'value>
|
|
798
|
+
=
|
|
799
|
+
fun path value ->
|
|
800
|
+
match
|
|
801
|
+
d1 path value,
|
|
802
|
+
d2 path value,
|
|
803
|
+
d3 path value,
|
|
804
|
+
d4 path value,
|
|
805
|
+
d5 path value
|
|
806
|
+
with
|
|
807
|
+
| Ok v1, Ok v2, Ok v3, Ok v4, Ok v5 -> Ok(ctor v1 v2 v3 v4 v5)
|
|
808
|
+
| Error er, _, _, _, _ -> Error er
|
|
809
|
+
| _, Error er, _, _, _ -> Error er
|
|
810
|
+
| _, _, Error er, _, _ -> Error er
|
|
811
|
+
| _, _, _, Error er, _ -> Error er
|
|
812
|
+
| _, _, _, _, Error er -> Error er
|
|
813
|
+
|
|
814
|
+
let map6
|
|
815
|
+
(ctor: 'a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'value)
|
|
816
|
+
(d1: Decoder<'a>)
|
|
817
|
+
(d2: Decoder<'b>)
|
|
818
|
+
(d3: Decoder<'c>)
|
|
819
|
+
(d4: Decoder<'d>)
|
|
820
|
+
(d5: Decoder<'e>)
|
|
821
|
+
(d6: Decoder<'f>)
|
|
822
|
+
: Decoder<'value>
|
|
823
|
+
=
|
|
824
|
+
fun path value ->
|
|
825
|
+
match
|
|
826
|
+
d1 path value,
|
|
827
|
+
d2 path value,
|
|
828
|
+
d3 path value,
|
|
829
|
+
d4 path value,
|
|
830
|
+
d5 path value,
|
|
831
|
+
d6 path value
|
|
832
|
+
with
|
|
833
|
+
| Ok v1, Ok v2, Ok v3, Ok v4, Ok v5, Ok v6 ->
|
|
834
|
+
Ok(ctor v1 v2 v3 v4 v5 v6)
|
|
835
|
+
| Error er, _, _, _, _, _ -> Error er
|
|
836
|
+
| _, Error er, _, _, _, _ -> Error er
|
|
837
|
+
| _, _, Error er, _, _, _ -> Error er
|
|
838
|
+
| _, _, _, Error er, _, _ -> Error er
|
|
839
|
+
| _, _, _, _, Error er, _ -> Error er
|
|
840
|
+
| _, _, _, _, _, Error er -> Error er
|
|
841
|
+
|
|
842
|
+
let map7
|
|
843
|
+
(ctor: 'a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'value)
|
|
844
|
+
(d1: Decoder<'a>)
|
|
845
|
+
(d2: Decoder<'b>)
|
|
846
|
+
(d3: Decoder<'c>)
|
|
847
|
+
(d4: Decoder<'d>)
|
|
848
|
+
(d5: Decoder<'e>)
|
|
849
|
+
(d6: Decoder<'f>)
|
|
850
|
+
(d7: Decoder<'g>)
|
|
851
|
+
: Decoder<'value>
|
|
852
|
+
=
|
|
853
|
+
fun path value ->
|
|
854
|
+
match
|
|
855
|
+
d1 path value,
|
|
856
|
+
d2 path value,
|
|
857
|
+
d3 path value,
|
|
858
|
+
d4 path value,
|
|
859
|
+
d5 path value,
|
|
860
|
+
d6 path value,
|
|
861
|
+
d7 path value
|
|
862
|
+
with
|
|
863
|
+
| Ok v1, Ok v2, Ok v3, Ok v4, Ok v5, Ok v6, Ok v7 ->
|
|
864
|
+
Ok(ctor v1 v2 v3 v4 v5 v6 v7)
|
|
865
|
+
| Error er, _, _, _, _, _, _ -> Error er
|
|
866
|
+
| _, Error er, _, _, _, _, _ -> Error er
|
|
867
|
+
| _, _, Error er, _, _, _, _ -> Error er
|
|
868
|
+
| _, _, _, Error er, _, _, _ -> Error er
|
|
869
|
+
| _, _, _, _, Error er, _, _ -> Error er
|
|
870
|
+
| _, _, _, _, _, Error er, _ -> Error er
|
|
871
|
+
| _, _, _, _, _, _, Error er -> Error er
|
|
872
|
+
|
|
873
|
+
let map8
|
|
874
|
+
(ctor: 'a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'h -> 'value)
|
|
875
|
+
(d1: Decoder<'a>)
|
|
876
|
+
(d2: Decoder<'b>)
|
|
877
|
+
(d3: Decoder<'c>)
|
|
878
|
+
(d4: Decoder<'d>)
|
|
879
|
+
(d5: Decoder<'e>)
|
|
880
|
+
(d6: Decoder<'f>)
|
|
881
|
+
(d7: Decoder<'g>)
|
|
882
|
+
(d8: Decoder<'h>)
|
|
883
|
+
: Decoder<'value>
|
|
884
|
+
=
|
|
885
|
+
fun path value ->
|
|
886
|
+
match
|
|
887
|
+
d1 path value,
|
|
888
|
+
d2 path value,
|
|
889
|
+
d3 path value,
|
|
890
|
+
d4 path value,
|
|
891
|
+
d5 path value,
|
|
892
|
+
d6 path value,
|
|
893
|
+
d7 path value,
|
|
894
|
+
d8 path value
|
|
895
|
+
with
|
|
896
|
+
| Ok v1, Ok v2, Ok v3, Ok v4, Ok v5, Ok v6, Ok v7, Ok v8 ->
|
|
897
|
+
Ok(ctor v1 v2 v3 v4 v5 v6 v7 v8)
|
|
898
|
+
| Error er, _, _, _, _, _, _, _ -> Error er
|
|
899
|
+
| _, Error er, _, _, _, _, _, _ -> Error er
|
|
900
|
+
| _, _, Error er, _, _, _, _, _ -> Error er
|
|
901
|
+
| _, _, _, Error er, _, _, _, _ -> Error er
|
|
902
|
+
| _, _, _, _, Error er, _, _, _ -> Error er
|
|
903
|
+
| _, _, _, _, _, Error er, _, _ -> Error er
|
|
904
|
+
| _, _, _, _, _, _, Error er, _ -> Error er
|
|
905
|
+
| _, _, _, _, _, _, _, Error er -> Error er
|
|
906
|
+
|
|
907
|
+
//////////////////////
|
|
908
|
+
// Object builder ///
|
|
909
|
+
////////////////////
|
|
910
|
+
|
|
911
|
+
/// <summary>
|
|
912
|
+
/// Allow to incrementally apply a decoder, for building large objects.
|
|
913
|
+
/// </summary>
|
|
914
|
+
/// <example>
|
|
915
|
+
/// <code lang="fsharp">
|
|
916
|
+
/// type Point =
|
|
917
|
+
/// {
|
|
918
|
+
/// X : float
|
|
919
|
+
/// Y : float
|
|
920
|
+
/// }
|
|
921
|
+
///
|
|
922
|
+
/// module Point =
|
|
923
|
+
/// let create x y = { X = x; Y = y }
|
|
924
|
+
///
|
|
925
|
+
/// let decode =
|
|
926
|
+
/// Decode.succeed create
|
|
927
|
+
/// |> Decode.andMap (Decode.field "x" Decode.float)
|
|
928
|
+
/// |> Decode.andMap (Decode.field "y" Decode.float)
|
|
929
|
+
/// </code>
|
|
930
|
+
/// </example>
|
|
931
|
+
let andMap<'a, 'b> : 'a Decoder -> ('a -> 'b) Decoder -> 'b Decoder =
|
|
932
|
+
map2 (|>)
|
|
933
|
+
|
|
934
|
+
type IRequiredGetter =
|
|
935
|
+
abstract Field: string -> Decoder<'a> -> 'a
|
|
936
|
+
abstract At: List<string> -> Decoder<'a> -> 'a
|
|
937
|
+
abstract Raw: Decoder<'a> -> 'a
|
|
938
|
+
|
|
939
|
+
type IOptionalGetter =
|
|
940
|
+
abstract Field: string -> Decoder<'a> -> 'a option
|
|
941
|
+
abstract At: List<string> -> Decoder<'a> -> 'a option
|
|
942
|
+
abstract Raw: Decoder<'a> -> 'a option
|
|
943
|
+
|
|
944
|
+
type IGetters =
|
|
945
|
+
abstract Required: IRequiredGetter
|
|
946
|
+
abstract Optional: IOptionalGetter
|
|
947
|
+
|
|
948
|
+
let private unwrapWith
|
|
949
|
+
(errors: ResizeArray<DecoderError>)
|
|
950
|
+
path
|
|
951
|
+
(decoder: Decoder<'T>)
|
|
952
|
+
value
|
|
953
|
+
: 'T
|
|
954
|
+
=
|
|
955
|
+
match decoder path value with
|
|
956
|
+
| Ok v -> v
|
|
957
|
+
| Error er ->
|
|
958
|
+
errors.Add(er)
|
|
959
|
+
Unchecked.defaultof<'T>
|
|
960
|
+
|
|
961
|
+
type Getters<'T>(path: string, v: 'T) =
|
|
962
|
+
let mutable errors = ResizeArray<DecoderError>()
|
|
963
|
+
|
|
964
|
+
let required =
|
|
965
|
+
{ new IRequiredGetter with
|
|
966
|
+
member __.Field (fieldName: string) (decoder: Decoder<_>) =
|
|
967
|
+
unwrapWith errors path (field fieldName decoder) v
|
|
968
|
+
|
|
969
|
+
member __.At (fieldNames: string list) (decoder: Decoder<_>) =
|
|
970
|
+
unwrapWith errors path (at fieldNames decoder) v
|
|
971
|
+
|
|
972
|
+
member __.Raw(decoder: Decoder<_>) =
|
|
973
|
+
unwrapWith errors path decoder v
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
let optional =
|
|
977
|
+
{ new IOptionalGetter with
|
|
978
|
+
member __.Field (fieldName: string) (decoder: Decoder<_>) =
|
|
979
|
+
unwrapWith errors path (optional fieldName decoder) v
|
|
980
|
+
|
|
981
|
+
member __.At (fieldNames: string list) (decoder: Decoder<_>) =
|
|
982
|
+
unwrapWith errors path (optionalAt fieldNames decoder) v
|
|
983
|
+
|
|
984
|
+
member __.Raw(decoder: Decoder<_>) =
|
|
985
|
+
match decoder path v with
|
|
986
|
+
| Ok v -> Some v
|
|
987
|
+
| Error((_, reason) as error) ->
|
|
988
|
+
match reason with
|
|
989
|
+
| BadPrimitive(_, v)
|
|
990
|
+
| BadPrimitiveExtra(_, v, _)
|
|
991
|
+
| BadType(_, v) ->
|
|
992
|
+
if Helpers.isNullValue v then
|
|
993
|
+
None
|
|
994
|
+
else
|
|
995
|
+
errors.Add(error)
|
|
996
|
+
Unchecked.defaultof<_>
|
|
997
|
+
| BadField _
|
|
998
|
+
| BadPath _ -> None
|
|
999
|
+
| TooSmallArray _
|
|
1000
|
+
| FailMessage _
|
|
1001
|
+
| BadOneOf _ ->
|
|
1002
|
+
errors.Add(error)
|
|
1003
|
+
Unchecked.defaultof<_>
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
member __.Errors: _ list = Seq.toList errors
|
|
1007
|
+
|
|
1008
|
+
interface IGetters with
|
|
1009
|
+
member __.Required = required
|
|
1010
|
+
member __.Optional = optional
|
|
1011
|
+
|
|
1012
|
+
let object (builder: IGetters -> 'value) : Decoder<'value> =
|
|
1013
|
+
fun path v ->
|
|
1014
|
+
let getters = Getters(path, v)
|
|
1015
|
+
let result = builder getters
|
|
1016
|
+
|
|
1017
|
+
match getters.Errors with
|
|
1018
|
+
| [] -> Ok result
|
|
1019
|
+
| fst :: _ as errors ->
|
|
1020
|
+
if errors.Length > 1 then
|
|
1021
|
+
let errors = List.map errorToString errors
|
|
1022
|
+
(path, BadOneOf errors) |> Error
|
|
1023
|
+
else
|
|
1024
|
+
Error fst
|
|
1025
|
+
|
|
1026
|
+
///////////////////////
|
|
1027
|
+
// Tuples decoders ///
|
|
1028
|
+
////////////////////
|
|
1029
|
+
|
|
1030
|
+
let tuple2
|
|
1031
|
+
(decoder1: Decoder<'T1>)
|
|
1032
|
+
(decoder2: Decoder<'T2>)
|
|
1033
|
+
: Decoder<'T1 * 'T2>
|
|
1034
|
+
=
|
|
1035
|
+
index 0 decoder1
|
|
1036
|
+
|> andThen (fun v1 ->
|
|
1037
|
+
index 1 decoder2 |> andThen (fun v2 -> succeed (v1, v2))
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
let tuple3
|
|
1041
|
+
(decoder1: Decoder<'T1>)
|
|
1042
|
+
(decoder2: Decoder<'T2>)
|
|
1043
|
+
(decoder3: Decoder<'T3>)
|
|
1044
|
+
: Decoder<'T1 * 'T2 * 'T3>
|
|
1045
|
+
=
|
|
1046
|
+
index 0 decoder1
|
|
1047
|
+
|> andThen (fun v1 ->
|
|
1048
|
+
index 1 decoder2
|
|
1049
|
+
|> andThen (fun v2 ->
|
|
1050
|
+
index 2 decoder3 |> andThen (fun v3 -> succeed (v1, v2, v3))
|
|
1051
|
+
)
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
let tuple4
|
|
1055
|
+
(decoder1: Decoder<'T1>)
|
|
1056
|
+
(decoder2: Decoder<'T2>)
|
|
1057
|
+
(decoder3: Decoder<'T3>)
|
|
1058
|
+
(decoder4: Decoder<'T4>)
|
|
1059
|
+
: Decoder<'T1 * 'T2 * 'T3 * 'T4>
|
|
1060
|
+
=
|
|
1061
|
+
index 0 decoder1
|
|
1062
|
+
|> andThen (fun v1 ->
|
|
1063
|
+
index 1 decoder2
|
|
1064
|
+
|> andThen (fun v2 ->
|
|
1065
|
+
index 2 decoder3
|
|
1066
|
+
|> andThen (fun v3 ->
|
|
1067
|
+
index 3 decoder4
|
|
1068
|
+
|> andThen (fun v4 -> succeed (v1, v2, v3, v4))
|
|
1069
|
+
)
|
|
1070
|
+
)
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
let tuple5
|
|
1074
|
+
(decoder1: Decoder<'T1>)
|
|
1075
|
+
(decoder2: Decoder<'T2>)
|
|
1076
|
+
(decoder3: Decoder<'T3>)
|
|
1077
|
+
(decoder4: Decoder<'T4>)
|
|
1078
|
+
(decoder5: Decoder<'T5>)
|
|
1079
|
+
: Decoder<'T1 * 'T2 * 'T3 * 'T4 * 'T5>
|
|
1080
|
+
=
|
|
1081
|
+
index 0 decoder1
|
|
1082
|
+
|> andThen (fun v1 ->
|
|
1083
|
+
index 1 decoder2
|
|
1084
|
+
|> andThen (fun v2 ->
|
|
1085
|
+
index 2 decoder3
|
|
1086
|
+
|> andThen (fun v3 ->
|
|
1087
|
+
index 3 decoder4
|
|
1088
|
+
|> andThen (fun v4 ->
|
|
1089
|
+
index 4 decoder5
|
|
1090
|
+
|> andThen (fun v5 -> succeed (v1, v2, v3, v4, v5))
|
|
1091
|
+
)
|
|
1092
|
+
)
|
|
1093
|
+
)
|
|
1094
|
+
)
|
|
1095
|
+
|
|
1096
|
+
let tuple6
|
|
1097
|
+
(decoder1: Decoder<'T1>)
|
|
1098
|
+
(decoder2: Decoder<'T2>)
|
|
1099
|
+
(decoder3: Decoder<'T3>)
|
|
1100
|
+
(decoder4: Decoder<'T4>)
|
|
1101
|
+
(decoder5: Decoder<'T5>)
|
|
1102
|
+
(decoder6: Decoder<'T6>)
|
|
1103
|
+
: Decoder<'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6>
|
|
1104
|
+
=
|
|
1105
|
+
index 0 decoder1
|
|
1106
|
+
|> andThen (fun v1 ->
|
|
1107
|
+
index 1 decoder2
|
|
1108
|
+
|> andThen (fun v2 ->
|
|
1109
|
+
index 2 decoder3
|
|
1110
|
+
|> andThen (fun v3 ->
|
|
1111
|
+
index 3 decoder4
|
|
1112
|
+
|> andThen (fun v4 ->
|
|
1113
|
+
index 4 decoder5
|
|
1114
|
+
|> andThen (fun v5 ->
|
|
1115
|
+
index 5 decoder6
|
|
1116
|
+
|> andThen (fun v6 ->
|
|
1117
|
+
succeed (v1, v2, v3, v4, v5, v6)
|
|
1118
|
+
)
|
|
1119
|
+
)
|
|
1120
|
+
)
|
|
1121
|
+
)
|
|
1122
|
+
)
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
let tuple7
|
|
1126
|
+
(decoder1: Decoder<'T1>)
|
|
1127
|
+
(decoder2: Decoder<'T2>)
|
|
1128
|
+
(decoder3: Decoder<'T3>)
|
|
1129
|
+
(decoder4: Decoder<'T4>)
|
|
1130
|
+
(decoder5: Decoder<'T5>)
|
|
1131
|
+
(decoder6: Decoder<'T6>)
|
|
1132
|
+
(decoder7: Decoder<'T7>)
|
|
1133
|
+
: Decoder<'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7>
|
|
1134
|
+
=
|
|
1135
|
+
index 0 decoder1
|
|
1136
|
+
|> andThen (fun v1 ->
|
|
1137
|
+
index 1 decoder2
|
|
1138
|
+
|> andThen (fun v2 ->
|
|
1139
|
+
index 2 decoder3
|
|
1140
|
+
|> andThen (fun v3 ->
|
|
1141
|
+
index 3 decoder4
|
|
1142
|
+
|> andThen (fun v4 ->
|
|
1143
|
+
index 4 decoder5
|
|
1144
|
+
|> andThen (fun v5 ->
|
|
1145
|
+
index 5 decoder6
|
|
1146
|
+
|> andThen (fun v6 ->
|
|
1147
|
+
index 6 decoder7
|
|
1148
|
+
|> andThen (fun v7 ->
|
|
1149
|
+
succeed (v1, v2, v3, v4, v5, v6, v7)
|
|
1150
|
+
)
|
|
1151
|
+
)
|
|
1152
|
+
)
|
|
1153
|
+
)
|
|
1154
|
+
)
|
|
1155
|
+
)
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
let tuple8
|
|
1159
|
+
(decoder1: Decoder<'T1>)
|
|
1160
|
+
(decoder2: Decoder<'T2>)
|
|
1161
|
+
(decoder3: Decoder<'T3>)
|
|
1162
|
+
(decoder4: Decoder<'T4>)
|
|
1163
|
+
(decoder5: Decoder<'T5>)
|
|
1164
|
+
(decoder6: Decoder<'T6>)
|
|
1165
|
+
(decoder7: Decoder<'T7>)
|
|
1166
|
+
(decoder8: Decoder<'T8>)
|
|
1167
|
+
: Decoder<'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8>
|
|
1168
|
+
=
|
|
1169
|
+
index 0 decoder1
|
|
1170
|
+
|> andThen (fun v1 ->
|
|
1171
|
+
index 1 decoder2
|
|
1172
|
+
|> andThen (fun v2 ->
|
|
1173
|
+
index 2 decoder3
|
|
1174
|
+
|> andThen (fun v3 ->
|
|
1175
|
+
index 3 decoder4
|
|
1176
|
+
|> andThen (fun v4 ->
|
|
1177
|
+
index 4 decoder5
|
|
1178
|
+
|> andThen (fun v5 ->
|
|
1179
|
+
index 5 decoder6
|
|
1180
|
+
|> andThen (fun v6 ->
|
|
1181
|
+
index 6 decoder7
|
|
1182
|
+
|> andThen (fun v7 ->
|
|
1183
|
+
index 7 decoder8
|
|
1184
|
+
|> andThen (fun v8 ->
|
|
1185
|
+
succeed (
|
|
1186
|
+
v1,
|
|
1187
|
+
v2,
|
|
1188
|
+
v3,
|
|
1189
|
+
v4,
|
|
1190
|
+
v5,
|
|
1191
|
+
v6,
|
|
1192
|
+
v7,
|
|
1193
|
+
v8
|
|
1194
|
+
)
|
|
1195
|
+
)
|
|
1196
|
+
)
|
|
1197
|
+
)
|
|
1198
|
+
)
|
|
1199
|
+
)
|
|
1200
|
+
)
|
|
1201
|
+
)
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
///////////
|
|
1205
|
+
// Map ///
|
|
1206
|
+
/////////
|
|
1207
|
+
|
|
1208
|
+
let dict (decoder: Decoder<'value>) : Decoder<Map<string, 'value>> =
|
|
1209
|
+
map Map.ofList (keyValuePairs decoder)
|
|
1210
|
+
|
|
1211
|
+
let map'
|
|
1212
|
+
(keyDecoder: Decoder<'key>)
|
|
1213
|
+
(valueDecoder: Decoder<'value>)
|
|
1214
|
+
: Decoder<Map<'key, 'value>>
|
|
1215
|
+
=
|
|
1216
|
+
map Map.ofSeq (array (tuple2 keyDecoder valueDecoder))
|
|
1217
|
+
|
|
1218
|
+
////////////
|
|
1219
|
+
// Enum ///
|
|
1220
|
+
/////////
|
|
1221
|
+
|
|
1222
|
+
#if !FABLE_REPL_LIB
|
|
1223
|
+
module Enum =
|
|
1224
|
+
|
|
1225
|
+
let inline byte<'TEnum when 'TEnum: enum<byte>> : Decoder<'TEnum> =
|
|
1226
|
+
byte
|
|
1227
|
+
|> andThen (fun value ->
|
|
1228
|
+
LanguagePrimitives.EnumOfValue<byte, 'TEnum> value |> succeed
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
let inline sbyte<'TEnum when 'TEnum: enum<sbyte>> : Decoder<'TEnum> =
|
|
1232
|
+
sbyte
|
|
1233
|
+
|> andThen (fun value ->
|
|
1234
|
+
LanguagePrimitives.EnumOfValue<sbyte, 'TEnum> value |> succeed
|
|
1235
|
+
)
|
|
1236
|
+
|
|
1237
|
+
let inline int16<'TEnum when 'TEnum: enum<int16>> : Decoder<'TEnum> =
|
|
1238
|
+
int16
|
|
1239
|
+
|> andThen (fun value ->
|
|
1240
|
+
LanguagePrimitives.EnumOfValue<int16, 'TEnum> value |> succeed
|
|
1241
|
+
)
|
|
1242
|
+
|
|
1243
|
+
let inline uint16<'TEnum when 'TEnum: enum<uint16>> : Decoder<'TEnum> =
|
|
1244
|
+
uint16
|
|
1245
|
+
|> andThen (fun value ->
|
|
1246
|
+
LanguagePrimitives.EnumOfValue<uint16, 'TEnum> value |> succeed
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
let inline int<'TEnum when 'TEnum: enum<int>> : Decoder<'TEnum> =
|
|
1250
|
+
int
|
|
1251
|
+
|> andThen (fun value ->
|
|
1252
|
+
LanguagePrimitives.EnumOfValue<int, 'TEnum> value |> succeed
|
|
1253
|
+
)
|
|
1254
|
+
|
|
1255
|
+
let inline uint32<'TEnum when 'TEnum: enum<uint32>> : Decoder<'TEnum> =
|
|
1256
|
+
uint32
|
|
1257
|
+
|> andThen (fun value ->
|
|
1258
|
+
LanguagePrimitives.EnumOfValue<uint32, 'TEnum> value |> succeed
|
|
1259
|
+
)
|
|
1260
|
+
#endif
|
|
1261
|
+
|
|
1262
|
+
//////////////////
|
|
1263
|
+
// Reflection ///
|
|
1264
|
+
////////////////
|
|
1265
|
+
|
|
1266
|
+
open FSharp.Reflection
|
|
1267
|
+
|
|
1268
|
+
// As generics are erased by Fable, let's just do an unsafe cast for performance
|
|
1269
|
+
let inline boxDecoder (d: Decoder<'T>) : BoxedDecoder = !!d // d >> Result.map box
|
|
1270
|
+
|
|
1271
|
+
let inline unboxDecoder (d: BoxedDecoder) : Decoder<'T> = !!d // d >> Result.map unbox
|
|
1272
|
+
|
|
1273
|
+
// This is used to force Fable use a generic comparer for map keys
|
|
1274
|
+
let private toMap<'key, 'value when 'key: comparison>
|
|
1275
|
+
(xs: ('key * 'value) seq)
|
|
1276
|
+
=
|
|
1277
|
+
Map.ofSeq xs
|
|
1278
|
+
|
|
1279
|
+
let private toSet<'key when 'key: comparison> (xs: 'key seq) = Set.ofSeq xs
|
|
1280
|
+
|
|
1281
|
+
let private autoObject
|
|
1282
|
+
(decoderInfos: (string * BoxedDecoder)[])
|
|
1283
|
+
(path: string)
|
|
1284
|
+
(value: JsonValue)
|
|
1285
|
+
=
|
|
1286
|
+
if not (Helpers.isObject value) then
|
|
1287
|
+
(path, BadPrimitive("an object", value)) |> Error
|
|
1288
|
+
else
|
|
1289
|
+
(decoderInfos, Ok [])
|
|
1290
|
+
||> Array.foldBack (fun (name, decoder) acc ->
|
|
1291
|
+
match acc with
|
|
1292
|
+
| Error _ -> acc
|
|
1293
|
+
| Ok result ->
|
|
1294
|
+
Helpers.getField name value
|
|
1295
|
+
|> decoder (path + "." + name)
|
|
1296
|
+
|> Result.map (fun v -> v :: result)
|
|
1297
|
+
)
|
|
1298
|
+
|
|
1299
|
+
let inline private enumDecoder<'UnderlineType when 'UnderlineType: equality>
|
|
1300
|
+
(decoder: Decoder<'UnderlineType>)
|
|
1301
|
+
(toString: 'UnderlineType -> string)
|
|
1302
|
+
(t: System.Type)
|
|
1303
|
+
=
|
|
1304
|
+
|
|
1305
|
+
fun path value ->
|
|
1306
|
+
match decoder path value with
|
|
1307
|
+
| Ok enumValue ->
|
|
1308
|
+
System.Enum.GetValues(t)
|
|
1309
|
+
|> Seq.cast<'UnderlineType>
|
|
1310
|
+
|> Seq.contains enumValue
|
|
1311
|
+
|> function
|
|
1312
|
+
| true -> System.Enum.Parse(t, toString enumValue) |> Ok
|
|
1313
|
+
| false ->
|
|
1314
|
+
(path,
|
|
1315
|
+
BadPrimitiveExtra(
|
|
1316
|
+
t.FullName,
|
|
1317
|
+
value,
|
|
1318
|
+
"Unkown value provided for the enum"
|
|
1319
|
+
))
|
|
1320
|
+
|> Error
|
|
1321
|
+
| Error msg -> Error msg
|
|
1322
|
+
|
|
1323
|
+
let private autoObject2
|
|
1324
|
+
(keyDecoder: BoxedDecoder)
|
|
1325
|
+
(valueDecoder: BoxedDecoder)
|
|
1326
|
+
(path: string)
|
|
1327
|
+
(value: JsonValue)
|
|
1328
|
+
=
|
|
1329
|
+
if not (Helpers.isObject value) then
|
|
1330
|
+
(path, BadPrimitive("an object", value)) |> Error
|
|
1331
|
+
else
|
|
1332
|
+
(Ok [], Helpers.objectKeys (value))
|
|
1333
|
+
||> Seq.fold (fun acc name ->
|
|
1334
|
+
match acc with
|
|
1335
|
+
| Error _ -> acc
|
|
1336
|
+
| Ok acc ->
|
|
1337
|
+
match keyDecoder path name with
|
|
1338
|
+
| Error er -> Error er
|
|
1339
|
+
| Ok k ->
|
|
1340
|
+
Helpers.getField name value
|
|
1341
|
+
|> valueDecoder (path + "." + name)
|
|
1342
|
+
|> function
|
|
1343
|
+
| Error er -> Error er
|
|
1344
|
+
| Ok v -> (k, v) :: acc |> Ok
|
|
1345
|
+
)
|
|
1346
|
+
|
|
1347
|
+
let private mixedArray
|
|
1348
|
+
offset
|
|
1349
|
+
(decoders: BoxedDecoder[])
|
|
1350
|
+
(path: string)
|
|
1351
|
+
(values: JsonValue[])
|
|
1352
|
+
: Result<JsonValue list, DecoderError>
|
|
1353
|
+
=
|
|
1354
|
+
let expectedLength = decoders.Length + offset
|
|
1355
|
+
|
|
1356
|
+
if expectedLength <> values.Length then
|
|
1357
|
+
(path,
|
|
1358
|
+
sprintf
|
|
1359
|
+
"Expected array of length %i but got %i"
|
|
1360
|
+
expectedLength
|
|
1361
|
+
values.Length
|
|
1362
|
+
|> FailMessage)
|
|
1363
|
+
|> Error
|
|
1364
|
+
else
|
|
1365
|
+
let mutable result = Ok []
|
|
1366
|
+
|
|
1367
|
+
for i = offset to values.Length - 1 do
|
|
1368
|
+
match result with
|
|
1369
|
+
| Error _ -> ()
|
|
1370
|
+
| Ok acc ->
|
|
1371
|
+
let path = sprintf "%s[%i]" path i
|
|
1372
|
+
let decoder = decoders.[i - offset]
|
|
1373
|
+
let value = values.[i]
|
|
1374
|
+
|
|
1375
|
+
result <-
|
|
1376
|
+
decoder path value |> Result.map (fun v -> v :: acc)
|
|
1377
|
+
|
|
1378
|
+
result |> Result.map List.rev
|
|
1379
|
+
|
|
1380
|
+
let rec private makeUnion
|
|
1381
|
+
extra
|
|
1382
|
+
caseStrategy
|
|
1383
|
+
t
|
|
1384
|
+
name
|
|
1385
|
+
(path: string)
|
|
1386
|
+
(values: JsonValue[])
|
|
1387
|
+
=
|
|
1388
|
+
let uci =
|
|
1389
|
+
FSharpType.GetUnionCases(
|
|
1390
|
+
t,
|
|
1391
|
+
allowAccessToPrivateRepresentation = true
|
|
1392
|
+
)
|
|
1393
|
+
|> Array.tryFind (fun x -> x.Name = name)
|
|
1394
|
+
|
|
1395
|
+
match uci with
|
|
1396
|
+
| None ->
|
|
1397
|
+
(path, FailMessage("Cannot find case " + name + " in " + t.FullName))
|
|
1398
|
+
|> Error
|
|
1399
|
+
| Some uci ->
|
|
1400
|
+
let decoders =
|
|
1401
|
+
uci.GetFields()
|
|
1402
|
+
|> Array.map (fun fi ->
|
|
1403
|
+
autoDecoder extra caseStrategy false fi.PropertyType
|
|
1404
|
+
)
|
|
1405
|
+
|
|
1406
|
+
let values =
|
|
1407
|
+
if
|
|
1408
|
+
decoders.Length = 0 && values.Length <= 1 // First item is the case name
|
|
1409
|
+
then
|
|
1410
|
+
Ok []
|
|
1411
|
+
else
|
|
1412
|
+
mixedArray 1 decoders path values
|
|
1413
|
+
|
|
1414
|
+
values
|
|
1415
|
+
|> Result.map (fun values ->
|
|
1416
|
+
FSharpValue.MakeUnion(
|
|
1417
|
+
uci,
|
|
1418
|
+
List.toArray values,
|
|
1419
|
+
allowAccessToPrivateRepresentation = true
|
|
1420
|
+
)
|
|
1421
|
+
)
|
|
1422
|
+
|
|
1423
|
+
and private autoDecodeRecordsAndUnions
|
|
1424
|
+
extra
|
|
1425
|
+
(caseStrategy: CaseStrategy)
|
|
1426
|
+
(isOptional: bool)
|
|
1427
|
+
(t: System.Type)
|
|
1428
|
+
: BoxedDecoder
|
|
1429
|
+
=
|
|
1430
|
+
// Add the decoder to extra in case one of the fields is recursive
|
|
1431
|
+
let decoderRef = ref Unchecked.defaultof<_>
|
|
1432
|
+
|
|
1433
|
+
let extra =
|
|
1434
|
+
// As of 3.7.17 Fable assigns empty name to anonymous record, we shouldn't add them to the map to avoid conflicts.
|
|
1435
|
+
// Anonymous records cannot be recursive anyways, see #144
|
|
1436
|
+
match t.FullName with
|
|
1437
|
+
| "" -> extra
|
|
1438
|
+
| fullName -> extra |> Map.add fullName decoderRef
|
|
1439
|
+
|
|
1440
|
+
let decoder =
|
|
1441
|
+
if
|
|
1442
|
+
FSharpType.IsRecord(
|
|
1443
|
+
t,
|
|
1444
|
+
allowAccessToPrivateRepresentation = true
|
|
1445
|
+
)
|
|
1446
|
+
then
|
|
1447
|
+
let decoders =
|
|
1448
|
+
FSharpType.GetRecordFields(
|
|
1449
|
+
t,
|
|
1450
|
+
allowAccessToPrivateRepresentation = true
|
|
1451
|
+
)
|
|
1452
|
+
|> Array.map (fun fi ->
|
|
1453
|
+
let name = Util.Casing.convert caseStrategy fi.Name
|
|
1454
|
+
|
|
1455
|
+
name,
|
|
1456
|
+
autoDecoder extra caseStrategy false fi.PropertyType
|
|
1457
|
+
)
|
|
1458
|
+
|
|
1459
|
+
fun path value ->
|
|
1460
|
+
autoObject decoders path value
|
|
1461
|
+
|> Result.map (fun xs ->
|
|
1462
|
+
FSharpValue.MakeRecord(
|
|
1463
|
+
t,
|
|
1464
|
+
List.toArray xs,
|
|
1465
|
+
allowAccessToPrivateRepresentation = true
|
|
1466
|
+
)
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
elif
|
|
1470
|
+
FSharpType.IsUnion(t, allowAccessToPrivateRepresentation = true)
|
|
1471
|
+
then
|
|
1472
|
+
fun path (value: JsonValue) ->
|
|
1473
|
+
if Helpers.isString (value) then
|
|
1474
|
+
let name = Helpers.asString value
|
|
1475
|
+
makeUnion extra caseStrategy t name path [||]
|
|
1476
|
+
elif Helpers.isArray (value) then
|
|
1477
|
+
let values = Helpers.asArray value
|
|
1478
|
+
|
|
1479
|
+
string (path + "[0]") values.[0]
|
|
1480
|
+
|> Result.bind (fun name ->
|
|
1481
|
+
makeUnion extra caseStrategy t name path values
|
|
1482
|
+
)
|
|
1483
|
+
else
|
|
1484
|
+
(path, BadPrimitive("a string or array", value))
|
|
1485
|
+
|> Error
|
|
1486
|
+
|
|
1487
|
+
else if isOptional then
|
|
1488
|
+
// The error will only happen at runtime if the value is not null
|
|
1489
|
+
// See https://github.com/MangelMaxime/Thoth/pull/84#issuecomment-444837773
|
|
1490
|
+
boxDecoder (fun path value ->
|
|
1491
|
+
Error(
|
|
1492
|
+
path,
|
|
1493
|
+
BadType("an extra coder for " + t.FullName, value)
|
|
1494
|
+
)
|
|
1495
|
+
)
|
|
1496
|
+
else
|
|
1497
|
+
// Don't use failwithf here, for some reason F#/Fable compiles it as a function
|
|
1498
|
+
// when the return type is a function too, so it doesn't fail immediately
|
|
1499
|
+
sprintf
|
|
1500
|
+
"""Cannot generate auto decoder for %s. Please pass an extra decoder.
|
|
1501
|
+
|
|
1502
|
+
Documentation available at: https://thoth-org.github.io/Thoth.Json/documentation/auto/extra-coders.html#ready-to-use-extra-coders"""
|
|
1503
|
+
t.FullName
|
|
1504
|
+
|> failwith
|
|
1505
|
+
|
|
1506
|
+
decoderRef.Value <- decoder
|
|
1507
|
+
decoder
|
|
1508
|
+
|
|
1509
|
+
and private autoDecoder
|
|
1510
|
+
(extra: Map<string, ref<BoxedDecoder>>)
|
|
1511
|
+
caseStrategy
|
|
1512
|
+
(isOptional: bool)
|
|
1513
|
+
(t: System.Type)
|
|
1514
|
+
: BoxedDecoder
|
|
1515
|
+
=
|
|
1516
|
+
let fullname = t.FullName
|
|
1517
|
+
|
|
1518
|
+
match Map.tryFind fullname extra with
|
|
1519
|
+
| Some decoderRef -> fun path value -> decoderRef.contents path value
|
|
1520
|
+
| None ->
|
|
1521
|
+
if t.IsArray then
|
|
1522
|
+
let decoder =
|
|
1523
|
+
t.GetElementType() |> autoDecoder extra caseStrategy false
|
|
1524
|
+
|
|
1525
|
+
array decoder |> boxDecoder
|
|
1526
|
+
elif t.IsEnum then
|
|
1527
|
+
let enumType = System.Enum.GetUnderlyingType(t).FullName
|
|
1528
|
+
|
|
1529
|
+
if enumType = typeof<sbyte>.FullName then
|
|
1530
|
+
enumDecoder<sbyte> sbyte Operators.string t |> boxDecoder
|
|
1531
|
+
elif enumType = typeof<byte>.FullName then
|
|
1532
|
+
enumDecoder<byte> byte Operators.string t |> boxDecoder
|
|
1533
|
+
elif enumType = typeof<int16>.FullName then
|
|
1534
|
+
enumDecoder<int16> int16 Operators.string t |> boxDecoder
|
|
1535
|
+
elif enumType = typeof<uint16>.FullName then
|
|
1536
|
+
enumDecoder<uint16> uint16 Operators.string t |> boxDecoder
|
|
1537
|
+
elif enumType = typeof<int>.FullName then
|
|
1538
|
+
enumDecoder<int> int Operators.string t |> boxDecoder
|
|
1539
|
+
elif enumType = typeof<uint32>.FullName then
|
|
1540
|
+
enumDecoder<uint32> uint32 Operators.string t |> boxDecoder
|
|
1541
|
+
else
|
|
1542
|
+
failwithf
|
|
1543
|
+
"""Cannot generate auto decoder for %s.
|
|
1544
|
+
Thoth.Json.Net only support the following enum types:
|
|
1545
|
+
- sbyte
|
|
1546
|
+
- byte
|
|
1547
|
+
- int16
|
|
1548
|
+
- uint16
|
|
1549
|
+
- int
|
|
1550
|
+
- uint32
|
|
1551
|
+
If you can't use one of these types, please pass an extra decoder.
|
|
1552
|
+
"""
|
|
1553
|
+
t.FullName
|
|
1554
|
+
elif t.IsGenericType then
|
|
1555
|
+
if FSharpType.IsTuple(t) then
|
|
1556
|
+
let decoders =
|
|
1557
|
+
FSharpType.GetTupleElements(t)
|
|
1558
|
+
|> Array.map (autoDecoder extra caseStrategy false)
|
|
1559
|
+
|
|
1560
|
+
fun path value ->
|
|
1561
|
+
if Helpers.isArray value then
|
|
1562
|
+
mixedArray 0 decoders path (Helpers.asArray value)
|
|
1563
|
+
|> Result.map (fun xs ->
|
|
1564
|
+
FSharpValue.MakeTuple(List.toArray xs, t)
|
|
1565
|
+
)
|
|
1566
|
+
else
|
|
1567
|
+
(path, BadPrimitive("an array", value)) |> Error
|
|
1568
|
+
else
|
|
1569
|
+
let fullname = t.GetGenericTypeDefinition().FullName
|
|
1570
|
+
|
|
1571
|
+
if fullname = typedefof<obj option>.FullName then
|
|
1572
|
+
t.GenericTypeArguments.[0]
|
|
1573
|
+
|> (autoDecoder extra caseStrategy true)
|
|
1574
|
+
|> option
|
|
1575
|
+
|> boxDecoder
|
|
1576
|
+
elif fullname = typedefof<obj list>.FullName then
|
|
1577
|
+
t.GenericTypeArguments.[0]
|
|
1578
|
+
|> (autoDecoder extra caseStrategy false)
|
|
1579
|
+
|> list
|
|
1580
|
+
|> boxDecoder
|
|
1581
|
+
elif fullname = typedefof<obj seq>.FullName then
|
|
1582
|
+
t.GenericTypeArguments.[0]
|
|
1583
|
+
|> (autoDecoder extra caseStrategy false)
|
|
1584
|
+
|> seq
|
|
1585
|
+
|> boxDecoder
|
|
1586
|
+
elif
|
|
1587
|
+
fullname = typedefof<Map<System.IComparable, obj>>
|
|
1588
|
+
.FullName
|
|
1589
|
+
then
|
|
1590
|
+
let keyDecoder =
|
|
1591
|
+
t.GenericTypeArguments.[0]
|
|
1592
|
+
|> autoDecoder extra caseStrategy false
|
|
1593
|
+
|
|
1594
|
+
let valueDecoder =
|
|
1595
|
+
t.GenericTypeArguments.[1]
|
|
1596
|
+
|> autoDecoder extra caseStrategy false
|
|
1597
|
+
|
|
1598
|
+
oneOf
|
|
1599
|
+
[
|
|
1600
|
+
autoObject2 keyDecoder valueDecoder
|
|
1601
|
+
list (tuple2 keyDecoder valueDecoder)
|
|
1602
|
+
]
|
|
1603
|
+
|> map (fun ar -> toMap (unbox ar) |> box)
|
|
1604
|
+
elif fullname = typedefof<Set<string>>.FullName then
|
|
1605
|
+
let decoder =
|
|
1606
|
+
t.GenericTypeArguments.[0]
|
|
1607
|
+
|> autoDecoder extra caseStrategy false
|
|
1608
|
+
|
|
1609
|
+
fun path value ->
|
|
1610
|
+
match array decoder path value with
|
|
1611
|
+
| Error er -> Error er
|
|
1612
|
+
| Ok ar -> toSet (unbox ar) |> box |> Ok
|
|
1613
|
+
else
|
|
1614
|
+
autoDecodeRecordsAndUnions
|
|
1615
|
+
extra
|
|
1616
|
+
caseStrategy
|
|
1617
|
+
isOptional
|
|
1618
|
+
t
|
|
1619
|
+
else if fullname = typeof<bool>.FullName then
|
|
1620
|
+
boxDecoder bool
|
|
1621
|
+
elif fullname = typedefof<unit>.FullName then
|
|
1622
|
+
boxDecoder unit
|
|
1623
|
+
elif fullname = typeof<string>.FullName then
|
|
1624
|
+
boxDecoder string
|
|
1625
|
+
elif fullname = typeof<char>.FullName then
|
|
1626
|
+
boxDecoder char
|
|
1627
|
+
elif fullname = typeof<sbyte>.FullName then
|
|
1628
|
+
boxDecoder sbyte
|
|
1629
|
+
elif fullname = typeof<byte>.FullName then
|
|
1630
|
+
boxDecoder byte
|
|
1631
|
+
elif fullname = typeof<int16>.FullName then
|
|
1632
|
+
boxDecoder int16
|
|
1633
|
+
elif fullname = typeof<uint16>.FullName then
|
|
1634
|
+
boxDecoder uint16
|
|
1635
|
+
elif fullname = typeof<int>.FullName then
|
|
1636
|
+
boxDecoder int
|
|
1637
|
+
elif fullname = typeof<uint32>.FullName then
|
|
1638
|
+
boxDecoder uint32
|
|
1639
|
+
elif fullname = typeof<float>.FullName then
|
|
1640
|
+
boxDecoder float
|
|
1641
|
+
elif fullname = typeof<float32>.FullName then
|
|
1642
|
+
boxDecoder float32
|
|
1643
|
+
// These number types require extra libraries in Fable. To prevent penalizing
|
|
1644
|
+
// all users, extra decoders (withInt64, etc) must be passed when they're needed.
|
|
1645
|
+
|
|
1646
|
+
// elif fullname = typeof<int64>.FullName then
|
|
1647
|
+
// boxDecoder int64
|
|
1648
|
+
// elif fullname = typeof<uint64>.FullName then
|
|
1649
|
+
// boxDecoder uint64
|
|
1650
|
+
// elif fullname = typeof<bigint>.FullName then
|
|
1651
|
+
// boxDecoder bigint
|
|
1652
|
+
// elif fullname = typeof<decimal>.FullName then
|
|
1653
|
+
// boxDecoder decimal
|
|
1654
|
+
elif fullname = typeof<System.DateTime>.FullName then
|
|
1655
|
+
boxDecoder datetimeUtc
|
|
1656
|
+
elif fullname = typeof<System.DateTimeOffset>.FullName then
|
|
1657
|
+
boxDecoder datetimeOffset
|
|
1658
|
+
elif fullname = typeof<System.TimeSpan>.FullName then
|
|
1659
|
+
boxDecoder timespan
|
|
1660
|
+
elif fullname = typeof<System.Guid>.FullName then
|
|
1661
|
+
boxDecoder guid
|
|
1662
|
+
elif fullname = typeof<obj>.FullName then
|
|
1663
|
+
fun _ v -> Ok v
|
|
1664
|
+
else
|
|
1665
|
+
autoDecodeRecordsAndUnions extra caseStrategy isOptional t
|
|
1666
|
+
|
|
1667
|
+
let private makeExtra (extra: ExtraCoders option) =
|
|
1668
|
+
match extra with
|
|
1669
|
+
| None -> Map.empty
|
|
1670
|
+
| Some e -> Map.map (fun _ (_, dec) -> ref dec) e.Coders
|
|
1671
|
+
|
|
1672
|
+
type Auto =
|
|
1673
|
+
static member generateBoxedDecoderCached
|
|
1674
|
+
(
|
|
1675
|
+
t: System.Type,
|
|
1676
|
+
?caseStrategy: CaseStrategy,
|
|
1677
|
+
?extra: ExtraCoders
|
|
1678
|
+
)
|
|
1679
|
+
: BoxedDecoder
|
|
1680
|
+
=
|
|
1681
|
+
let caseStrategy = defaultArg caseStrategy PascalCase
|
|
1682
|
+
|
|
1683
|
+
let key =
|
|
1684
|
+
t.FullName
|
|
1685
|
+
|> (+) (Operators.string caseStrategy)
|
|
1686
|
+
|> (+) (
|
|
1687
|
+
extra
|
|
1688
|
+
|> Option.map (fun e -> e.Hash)
|
|
1689
|
+
|> Option.defaultValue ""
|
|
1690
|
+
)
|
|
1691
|
+
|
|
1692
|
+
Util.CachedDecoders.GetOrAdd(
|
|
1693
|
+
key,
|
|
1694
|
+
fun _ -> autoDecoder (makeExtra extra) caseStrategy false t
|
|
1695
|
+
)
|
|
1696
|
+
|
|
1697
|
+
static member inline generateDecoderCached<'T>
|
|
1698
|
+
(
|
|
1699
|
+
?caseStrategy: CaseStrategy,
|
|
1700
|
+
?extra: ExtraCoders
|
|
1701
|
+
)
|
|
1702
|
+
: Decoder<'T>
|
|
1703
|
+
=
|
|
1704
|
+
Auto.generateBoxedDecoderCached (
|
|
1705
|
+
typeof<'T>,
|
|
1706
|
+
?caseStrategy = caseStrategy,
|
|
1707
|
+
?extra = extra
|
|
1708
|
+
)
|
|
1709
|
+
|> unboxDecoder
|
|
1710
|
+
|
|
1711
|
+
static member generateBoxedDecoder
|
|
1712
|
+
(
|
|
1713
|
+
t: System.Type,
|
|
1714
|
+
?caseStrategy: CaseStrategy,
|
|
1715
|
+
?extra: ExtraCoders
|
|
1716
|
+
)
|
|
1717
|
+
: BoxedDecoder
|
|
1718
|
+
=
|
|
1719
|
+
let caseStrategy = defaultArg caseStrategy PascalCase
|
|
1720
|
+
autoDecoder (makeExtra extra) caseStrategy false t
|
|
1721
|
+
|
|
1722
|
+
static member inline generateDecoder<'T>
|
|
1723
|
+
(
|
|
1724
|
+
?caseStrategy: CaseStrategy,
|
|
1725
|
+
?extra: ExtraCoders
|
|
1726
|
+
)
|
|
1727
|
+
: Decoder<'T>
|
|
1728
|
+
=
|
|
1729
|
+
Auto.generateBoxedDecoder (
|
|
1730
|
+
typeof<'T>,
|
|
1731
|
+
?caseStrategy = caseStrategy,
|
|
1732
|
+
?extra = extra
|
|
1733
|
+
)
|
|
1734
|
+
|> unboxDecoder
|
|
1735
|
+
|
|
1736
|
+
static member inline fromString<'T>
|
|
1737
|
+
(
|
|
1738
|
+
json: string,
|
|
1739
|
+
?caseStrategy: CaseStrategy,
|
|
1740
|
+
?extra: ExtraCoders
|
|
1741
|
+
)
|
|
1742
|
+
: Result<'T, string>
|
|
1743
|
+
=
|
|
1744
|
+
let decoder =
|
|
1745
|
+
Auto.generateDecoder<'T> (
|
|
1746
|
+
?caseStrategy = caseStrategy,
|
|
1747
|
+
?extra = extra
|
|
1748
|
+
)
|
|
1749
|
+
|
|
1750
|
+
fromString decoder json
|
|
1751
|
+
|
|
1752
|
+
static member inline unsafeFromString<'T>
|
|
1753
|
+
(
|
|
1754
|
+
json: string,
|
|
1755
|
+
?caseStrategy: CaseStrategy,
|
|
1756
|
+
?extra: ExtraCoders
|
|
1757
|
+
)
|
|
1758
|
+
: 'T
|
|
1759
|
+
=
|
|
1760
|
+
let decoder =
|
|
1761
|
+
Auto.generateDecoder<'T> (
|
|
1762
|
+
?caseStrategy = caseStrategy,
|
|
1763
|
+
?extra = extra
|
|
1764
|
+
)
|
|
1765
|
+
|
|
1766
|
+
match fromString decoder json with
|
|
1767
|
+
| Ok x -> x
|
|
1768
|
+
| Error msg -> failwith msg
|