mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-05 20:18:43 +00:00
Support units of measure in JsonParse (#170)
This commit is contained in:
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,12 +1,12 @@
|
|||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
/packages/
|
/packages/
|
||||||
riderModule.iml
|
riderModule.iml
|
||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
.idea/
|
.idea/
|
||||||
*.sln.DotSettings.user
|
*.sln.DotSettings.user
|
||||||
.DS_Store
|
.DS_Store
|
||||||
result
|
result
|
||||||
.analyzerpackages/
|
.analyzerpackages/
|
||||||
analysis.sarif
|
analysis.sarif
|
||||||
.direnv/
|
.direnv/
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
Notable changes are recorded here.
|
Notable changes are recorded here.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.1.45, WoofWare.Myriad.Plugins.Attributes 3.1.7
|
||||||
|
|
||||||
|
The NuGet packages are now attested to through [GitHub Attestations](https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/).
|
||||||
|
You can run `gh attestation verify ~/.nuget/packages/woofware.myriad.plugins/2.1.45/woofware.myriad.plugins.2.1.45.nupkg -o Smaug123`, for example, to verify with GitHub that the GitHub Actions pipeline on this repository produced a nupkg file with the same hash as the one you were served from NuGet.
|
||||||
|
|
||||||
# WoofWare.Myriad.Plugins 2.1.33
|
# WoofWare.Myriad.Plugins 2.1.33
|
||||||
|
|
||||||
`JsonParse` can now deserialize the discriminated unions which `JsonSerialize` wrote out.
|
`JsonParse` can now deserialize the discriminated unions which `JsonSerialize` wrote out.
|
||||||
|
@@ -148,6 +148,7 @@ module GymLocation =
|
|||||||
reraise ()
|
reraise ()
|
||||||
else
|
else
|
||||||
reraise ()
|
reraise ()
|
||||||
|
|> LanguagePrimitives.FloatWithMeasure
|
||||||
|
|
||||||
let arg_0 =
|
let arg_0 =
|
||||||
try
|
try
|
||||||
|
@@ -19,13 +19,16 @@ type GymAccessOptions =
|
|||||||
QrCodeAccess : bool
|
QrCodeAccess : bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[<Measure>]
|
||||||
|
type measure
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||||
type GymLocation =
|
type GymLocation =
|
||||||
{
|
{
|
||||||
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
||||||
Longitude : float
|
Longitude : float
|
||||||
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
||||||
Latitude : float
|
Latitude : float<measure>
|
||||||
}
|
}
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||||
|
@@ -58,7 +58,7 @@ module PureGymDtos =
|
|||||||
[
|
[
|
||||||
"""{"latitude": 1.0, "longitude": 3.0}""",
|
"""{"latitude": 1.0, "longitude": 3.0}""",
|
||||||
{
|
{
|
||||||
GymLocation.Latitude = 1.0
|
GymLocation.Latitude = 1.0<measure>
|
||||||
Longitude = 3.0
|
Longitude = 3.0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -96,7 +96,7 @@ module PureGymDtos =
|
|||||||
Location =
|
Location =
|
||||||
{
|
{
|
||||||
Longitude = -0.110252
|
Longitude = -0.110252
|
||||||
Latitude = 51.480401
|
Latitude = 51.480401<measure>
|
||||||
}
|
}
|
||||||
TimeZone = "Europe/London"
|
TimeZone = "Europe/London"
|
||||||
ReopenDate = "2021-04-12T00:00:00+01 Europe/London"
|
ReopenDate = "2021-04-12T00:00:00+01 Europe/London"
|
||||||
|
@@ -140,6 +140,47 @@ module internal JsonParseGenerator =
|
|||||||
failwithf
|
failwithf
|
||||||
$"Unable to parse the key type %+A{desiredType} of a JSON object. Keys are strings, and this plugin does not know how to convert to that from a string."
|
$"Unable to parse the key type %+A{desiredType} of a JSON object. Keys are strings, and this plugin does not know how to convert to that from a string."
|
||||||
|
|
||||||
|
let private parseNumberType
|
||||||
|
(options : JsonParseOption)
|
||||||
|
(propertyName : SynExpr option)
|
||||||
|
(node : SynExpr)
|
||||||
|
(typeName : string)
|
||||||
|
=
|
||||||
|
let basic = asValueGetValue propertyName typeName node
|
||||||
|
|
||||||
|
match options.JsonNumberHandlingArg with
|
||||||
|
| None -> basic
|
||||||
|
| Some option ->
|
||||||
|
let cond =
|
||||||
|
SynExpr.DotGet (SynExpr.createIdent "exc", range0, SynLongIdent.createS "Message", range0)
|
||||||
|
|> SynExpr.callMethodArg "Contains" (SynExpr.CreateConst "cannot be converted to")
|
||||||
|
|
||||||
|
let handler =
|
||||||
|
asValueGetValue propertyName "string" node
|
||||||
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent' (parseFunction typeName))
|
||||||
|
|> SynExpr.ifThenElse
|
||||||
|
(SynExpr.equals
|
||||||
|
option
|
||||||
|
(SynExpr.createLongIdent
|
||||||
|
[
|
||||||
|
"System"
|
||||||
|
"Text"
|
||||||
|
"Json"
|
||||||
|
"Serialization"
|
||||||
|
"JsonNumberHandling"
|
||||||
|
"AllowReadingFromString"
|
||||||
|
]))
|
||||||
|
SynExpr.reraise
|
||||||
|
|> SynExpr.ifThenElse cond SynExpr.reraise
|
||||||
|
|
||||||
|
basic
|
||||||
|
|> SynExpr.pipeThroughTryWith
|
||||||
|
(SynPat.IsInst (
|
||||||
|
SynType.LongIdent (SynLongIdent.createS' [ "System" ; "InvalidOperationException" ]),
|
||||||
|
range0
|
||||||
|
))
|
||||||
|
handler
|
||||||
|
|
||||||
/// Given `node.["town"]`, for example, choose how to obtain a JSON value from it.
|
/// Given `node.["town"]`, for example, choose how to obtain a JSON value from it.
|
||||||
/// The property name is used in error messages at runtime to show where a JSON
|
/// The property name is used in error messages at runtime to show where a JSON
|
||||||
/// parse error occurred; supply `None` to indicate "don't validate".
|
/// parse error occurred; supply `None` to indicate "don't validate".
|
||||||
@@ -168,41 +209,7 @@ module internal JsonParseGenerator =
|
|||||||
node
|
node
|
||||||
|> asValueGetValue propertyName "string"
|
|> asValueGetValue propertyName "string"
|
||||||
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTime" ; "Parse" ])
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTime" ; "Parse" ])
|
||||||
| NumberType typeName ->
|
| NumberType typeName -> parseNumberType options propertyName node typeName
|
||||||
let basic = asValueGetValue propertyName typeName node
|
|
||||||
|
|
||||||
match options.JsonNumberHandlingArg with
|
|
||||||
| None -> basic
|
|
||||||
| Some option ->
|
|
||||||
let cond =
|
|
||||||
SynExpr.DotGet (SynExpr.createIdent "exc", range0, SynLongIdent.createS "Message", range0)
|
|
||||||
|> SynExpr.callMethodArg "Contains" (SynExpr.CreateConst "cannot be converted to")
|
|
||||||
|
|
||||||
let handler =
|
|
||||||
asValueGetValue propertyName "string" node
|
|
||||||
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent' (parseFunction typeName))
|
|
||||||
|> SynExpr.ifThenElse
|
|
||||||
(SynExpr.equals
|
|
||||||
option
|
|
||||||
(SynExpr.createLongIdent
|
|
||||||
[
|
|
||||||
"System"
|
|
||||||
"Text"
|
|
||||||
"Json"
|
|
||||||
"Serialization"
|
|
||||||
"JsonNumberHandling"
|
|
||||||
"AllowReadingFromString"
|
|
||||||
]))
|
|
||||||
SynExpr.reraise
|
|
||||||
|> SynExpr.ifThenElse cond SynExpr.reraise
|
|
||||||
|
|
||||||
basic
|
|
||||||
|> SynExpr.pipeThroughTryWith
|
|
||||||
(SynPat.IsInst (
|
|
||||||
SynType.LongIdent (SynLongIdent.createS' [ "System" ; "InvalidOperationException" ]),
|
|
||||||
range0
|
|
||||||
))
|
|
||||||
handler
|
|
||||||
| PrimitiveType typeName -> asValueGetValueIdent propertyName typeName node
|
| PrimitiveType typeName -> asValueGetValueIdent propertyName typeName node
|
||||||
| OptionType ty ->
|
| OptionType ty ->
|
||||||
parseNode None options ty (SynExpr.createIdent "v")
|
parseNode None options ty (SynExpr.createIdent "v")
|
||||||
@@ -261,6 +268,14 @@ module internal JsonParseGenerator =
|
|||||||
|> SynExpr.callMethod "ToJsonString"
|
|> SynExpr.callMethod "ToJsonString"
|
||||||
|> SynExpr.paren
|
|> SynExpr.paren
|
||||||
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Numerics" ; "BigInteger" ; "Parse" ])
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Numerics" ; "BigInteger" ; "Parse" ])
|
||||||
|
| Measure (_measure, primType) ->
|
||||||
|
let qualified =
|
||||||
|
match Primitives.qualifyType primType with
|
||||||
|
| None -> failwith $"did not recognise type %s{primType} to assign measure"
|
||||||
|
| Some t -> t
|
||||||
|
|
||||||
|
parseNumberType options propertyName node primType
|
||||||
|
|> SynExpr.pipeThroughFunction (Measure.getLanguagePrimitivesMeasure qualified)
|
||||||
| _ ->
|
| _ ->
|
||||||
// Let's just hope that we've also got our own type annotation!
|
// Let's just hope that we've also got our own type annotation!
|
||||||
let typeName =
|
let typeName =
|
||||||
|
24
WoofWare.Myriad.Plugins/Measure.fs
Normal file
24
WoofWare.Myriad.Plugins/Measure.fs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal Measure =
|
||||||
|
|
||||||
|
let getLanguagePrimitivesMeasure (typeName : LongIdent) : SynExpr =
|
||||||
|
match typeName |> List.map _.idText with
|
||||||
|
| [ "System" ; "Single" ] -> [ "LanguagePrimitives" ; "Float32WithMeasure" ]
|
||||||
|
| [ "System" ; "Double" ] -> [ "LanguagePrimitives" ; "FloatWithMeasure" ]
|
||||||
|
| [ "System" ; "Byte" ] -> [ "LanguagePrimitives" ; "ByteWithMeasure" ]
|
||||||
|
| [ "System" ; "SByte" ] -> [ "LanguagePrimitives" ; "SByteWithMeasure" ]
|
||||||
|
| [ "System" ; "Int16" ] -> [ "LanguagePrimitives" ; "Int16WithMeasure" ]
|
||||||
|
| [ "System" ; "Int32" ] -> [ "LanguagePrimitives" ; "Int32WithMeasure" ]
|
||||||
|
| [ "System" ; "Int64" ] -> [ "LanguagePrimitives" ; "Int64WithMeasure" ]
|
||||||
|
| [ "System" ; "UInt16" ] -> [ "LanguagePrimitives" ; "UInt16WithMeasure" ]
|
||||||
|
| [ "System" ; "UInt32" ] -> [ "LanguagePrimitives" ; "UInt32WithMeasure" ]
|
||||||
|
| [ "System" ; "UInt64" ] -> [ "LanguagePrimitives" ; "UInt64WithMeasure" ]
|
||||||
|
| l ->
|
||||||
|
let l = String.concat "." l
|
||||||
|
failwith $"unrecognised type for measure: %s{l}"
|
||||||
|
|
||||||
|
|> SynExpr.createLongIdent
|
@@ -87,6 +87,20 @@ module internal SynExpr =
|
|||||||
)
|
)
|
||||||
|> applyTo b
|
|> applyTo b
|
||||||
|
|
||||||
|
/// {a} * {b}
|
||||||
|
let times (a : SynExpr) (b : SynExpr) =
|
||||||
|
SynExpr.CreateAppInfix (
|
||||||
|
SynExpr.CreateLongIdent (
|
||||||
|
SynLongIdent.SynLongIdent (
|
||||||
|
Ident.CreateLong "op_Multiply",
|
||||||
|
[],
|
||||||
|
[ Some (IdentTrivia.OriginalNotation "*") ]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
a
|
||||||
|
)
|
||||||
|
|> applyTo b
|
||||||
|
|
||||||
let rec stripOptionalParen (expr : SynExpr) : SynExpr =
|
let rec stripOptionalParen (expr : SynExpr) : SynExpr =
|
||||||
match expr with
|
match expr with
|
||||||
| SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr
|
| SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr
|
||||||
|
@@ -197,6 +197,17 @@ module internal SynTypePatterns =
|
|||||||
| _ -> None
|
| _ -> None
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
let (|Measure|_|) (fieldType : SynType) : (Ident * string) option =
|
||||||
|
match fieldType with
|
||||||
|
| SynType.App (NumberType outer,
|
||||||
|
_,
|
||||||
|
[ SynType.LongIdent (SynLongIdent.SynLongIdent ([ ident ], _, _)) ],
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_) -> Some (ident, outer)
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
let (|DateOnly|_|) (fieldType : SynType) =
|
let (|DateOnly|_|) (fieldType : SynType) =
|
||||||
match fieldType with
|
match fieldType with
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
||||||
|
@@ -46,6 +46,7 @@
|
|||||||
<Compile Include="SynExpr\SynAttribute.fs" />
|
<Compile Include="SynExpr\SynAttribute.fs" />
|
||||||
<Compile Include="SynExpr\SynModuleDecl.fs" />
|
<Compile Include="SynExpr\SynModuleDecl.fs" />
|
||||||
<Compile Include="SynExpr\SynModuleOrNamespace.fs" />
|
<Compile Include="SynExpr\SynModuleOrNamespace.fs" />
|
||||||
|
<Compile Include="Measure.fs" />
|
||||||
<Compile Include="AstHelper.fs" />
|
<Compile Include="AstHelper.fs" />
|
||||||
<Compile Include="RemoveOptionsGenerator.fs"/>
|
<Compile Include="RemoveOptionsGenerator.fs"/>
|
||||||
<Compile Include="InterfaceMockGenerator.fs"/>
|
<Compile Include="InterfaceMockGenerator.fs"/>
|
||||||
|
Reference in New Issue
Block a user