From a7682fbb9e34aed843ec5a3ea664da2555a6d1cd Mon Sep 17 00:00:00 2001 From: njlr Date: Thu, 30 Oct 2025 20:09:51 +0000 Subject: [PATCH 1/3] feat: adds decoder CE Adds decoder CE and tests Tag: core --- flake.nix | 35 ++++++ packages/Thoth.Json.Core/Syntax.fs | 104 ++++++++++++++++++ .../Thoth.Json.Core/Thoth.Json.Core.fsproj | 1 + tests/Thoth.Json.Tests.JavaScript/Main.fs | 1 + tests/Thoth.Json.Tests.Newtonsoft/Main.fs | 2 +- tests/Thoth.Json.Tests.Python/Main.fs | 1 + .../Thoth.Json.Tests.System.Text.Json/Main.fs | 2 + tests/Thoth.Json.Tests/Syntax.fs | 36 ++++++ .../Thoth.Json.Tests/Thoth.Json.Tests.fsproj | 1 + 9 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 flake.nix create mode 100644 packages/Thoth.Json.Core/Syntax.fs create mode 100644 tests/Thoth.Json.Tests/Syntax.fs diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..524e52c --- /dev/null +++ b/flake.nix @@ -0,0 +1,35 @@ +{ + description = "Development environment"; + + inputs = { + nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2405.*.tar.gz"; + }; + + outputs = { self, nixpkgs }: + let + allSystems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f { + pkgs = import nixpkgs { inherit system; }; + }); + in + { + devShells = forAllSystems ({ pkgs }: { + default = + pkgs.mkShell { + packages = [ + pkgs.bashInteractive + pkgs.dotnet-sdk_8 + pkgs.nodejs_20 + pkgs.pnpm_9 + pkgs.python312 + ]; + }; + }); + }; +} diff --git a/packages/Thoth.Json.Core/Syntax.fs b/packages/Thoth.Json.Core/Syntax.fs new file mode 100644 index 0000000..8473622 --- /dev/null +++ b/packages/Thoth.Json.Core/Syntax.fs @@ -0,0 +1,104 @@ +namespace Thoth.Json.Core + +[] +module Syntax = + + type DecoderBuilder internal () = + member inline this.Bind(m, f) = Decode.andThen f m + + member this.BindReturn(m, f) = Decode.map f m + + member this.Bind2Return(d1, d2, ctor: struct ('a * 'b) -> 't) = + Decode.map2 (fun a b -> ctor struct (a, b)) d1 d2 + + member this.Bind3Return(d1, d2, d3, ctor: struct ('a * 'b * 'c) -> 't) = + Decode.map3 (fun a b c -> ctor struct (a, b, c)) d1 d2 d3 + + member this.Bind4Return + (d1, d2, d3, d4, ctor: struct ('a * 'b * 'c * 'd) -> 't) + = + Decode.map4 (fun a b c d -> ctor struct (a, b, c, d)) d1 d2 d3 d4 + + member this.Bind5Return + (d1, d2, d3, d4, d5, ctor: struct ('a * 'b * 'c * 'd * 'e) -> 't) + = + Decode.map5 + (fun a b c d e -> ctor struct (a, b, c, d, e)) + d1 + d2 + d3 + d4 + d5 + + member this.Bind6Return + ( + d1, + d2, + d3, + d4, + d5, + d6, + ctor: struct ('a * 'b * 'c * 'd * 'e * 'f) -> 't + ) + = + Decode.map6 + (fun a b c d e f -> ctor struct (a, b, c, d, e, f)) + d1 + d2 + d3 + d4 + d5 + d6 + + member this.Bind7Return + ( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + ctor: struct ('a * 'b * 'c * 'd * 'e * 'f * 'g) -> 't + ) + = + Decode.map7 + (fun a b c d e f g -> ctor struct (a, b, c, d, e, f, g)) + d1 + d2 + d3 + d4 + d5 + d6 + d7 + + member this.Bind8Return + ( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + ctor: struct ('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h) -> 't + ) + = + Decode.map8 + (fun a b c d e f g h -> ctor struct (a, b, c, d, e, f, g, h)) + d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + + member this.MergeSources(d1, d2) = + Decode.map2 (fun a b -> struct (a, b)) d1 d2 + + member this.Return(x) = Decode.succeed x + + let decoder = DecoderBuilder() diff --git a/packages/Thoth.Json.Core/Thoth.Json.Core.fsproj b/packages/Thoth.Json.Core/Thoth.Json.Core.fsproj index 754a088..22975b6 100644 --- a/packages/Thoth.Json.Core/Thoth.Json.Core.fsproj +++ b/packages/Thoth.Json.Core/Thoth.Json.Core.fsproj @@ -21,6 +21,7 @@ Pick the right additional package for your runtime: + diff --git a/tests/Thoth.Json.Tests.JavaScript/Main.fs b/tests/Thoth.Json.Tests.JavaScript/Main.fs index ca752fb..9a35e3e 100644 --- a/tests/Thoth.Json.Tests.JavaScript/Main.fs +++ b/tests/Thoth.Json.Tests.JavaScript/Main.fs @@ -61,6 +61,7 @@ let main args = Decoders.tests runner Encoders.tests runner BackAndForth.tests runner + Syntax.tests runner ] |> Pyxpecto.runTests [||] diff --git a/tests/Thoth.Json.Tests.Newtonsoft/Main.fs b/tests/Thoth.Json.Tests.Newtonsoft/Main.fs index d711cb2..dae545f 100644 --- a/tests/Thoth.Json.Tests.Newtonsoft/Main.fs +++ b/tests/Thoth.Json.Tests.Newtonsoft/Main.fs @@ -42,6 +42,6 @@ let main args = Decoders.tests runner Encoders.tests runner BackAndForth.tests runner - + Syntax.tests runner ] |> Pyxpecto.runTests [||] diff --git a/tests/Thoth.Json.Tests.Python/Main.fs b/tests/Thoth.Json.Tests.Python/Main.fs index 4475cd2..5640b39 100644 --- a/tests/Thoth.Json.Tests.Python/Main.fs +++ b/tests/Thoth.Json.Tests.Python/Main.fs @@ -41,5 +41,6 @@ let main args = Decoders.tests runner Encoders.tests runner BackAndForth.tests runner + Syntax.tests runner ] |> Pyxpecto.runTests [||] diff --git a/tests/Thoth.Json.Tests.System.Text.Json/Main.fs b/tests/Thoth.Json.Tests.System.Text.Json/Main.fs index d9887a4..1ad1ded 100644 --- a/tests/Thoth.Json.Tests.System.Text.Json/Main.fs +++ b/tests/Thoth.Json.Tests.System.Text.Json/Main.fs @@ -46,5 +46,7 @@ let main args = [ Decoders.tests runner Encoders.tests runner + BackAndForth.tests runner + Syntax.tests runner ] |> Pyxpecto.runTests [||] diff --git a/tests/Thoth.Json.Tests/Syntax.fs b/tests/Thoth.Json.Tests/Syntax.fs new file mode 100644 index 0000000..64656d4 --- /dev/null +++ b/tests/Thoth.Json.Tests/Syntax.fs @@ -0,0 +1,36 @@ +module Thoth.Json.Tests.Syntax + +open Thoth.Json.Tests.Testing +open Thoth.Json.Core +open Fable.Pyxpecto + +let tests (runner: TestRunner<_, _>) = + testList + "Thoth.Json - Syntax" + [ + + testCase "decoder syntax works for bind" + <| fun _ -> + let expected = 123, "abc" + + let json = + Encode.object + [ + "x", Encode.int 123 + "y", Encode.string "abc" + ] + |> runner.Encode.toString 0 + + let decoded = + runner.Decode.fromString + (decoder { + let! x = Decode.field "x" Decode.int + let! y = Decode.field "y" Decode.string + + return x, y + }) + json + + equal (Ok expected) decoded + + ] diff --git a/tests/Thoth.Json.Tests/Thoth.Json.Tests.fsproj b/tests/Thoth.Json.Tests/Thoth.Json.Tests.fsproj index 80c9c9e..f744943 100644 --- a/tests/Thoth.Json.Tests/Thoth.Json.Tests.fsproj +++ b/tests/Thoth.Json.Tests/Thoth.Json.Tests.fsproj @@ -8,6 +8,7 @@ + From d2d6cd62f28bc5c1bdd19d1ed3e3ace3bbad9bf8 Mon Sep 17 00:00:00 2001 From: njlr Date: Thu, 30 Oct 2025 20:49:38 +0000 Subject: [PATCH 2/3] fix: adds returnfrom to decoder Tag: core --- packages/Thoth.Json.Core/Syntax.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/Thoth.Json.Core/Syntax.fs b/packages/Thoth.Json.Core/Syntax.fs index 8473622..6f68a70 100644 --- a/packages/Thoth.Json.Core/Syntax.fs +++ b/packages/Thoth.Json.Core/Syntax.fs @@ -101,4 +101,6 @@ module Syntax = member this.Return(x) = Decode.succeed x + member this.ReturnFrom(x: Decoder<'a>) = x + let decoder = DecoderBuilder() From e9a63c1374425595371244050815d933cd8c5840 Mon Sep 17 00:00:00 2001 From: njlr Date: Thu, 30 Oct 2025 20:50:35 +0000 Subject: [PATCH 3/3] chore: revert nix flake --- flake.nix | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 flake.nix diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 524e52c..0000000 --- a/flake.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ - description = "Development environment"; - - inputs = { - nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2405.*.tar.gz"; - }; - - outputs = { self, nixpkgs }: - let - allSystems = [ - "x86_64-linux" - "aarch64-linux" - "x86_64-darwin" - "aarch64-darwin" - ]; - - forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f { - pkgs = import nixpkgs { inherit system; }; - }); - in - { - devShells = forAllSystems ({ pkgs }: { - default = - pkgs.mkShell { - packages = [ - pkgs.bashInteractive - pkgs.dotnet-sdk_8 - pkgs.nodejs_20 - pkgs.pnpm_9 - pkgs.python312 - ]; - }; - }); - }; -}