Compare commits

...

2 Commits

Author SHA1 Message Date
dependabot[bot]
b53b410feb Bump ApiSurface from 4.0.41 to 4.0.42 (#176)
* Bump ApiSurface from 4.0.41 to 4.0.42

Bumps [ApiSurface](https://github.com/G-Research/ApiSurface) from 4.0.41 to 4.0.42.
- [Release notes](https://github.com/G-Research/ApiSurface/releases)
- [Commits](https://github.com/G-Research/ApiSurface/compare/ApiSurface.4.0.41...ApiSurface.4.0.42)

---
updated-dependencies:
- dependency-name: ApiSurface
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-07-01 18:21:26 +01:00
Patrick Stevens
398cd04a2a Support DateTimeOffset in JSON generators (#179) 2024-07-01 18:08:09 +01:00
9 changed files with 154 additions and 14 deletions

View File

@@ -204,6 +204,12 @@ module JsonRecordTypeWithBothJsonSerializeExtension =
node.Add ("enum", (input.Enum |> SomeEnum.toJsonNode))
node.Add (
"timestamp",
(input.Timestamp
|> (fun field -> field.ToString "o" |> System.Text.Json.Nodes.JsonValue.Create<string>))
)
node :> _
namespace ConsumePlugin
@@ -418,6 +424,19 @@ module JsonRecordTypeWithBothJsonParseExtension =
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordTypeWithBoth =
let arg_20 =
(match node.["timestamp"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("timestamp")
)
)
| v -> v)
.AsValue()
.GetValue<string> ()
|> System.DateTimeOffset.Parse
let arg_19 =
SomeEnum.jsonParse (
match node.["enum"] with
@@ -685,6 +704,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
IntMeasureOption = arg_17
IntMeasureNullable = arg_18
Enum = arg_19
Timestamp = arg_20
}
namespace ConsumePlugin
@@ -804,3 +824,21 @@ module HeaderAndValueJsonParseExtension =
Header = arg_0
Value = arg_1
}
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the Foo type
[<AutoOpen>]
module FooJsonParseExtension =
/// Extension methods for JSON parsing
type Foo with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : Foo =
let arg_0 =
match node.["message"] with
| null -> None
| v -> HeaderAndValue.jsonParse v |> Some
{
Message = arg_0
}

View File

@@ -49,6 +49,7 @@ type JsonRecordTypeWithBoth =
IntMeasureOption : int<measure> option
IntMeasureNullable : int<measure> Nullable
Enum : SomeEnum
Timestamp : DateTimeOffset
}
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
@@ -67,6 +68,7 @@ type HeaderAndValue =
}
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
[<WoofWare.Myriad.Plugins.JsonParse true>]
type Foo =
{
Message : HeaderAndValue option

View File

@@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.41" />
<PackageReference Include="ApiSurface" Version="4.0.42" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="NUnit" Version="4.1.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>

View File

@@ -92,6 +92,7 @@ module TestJsonSerde =
let! intMeasureOption = Arb.generate
let! intMeasureNullable = Arb.generate
let! someEnum = Gen.choose (0, 1)
let! timestamp = Arb.generate
return
{
@@ -115,6 +116,7 @@ module TestJsonSerde =
IntMeasureOption = intMeasureOption
IntMeasureNullable = intMeasureNullable
Enum = enum<SomeEnum> someEnum
Timestamp = timestamp
}
}
@@ -132,6 +134,80 @@ module TestJsonSerde =
property |> Prop.forAll (Arb.fromGen outerGen) |> Check.QuickThrowOnFailure
[<Test>]
let ``Single example of big record`` () =
let guid = Guid.Parse "dfe24db5-9f8d-447b-8463-4c0bcf1166d5"
let data =
{
A = 3
B = "hello!"
C = [ 1 ; -9 ]
D =
{
Thing = guid
Map = Map.ofList []
ReadOnlyDict = readOnlyDict []
Dict = dict []
ConcreteDict = Dictionary ()
}
E = [| "I'm-a-string" |]
Arr = [| -18883 ; 9100 |]
Byte = 87uy<measure>
Sbyte = 89y<measure>
I = 199993345<measure>
I32 = -485832<measure>
I64 = -13458625689L<measure>
U = 458582u<measure>
U32 = 857362147u<measure>
U64 = 1234567892123414596UL<measure>
F = 8833345667.1<measure>
F32 = 1000.98f<measure>
Single = 0.334f<measure>
IntMeasureOption = Some 981<measure>
IntMeasureNullable = Nullable -883<measure>
Enum = enum<SomeEnum> 1
Timestamp = DateTimeOffset (2024, 07, 01, 17, 54, 00, TimeSpan.FromHours 1.0)
}
let expected =
"""{
"a": 3,
"b": "hello!",
"c": [1, -9],
"d": {
"it\u0027s-a-me": "dfe24db5-9f8d-447b-8463-4c0bcf1166d5",
"map": {},
"readOnlyDict": {},
"dict": {},
"concreteDict": {}
},
"e": ["I\u0027m-a-string"],
"arr": [-18883, 9100],
"byte": 87,
"sbyte": 89,
"i": 199993345,
"i32": -485832,
"i64": -13458625689,
"u": 458582,
"u32": 857362147,
"u64": 1234567892123414596,
"f": 8833345667.1,
"f32": 1000.98,
"single": 0.334,
"intMeasureOption": 981,
"intMeasureNullable": -883,
"enum": 1,
"timestamp": "2024-07-01T17:54:00.0000000\u002B01:00"
}
"""
|> fun s -> s.ToCharArray ()
|> Array.filter (fun c -> not (Char.IsWhiteSpace c))
|> fun s -> new String (s)
JsonRecordTypeWithBoth.toJsonNode(data).ToJsonString () |> shouldEqual expected
JsonRecordTypeWithBoth.jsonParse (JsonNode.Parse expected) |> shouldEqual data
[<Test>]
let ``Guids are treated just like strings`` () =
let guidStr = "b1e7496e-6e79-4158-8579-a01de355d3b2"

View File

@@ -33,7 +33,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.41"/>
<PackageReference Include="ApiSurface" Version="4.0.42"/>
<PackageReference Include="FsCheck" Version="2.16.6"/>
<PackageReference Include="FsUnit" Version="6.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>

View File

@@ -192,6 +192,10 @@ module internal JsonParseGenerator =
node
|> asValueGetValue propertyName "string"
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTime" ; "Parse" ])
| DateTimeOffset ->
node
|> asValueGetValue propertyName "string"
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTimeOffset" ; "Parse" ])
| NumberType typeName -> parseNumberType options propertyName node typeName
| PrimitiveType typeName -> asValueGetValueIdent propertyName typeName node
| OptionType ty ->

View File

@@ -38,6 +38,17 @@ module internal JsonSerializeGenerator =
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|> SynExpr.typeApp [ fieldType ]
|> fun e -> e, false
| DateTimeOffset ->
// fun field -> field.ToString("o") |> JsonValue.Create<string>
let create =
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|> SynExpr.typeApp [ SynType.named "string" ]
SynExpr.createIdent "field"
|> SynExpr.callMethodArg "ToString" (SynExpr.CreateConst "o")
|> SynExpr.pipeThroughFunction create
|> SynExpr.createLambda "field"
|> fun e -> e, false
| NullableType ty ->
// fun field -> if field.HasValue then {serializeNode ty} field.Value else JsonValue.Create null
let inner, innerIsJsonNode = serializeNode ty

View File

@@ -241,6 +241,15 @@ module internal SynTypePatterns =
| _ -> None
| _ -> None
let (|DateTimeOffset|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "DateTimeOffset" ]
| [ "DateTimeOffset" ] -> Some ()
| _ -> None
| _ -> None
let (|Uri|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->

View File

@@ -3,8 +3,8 @@
{fetchNuGet}: [
(fetchNuGet {
pname = "ApiSurface";
version = "4.0.41";
sha256 = "03kfa5ngmgkik9lc58sp8s9rrh9g40hhgjnrv662ks0d0y2i9i89";
version = "4.0.42";
sha256 = "0azjv64bbbhc4rndbjhcmqxxg1bkf1v3ym3x34zmsbz0lr1hy6pv";
})
(fetchNuGet {
pname = "fantomas";
@@ -193,23 +193,23 @@
})
(fetchNuGet {
pname = "NuGet.Common";
version = "6.10.0";
sha256 = "0nizrnilmlcqbm945293h8q3wfqfchb4xi8g50x4kjn0rbpd1kbh";
version = "6.10.1";
sha256 = "1z69k0j727jcwrxzmvnixdac84lb9706iabqs8mrns8j7kbmw1ns";
})
(fetchNuGet {
pname = "NuGet.Configuration";
version = "6.10.0";
sha256 = "1aqaknaawnqx4mnvx9qw73wvj48jjzv0d78dzwl7m9zjlrl9myhz";
version = "6.10.1";
sha256 = "0qy2bdi3dz6fdw7qbv77fg956idm9d9733j8b1pcrcj9pfayys26";
})
(fetchNuGet {
pname = "NuGet.Frameworks";
version = "6.10.0";
sha256 = "0hrd8y31zx9a0wps49czw0qgbrakb49zn3abfgylc9xrq990zkqk";
version = "6.10.1";
sha256 = "1p8d701fhbqv2r8vqmj948af9xvz2fd3273803cdrjy3a2wykmq1";
})
(fetchNuGet {
pname = "NuGet.Packaging";
version = "6.10.0";
sha256 = "18s53cvrf51lihmaqqdf48p2qi6ky1l48jv0hvbp76cxwdg7rba4";
version = "6.10.1";
sha256 = "0zl8xfzvd1yij2ln6iwy6cz8qfwlbyyqlin872ab5y58ws61a2x2";
})
(fetchNuGet {
pname = "NuGet.Protocol";
@@ -218,8 +218,8 @@
})
(fetchNuGet {
pname = "NuGet.Versioning";
version = "6.10.0";
sha256 = "1x19njx4x0sw9fz8y5fibi15xfsrw5avir0cx0599yd7p3ykik5g";
version = "6.10.1";
sha256 = "0lji7g6abnpmhzlgvni8wlb7l62n4180v3sphp4494wi0gn7ds4c";
})
(fetchNuGet {
pname = "NUnit";