diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index ee4bb5e..3cf5904 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/CSharpTypes/CSharpTypes.csproj b/CSharpTypes/CSharpTypes.csproj index 4fda753..e6e0601 100644 --- a/CSharpTypes/CSharpTypes.csproj +++ b/CSharpTypes/CSharpTypes.csproj @@ -1,9 +1,9 @@  - net6.0 + net7.0 - \ No newline at end of file + diff --git a/CSharpTypes/OrderId.cs b/CSharpTypes/OrderId.cs index 0e1cac3..016ecd2 100644 --- a/CSharpTypes/OrderId.cs +++ b/CSharpTypes/OrderId.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using Saithe; using System; +using System.Diagnostics.CodeAnalysis; namespace CSharpTypes { @@ -8,7 +9,7 @@ namespace CSharpTypes /// Order identifier, simple wrapper around long value. Since it wraps long we need to use the JsonConverter /// [JsonConverter(typeof(ParseTypeJsonConverter))] - public struct OrderId : IEquatable + public struct OrderId : IEquatable, IParsable { public readonly long Value; @@ -38,7 +39,7 @@ public override string ToString() { return Value.ToString(); } - public static bool TryParse(string str, out OrderId result) + private static bool TryParse(string str, out OrderId result) { result = Empty; if (string.IsNullOrEmpty(str)) @@ -53,12 +54,16 @@ public static bool TryParse(string str, out OrderId result) } return false; } - public static OrderId Parse(string str) + private static OrderId Parse(string str) { OrderId res; if (TryParse(str, out res)) return res; throw new Exception("Could not parse product id"); } + + public static OrderId Parse(string s, IFormatProvider provider) => Parse(s); + + public static bool TryParse([NotNullWhen(true)] string s, IFormatProvider provider, [MaybeNullWhen(false)] out OrderId result) => TryParse(s, out result); } } diff --git a/CSharpTypes/ParseValueType.cs b/CSharpTypes/ParseValueType.cs index ed8aa63..c7b9f1c 100644 --- a/CSharpTypes/ParseValueType.cs +++ b/CSharpTypes/ParseValueType.cs @@ -1,11 +1,12 @@ -using System; +using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using Saithe; namespace CSharpTypes { [TypeConverter(typeof(ParseTypeConverter))] - public class ParseValueType : IEquatable + public class ParseValueType : IEquatable, IParsable { public readonly string Value; @@ -26,7 +27,7 @@ public static ParseValueType Parse(string value) public override bool Equals(object obj) { return Value.Equals(obj as ParseValueType); - } + } public override int GetHashCode() { return Value.GetHashCode(); @@ -34,8 +35,21 @@ public override int GetHashCode() public bool Equals(ParseValueType other) { - if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(null, other)) return false; return Value.Equals(other.Value); } + + public static ParseValueType Parse(string s, IFormatProvider provider) => Parse(s); + + public static bool TryParse([NotNullWhen(true)] string s, IFormatProvider provider, [MaybeNullWhen(false)] out ParseValueType result) + { + try{ + result = Parse(s); + return true; + }catch(Exception){ + result = default; + return false; + } + } } } diff --git a/CSharpTypes/ProductId.cs b/CSharpTypes/ProductId.cs index 6b33d51..b7d3790 100644 --- a/CSharpTypes/ProductId.cs +++ b/CSharpTypes/ProductId.cs @@ -1,11 +1,12 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using Saithe; namespace CSharpTypes { [TypeConverter(typeof(ParseTypeConverter))] - public struct ProductId: IEquatable + public struct ProductId: IEquatable, IParsable { public readonly long Value; @@ -35,7 +36,7 @@ public override string ToString() { return $"ProductId/{Value}"; } - public static bool TryParse(string str, out ProductId result) + private static bool TryParse(string str, out ProductId result) { result = Empty; if (string.IsNullOrEmpty(str)) @@ -53,12 +54,16 @@ public static bool TryParse(string str, out ProductId result) } return false; } - public static ProductId Parse(string str) + private static ProductId Parse(string str) { ProductId res; if (TryParse(str, out res)) return res; throw new Exception("Could not parse product id"); } + + public static ProductId Parse(string s, IFormatProvider provider) => Parse(s); + + public static bool TryParse([NotNullWhen(true)] string s, IFormatProvider provider, [MaybeNullWhen(false)] out ProductId result) => TryParse(s, out result); } } diff --git a/MvcApp/Models.fs b/MvcApp/Models.fs index e4fc57c..44deaa0 100644 --- a/MvcApp/Models.fs +++ b/MvcApp/Models.fs @@ -27,29 +27,58 @@ let parseId prefix str = | None -> raise (FormatException str) [] -[>)>] +[)>] type CustomerId = { Value : Guid } static member Default : CustomerId = { Value=Guid.Empty } static member Parse(str : string) : CustomerId = { Value = parseId "c-" str } override this.ToString() = sprintf "c-%s" (toStr this.Value) + interface IParsable with + static member Parse(s:string, f:IFormatProvider) = CustomerId.Parse(s) + static member TryParse(s:string, f:IFormatProvider, result:byref) = + try + result <- CustomerId.Parse(s) + true + with _ -> + result <- Unchecked.defaultof<_> + false +and private CustomerId_T1 = ParseTypeConverter [] -[>)>] +[)>] type ProductId = { Value : Guid } static member Default : ProductId = { Value=Guid.Empty } static member Parse(str : string) : ProductId = { Value = parseId "p-" str } override this.ToString() = sprintf "p-%s" (toStr this.Value) + interface IParsable with + static member Parse(s:string, f:IFormatProvider) = ProductId.Parse(s) + static member TryParse(s:string, f:IFormatProvider, result:byref) = + try + result <- ProductId.Parse(s) + true + with _ -> + result <- Unchecked.defaultof<_> + false +and private ProductId_T1 = ParseTypeConverter [] -[>)>] +[)>] type OrderId = { Value : Guid } static member Default : OrderId = { Value=Guid.Empty } static member Parse(str : string) : OrderId = { Value = parseId "o-" str } override this.ToString() = sprintf "o-%s" (toStr this.Value) - + interface IParsable with + static member Parse(s:string, f:IFormatProvider) = OrderId.Parse(s) + static member TryParse(s:string, f:IFormatProvider, result:byref) = + try + result <- OrderId.Parse(s) + true + with _ -> + result <- Unchecked.defaultof<_> + false +and private OrderId_T1 = ParseTypeConverter type Customer = {Id:CustomerId; FirstName:string ; LastName:string; Version:int} diff --git a/MvcApp/MvcApp.fsproj b/MvcApp/MvcApp.fsproj index 5d398ab..c6e56c2 100644 --- a/MvcApp/MvcApp.fsproj +++ b/MvcApp/MvcApp.fsproj @@ -1,6 +1,6 @@  - net6.0 + net7.0 @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/Saithe/ParseTypeConverters.fs b/Saithe/ParseTypeConverters.fs index 987357c..f64e3c9 100644 --- a/Saithe/ParseTypeConverters.fs +++ b/Saithe/ParseTypeConverters.fs @@ -4,16 +4,18 @@ open System.ComponentModel open System open System.Reflection open Newtonsoft.Json +module internal MethodInfos= + let matchParse (t:MethodInfo) = t.Name<>null && (t.Name.Equals("Parse") || t.Name.EndsWith(".Parse")) && t.GetParameters().Length = 2 -type ParseTypeConverter<'T (*when 'T :> IParsable<'T>*) >() = //when 'T : (static member parse : string -> 'T) +type ParseTypeConverter<'T when 'T :> IParsable<'T> >() = inherit TypeConverter() let strT = typeof let t = typeof<'T> - let parse_method = t.GetTypeInfo().GetMethod("Parse") + let parse_method = t.GetMethods() |> Array.find MethodInfos.matchParse let parse s = try - box (parse_method.Invoke(null, [| s |])) + box (parse_method.Invoke(null, [| s; null |])) with :? TargetInvocationException as e -> raise (e.GetBaseException()) override this.CanConvertFrom(context, sourceType) = (strT = sourceType || sourceType = t) @@ -30,15 +32,15 @@ type ParseTypeConverter<'T (*when 'T :> IParsable<'T>*) >() = //when 'T : (stati if destinationType = t then box (parse value) else box (value.ToString()) -type public ParseTypeJsonConverter<'T>() = +type public ParseTypeJsonConverter<'T when 'T :> IParsable<'T> >() = inherit JsonConverter() let t = typeof<'T> - let parse_method = t.GetTypeInfo().GetMethod("Parse") + let parse_method = t.GetMethods() |> Array.find MethodInfos.matchParse let parse s = try - box (parse_method.Invoke(null, [| s |])) + box (parse_method.Invoke(null, [| s ; null |])) with :? TargetInvocationException as e -> raise (e.GetBaseException()) override this.CanConvert(objectType) = objectType = t diff --git a/Saithe/Saithe.fsproj b/Saithe/Saithe.fsproj index 60ac0a1..711cfc7 100644 --- a/Saithe/Saithe.fsproj +++ b/Saithe/Saithe.fsproj @@ -1,6 +1,6 @@  - netstandard2.0 + net7.0 Saithe wallymathieu diff --git a/Tests/Handle_discriminated_union.fs b/Tests/Handle_discriminated_union.fs index 2159a16..21ae781 100644 --- a/Tests/Handle_discriminated_union.fs +++ b/Tests/Handle_discriminated_union.fs @@ -7,9 +7,8 @@ open Newtonsoft.Json open System.ComponentModel open System.Globalization - -[>)>] -[>)>] +[)>] +[)>] type ParseValueType = | ValueType of string | Empty @@ -24,6 +23,18 @@ type ParseValueType = match this with | Empty -> "" | ValueType value -> sprintf "P_%s" value + interface IParsable with + static member Parse(s:string, f:IFormatProvider) = ParseValueType.Parse(s) + static member TryParse(s:string, f:IFormatProvider, result:byref) = + try + result <- ParseValueType.Parse(s) + true + with _ -> + result <- Unchecked.defaultof<_> + false +and private ParseValueType_T1 = ParseTypeConverter +and private ParseValueType_T2 = ParseTypeJsonConverter + [] [] diff --git a/Tests/Parse_fs_type.fs b/Tests/Parse_fs_type.fs index 0000fff..ad139ea 100644 --- a/Tests/Parse_fs_type.fs +++ b/Tests/Parse_fs_type.fs @@ -6,7 +6,7 @@ open Newtonsoft.Json open System.ComponentModel open System.Globalization -[>)>] +[)>] type ParseValueType={ Value:string } with static member Parse (str:string)= @@ -15,6 +15,16 @@ with | _ -> raise (FormatException str) override this.ToString()= sprintf "P_%s" this.Value + interface IParsable with + static member Parse(s:string, f:IFormatProvider) = ParseValueType.Parse(s) + static member TryParse(s:string, f:IFormatProvider, result:byref) = + try + result <- ParseValueType.Parse(s) + true + with _ -> + result <- Unchecked.defaultof<_> + false +and private ParseValueType_T1 = ParseTypeConverter [] [] diff --git a/Tests/Tests.fsproj b/Tests/Tests.fsproj index 2b07e5f..c73bc48 100644 --- a/Tests/Tests.fsproj +++ b/Tests/Tests.fsproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 Tests Tests @@ -27,4 +27,4 @@ - \ No newline at end of file + diff --git a/appveyor.yml b/appveyor.yml index 9acad0f..d138369 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,6 @@ image: Visual Studio 2022 +install: + - cmd: choco install dotnetcore-sdk --pre -y build_script: - dotnet restore diff --git a/global.json b/global.json index 7ecd3c2..17a759e 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,7 @@ { "sdk": { - "version": "6.0.0", - "rollForward": "latestFeature" + "version": "7.0.0", + "rollForward": "latestFeature", + "allowPrerelease": true } }