@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.
Files changed (225) hide show
  1. package/Designer.d.ts +89 -0
  2. package/FormSpec.TS/Components/HelloComponent.js +68 -0
  3. package/FormSpec.TS/Components/HelloComponent.js.map +1 -0
  4. package/FormSpec.TS/Components/HelloComponent.ts.map +1 -0
  5. package/FormSpec.TS/Designer.js +526 -0
  6. package/FormSpec.TS/Designer.js.map +1 -0
  7. package/FormSpec.TS/Designer.ts.map +1 -0
  8. package/FormSpec.TS/FormSpec.js +5400 -0
  9. package/FormSpec.TS/FormSpec.js.map +1 -0
  10. package/FormSpec.TS/FormSpec.ts.map +1 -0
  11. package/FormSpec.TS/FormSpecHelpers.js +382 -0
  12. package/FormSpec.TS/FormSpecHelpers.js.map +1 -0
  13. package/FormSpec.TS/FormSpecHelpers.ts.map +1 -0
  14. package/FormSpec.TS/Helpers.js +732 -0
  15. package/FormSpec.TS/Helpers.js.map +1 -0
  16. package/FormSpec.TS/Helpers.ts.map +1 -0
  17. package/FormSpec.TS/Interfaces.js +257 -0
  18. package/FormSpec.TS/Interfaces.js.map +1 -0
  19. package/FormSpec.TS/Interfaces.ts.map +1 -0
  20. package/FormSpec.TS/Interop/FormSpec.Api.Helpers.js +854 -0
  21. package/FormSpec.TS/Interop/FormSpec.Api.Helpers.js.map +1 -0
  22. package/FormSpec.TS/Interop/FormSpec.Api.Helpers.ts.map +1 -0
  23. package/FormSpec.TS/Interop/FormSpec.Api.Option.js +1961 -0
  24. package/FormSpec.TS/Interop/FormSpec.Api.Option.js.map +1 -0
  25. package/FormSpec.TS/Interop/FormSpec.Api.Option.ts.map +1 -0
  26. package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.js +1367 -0
  27. package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.js.map +1 -0
  28. package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.ts.map +1 -0
  29. package/FormSpec.TS/Logging/LogTypes.js +347 -0
  30. package/FormSpec.TS/Logging/LogTypes.js.map +1 -0
  31. package/FormSpec.TS/Logging/LogTypes.ts.map +1 -0
  32. package/FormSpec.TS/Migrator.js +230 -0
  33. package/FormSpec.TS/Migrator.js.map +1 -0
  34. package/FormSpec.TS/Migrator.ts.map +1 -0
  35. package/FormSpec.TS/PathwayDataExtractor.js +361 -0
  36. package/FormSpec.TS/PathwayDataExtractor.js.map +1 -0
  37. package/FormSpec.TS/PathwayDataExtractor.ts.map +1 -0
  38. package/FormSpec.TS/PathwayExecutor.js +1321 -0
  39. package/FormSpec.TS/PathwayExecutor.js.map +1 -0
  40. package/FormSpec.TS/PathwayExecutor.ts.map +1 -0
  41. package/FormSpec.TS/PathwayValidator.js +346 -0
  42. package/FormSpec.TS/PathwayValidator.js.map +1 -0
  43. package/FormSpec.TS/PathwayValidator.ts.map +1 -0
  44. package/FormSpec.TS/PluginInterface.js +171 -0
  45. package/FormSpec.TS/PluginInterface.js.map +1 -0
  46. package/FormSpec.TS/PluginInterface.ts.map +1 -0
  47. package/FormSpec.TS/Prelude.js +59 -0
  48. package/FormSpec.TS/Prelude.js.map +1 -0
  49. package/FormSpec.TS/Prelude.ts.map +1 -0
  50. package/FormSpec.TS/Renderers/FormSpecMarkdownRenderer.js +958 -0
  51. package/FormSpec.TS/Renderers/FormSpecMarkdownRenderer.js.map +1 -0
  52. package/FormSpec.TS/Renderers/FormSpecMarkdownRenderer.ts.map +1 -0
  53. package/FormSpec.TS/Renderers/MermaidRenderer.js +228 -0
  54. package/FormSpec.TS/Renderers/MermaidRenderer.js.map +1 -0
  55. package/FormSpec.TS/Renderers/MermaidRenderer.ts.map +1 -0
  56. package/FormSpec.TS/Renderers/PathwayRenderers.js +190 -0
  57. package/FormSpec.TS/Renderers/PathwayRenderers.js.map +1 -0
  58. package/FormSpec.TS/Renderers/PathwayRenderers.ts.map +1 -0
  59. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs +511 -0
  60. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs.js +437 -0
  61. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs.js.map +1 -0
  62. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs.ts.map +1 -0
  63. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs +16 -0
  64. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs.js +73 -0
  65. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs.js.map +1 -0
  66. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs.ts.map +1 -0
  67. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs +50 -0
  68. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs.js +72 -0
  69. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs.js.map +1 -0
  70. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs.ts.map +1 -0
  71. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Fable.Form.fableproj +28 -0
  72. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs +24 -0
  73. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs.js +56 -0
  74. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs.js.map +1 -0
  75. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs.ts.map +1 -0
  76. package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Fable.Form.Simple.fableproj +31 -0
  77. package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs +178 -0
  78. package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs.js +464 -0
  79. package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs.js.map +1 -0
  80. package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs.ts.map +1 -0
  81. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Extensions.fs +17 -0
  82. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs +152 -0
  83. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs.js +25 -0
  84. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs.js.map +1 -0
  85. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs.ts.map +1 -0
  86. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Types.fableproj +28 -0
  87. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs +218 -0
  88. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs.js +37 -0
  89. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs.js.map +1 -0
  90. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs.ts.map +1 -0
  91. package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.Types.fableproj +27 -0
  92. package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs +82 -0
  93. package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs.js +7 -0
  94. package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs.js.map +1 -0
  95. package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs.ts.map +1 -0
  96. package/FormSpec.TS/fable_modules/Feliz.2.7.0/BorderStyle.fs +59 -0
  97. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs +154 -0
  98. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs.js +32 -0
  99. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs.js.map +1 -0
  100. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs.ts.map +1 -0
  101. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Feliz.fableproj +42 -0
  102. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Fonts.fs +240 -0
  103. package/FormSpec.TS/fable_modules/Feliz.2.7.0/GridTypes.fs +24 -0
  104. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Html.fs +826 -0
  105. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs +83 -0
  106. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs.js +292 -0
  107. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs.js.map +1 -0
  108. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs.ts.map +1 -0
  109. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs +65 -0
  110. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs.js +229 -0
  111. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs.js.map +1 -0
  112. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs.ts.map +1 -0
  113. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Length.fs +91 -0
  114. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Locale.fs +876 -0
  115. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs +4080 -0
  116. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs.js +133 -0
  117. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs.js.map +1 -0
  118. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs.ts.map +1 -0
  119. package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs +656 -0
  120. package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs.js +561 -0
  121. package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs.js.map +1 -0
  122. package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs.ts.map +1 -0
  123. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs +25 -0
  124. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs.js +46 -0
  125. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs.js.map +1 -0
  126. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs.ts.map +1 -0
  127. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactInterop.js +63 -0
  128. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactInterop.js.map +1 -0
  129. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs +41 -0
  130. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs.js +7 -0
  131. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs.js.map +1 -0
  132. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs.ts.map +1 -0
  133. package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs +53 -0
  134. package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs.js +7 -0
  135. package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs.js.map +1 -0
  136. package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs.ts.map +1 -0
  137. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Styles.fs +5740 -0
  138. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs +1455 -0
  139. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs.js +39 -0
  140. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs.js.map +1 -0
  141. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs.ts.map +1 -0
  142. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TextDecorationLine.fs +13 -0
  143. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TextDecorationStyle.fs +33 -0
  144. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Transform.fs +181 -0
  145. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TransformOrigin.fs +17 -0
  146. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TransitionProperty.fs +162 -0
  147. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs +13 -0
  148. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs.js +7 -0
  149. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs.js.map +1 -0
  150. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs.ts.map +1 -0
  151. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs +1768 -0
  152. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs.js +2337 -0
  153. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs.js.map +1 -0
  154. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs.ts.map +1 -0
  155. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs +811 -0
  156. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.js +465 -0
  157. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.js.map +1 -0
  158. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.ts.map +1 -0
  159. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs +47 -0
  160. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs.js +18 -0
  161. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs.js.map +1 -0
  162. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs.ts.map +1 -0
  163. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Thoth.Json.fableproj +34 -0
  164. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs +68 -0
  165. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs.js +355 -0
  166. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs.js.map +1 -0
  167. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs.ts.map +1 -0
  168. package/FormSpec.TS/fable_modules/project_cracked.json +1 -0
  169. package/FormSpec.d.ts +1257 -0
  170. package/FormSpecHelpers.d.ts +50 -0
  171. package/Helpers.d.ts +147 -0
  172. package/Interfaces.d.ts +70 -0
  173. package/Interop/FormSpec.Api.Helpers.d.ts +247 -0
  174. package/Interop/FormSpec.Api.Helpers.d.ts.map +1 -0
  175. package/Interop/FormSpec.Api.Option.d.ts +384 -0
  176. package/Interop/FormSpec.Api.Option.d.ts.map +1 -0
  177. package/Interop/FormSpec.Values.Api.Option.d.ts +324 -0
  178. package/Interop/FormSpec.Values.Api.Option.d.ts.map +1 -0
  179. package/Migrator.d.ts +59 -0
  180. package/PathwayDataExtractor.d.ts +19 -0
  181. package/PathwayExecutor.d.ts +210 -0
  182. package/PathwayValidator.d.ts +52 -0
  183. package/PluginInterface.d.ts +36 -0
  184. package/Prelude.d.ts +11 -0
  185. package/README.TS.md +622 -0
  186. package/README.md +85 -0
  187. package/package.json +39 -0
  188. package/src/Components/HelloComponent.ts +48 -0
  189. package/src/Components/HelloComponent.ts.map +1 -0
  190. package/src/Designer.ts +389 -0
  191. package/src/Designer.ts.map +1 -0
  192. package/src/FormSpec.ts +3114 -0
  193. package/src/FormSpec.ts.map +1 -0
  194. package/src/FormSpecHelpers.ts +374 -0
  195. package/src/FormSpecHelpers.ts.map +1 -0
  196. package/src/Helpers.ts +765 -0
  197. package/src/Helpers.ts.map +1 -0
  198. package/src/Interfaces.ts +166 -0
  199. package/src/Interfaces.ts.map +1 -0
  200. package/src/Interop/FormSpec.Api.Helpers.ts +872 -0
  201. package/src/Interop/FormSpec.Api.Helpers.ts.map +1 -0
  202. package/src/Interop/FormSpec.Api.Option.ts +1618 -0
  203. package/src/Interop/FormSpec.Api.Option.ts.map +1 -0
  204. package/src/Interop/FormSpec.Values.Api.Option.ts +1214 -0
  205. package/src/Interop/FormSpec.Values.Api.Option.ts.map +1 -0
  206. package/src/Logging/LogTypes.ts +212 -0
  207. package/src/Logging/LogTypes.ts.map +1 -0
  208. package/src/Migrator.ts +156 -0
  209. package/src/Migrator.ts.map +1 -0
  210. package/src/PathwayDataExtractor.ts +290 -0
  211. package/src/PathwayDataExtractor.ts.map +1 -0
  212. package/src/PathwayExecutor.ts +1102 -0
  213. package/src/PathwayExecutor.ts.map +1 -0
  214. package/src/PathwayValidator.ts +244 -0
  215. package/src/PathwayValidator.ts.map +1 -0
  216. package/src/PluginInterface.ts +79 -0
  217. package/src/PluginInterface.ts.map +1 -0
  218. package/src/Prelude.ts +21 -0
  219. package/src/Prelude.ts.map +1 -0
  220. package/src/Renderers/FormSpecMarkdownRenderer.ts +874 -0
  221. package/src/Renderers/FormSpecMarkdownRenderer.ts.map +1 -0
  222. package/src/Renderers/MermaidRenderer.ts +218 -0
  223. package/src/Renderers/MermaidRenderer.ts.map +1 -0
  224. package/src/Renderers/PathwayRenderers.ts +200 -0
  225. 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&lt;'T&gt;) =
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