diff --git a/CSharpPlus.sln b/CSharpPlus.sln index b5d1f06..fe5425f 100644 --- a/CSharpPlus.sln +++ b/CSharpPlus.sln @@ -13,8 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpPlus.Tests", "tests\C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "_build", "build\_build.csproj", "{6492F1AE-1F55-410A-8052-EBC1572FCD0B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlayGround", "PlayGround\PlayGround.csproj", "{DE61D8F2-82FB-4F7E-A93A-A90EF0DA41C9}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,10 +29,6 @@ Global {E54ABC87-5074-4EE7-A220-53E46986428A}.Release|Any CPU.Build.0 = Release|Any CPU {6492F1AE-1F55-410A-8052-EBC1572FCD0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6492F1AE-1F55-410A-8052-EBC1572FCD0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE61D8F2-82FB-4F7E-A93A-A90EF0DA41C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DE61D8F2-82FB-4F7E-A93A-A90EF0DA41C9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DE61D8F2-82FB-4F7E-A93A-A90EF0DA41C9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE61D8F2-82FB-4F7E-A93A-A90EF0DA41C9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -45,6 +39,5 @@ Global GlobalSection(NestedProjects) = preSolution {143B8D92-AE6B-486A-90B7-117F38F63C88} = {0BAC928C-339F-4FB4-A854-386C901B6B0F} {E54ABC87-5074-4EE7-A220-53E46986428A} = {18DDE0D0-38EA-4936-8AD2-03ED44A38053} - {DE61D8F2-82FB-4F7E-A93A-A90EF0DA41C9} = {0BAC928C-339F-4FB4-A854-386C901B6B0F} EndGlobalSection EndGlobal diff --git a/PlayGround/PlayGround.csproj b/PlayGround/PlayGround.csproj deleted file mode 100644 index e418144..0000000 --- a/PlayGround/PlayGround.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - diff --git a/PlayGround/Program.cs b/PlayGround/Program.cs deleted file mode 100644 index 4ed76c6..0000000 --- a/PlayGround/Program.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.ComponentModel; -using System.Runtime.Serialization; -using System.Text.Json; -using System.Text.Json.Serialization; - -Console.WriteLine("Hello, World!"); - -var persons = new Person[] -{ - new(1, "Lucas", FooType.Foo1), new(2, "Teles", FooType.Foo2) -}; - -var values = Enum.GetValues(typeof(FooType)).GetEnumerator().ToEnumerable().ToArray(); - -var foo = EnumerationExtensions.GetDescription(values[0]!); -var bar = EnumerationExtensions.GetEnumMemberValue(values[1]!); -Console.WriteLine($"{foo} and {bar}"); - -var json = JsonSerializer.Serialize(persons); -Console.WriteLine(json); - -var back = JsonSerializer.Deserialize(json); -foreach (var p in back ?? Array.Empty()) - Console.WriteLine(p.ToString()); - - -public record Person(int Id, string Name, FooType Foo); - -[JsonEnumDescription] -public enum FooType : long -{ - [Description("The Foo number 1")] - Foo1 = 1, - - [EnumMember(Value = "The Foo number 2")] - Foo2, -} diff --git a/src/JsonConverters/Attributes.cs b/src/JsonConverters/Attributes.cs deleted file mode 100644 index c92e0ba..0000000 --- a/src/JsonConverters/Attributes.cs +++ /dev/null @@ -1,74 +0,0 @@ -// ReSharper disable once CheckNamespace - -namespace System.Text.Json.Serialization; - -/// -/// Sets enum to use default string value for json serialization -/// -[AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | - AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field)] -public sealed class JsonEnumStringAttribute : JsonConverterAttribute -{ - /// - public JsonEnumStringAttribute() : base(typeof(JsonStringEnumConverter)) - { - } -} - -/// -/// Sets enum to use description for json serialization -/// -[AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | - AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field)] -public sealed class JsonEnumDescriptionAttribute : JsonConverterAttribute -{ - /// - public JsonEnumDescriptionAttribute() : - base(typeof(JsonDescriptionEnumConverter)) - { - } -} - -/// -/// Sets enum to use enum member value for json serialization -/// -[AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | - AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field)] -public sealed class JsonEnumMemberValueAttribute : JsonConverterAttribute -{ - /// - public JsonEnumMemberValueAttribute() : base(typeof(JsonEnumMemberValueConverter)) - { - } -} - -/// -/// Sets enum to use string numeric values, for json serialization -/// -[AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | - AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field)] -public sealed class JsonEnumNumericAsStringAttribute : JsonConverterAttribute -{ - /// - public JsonEnumNumericAsStringAttribute() : base(typeof(JsonEnumNumericAsStringConverter)) - { - } -} - -/// -/// Sets enum to use numeric values for json serialization -/// -[AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | - AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field)] -public sealed class JsonEnumNumericAttribute : JsonConverterAttribute -{ - /// - public JsonEnumNumericAttribute() : base(typeof(JsonNumericEnumConverter)) - { - } -} diff --git a/src/JsonConverters/Base/Converters.cs b/src/JsonConverters/Base/Converters.cs deleted file mode 100644 index 18aeea9..0000000 --- a/src/JsonConverters/Base/Converters.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; - -namespace CSharpPlus.JsonConverters.Base; - -class JsonEnumMemberValueConverter : JsonEnumCustomStringConverter - where TEnum : struct, Enum -{ - public JsonEnumMemberValueConverter(StringComparison comparison = StringComparison.Ordinal) : - base(comparison) - { - } - - protected override string GetCustomString(TEnum value) => value.GetEnumMemberValue(); - - protected override TEnum? GetValueFromString(string value, StringComparison comparison) => - EnumerationExtensions.GetEnumFromEnumMemberValue(value, comparison); -} - -class JsonEnumDescriptionConverter : JsonEnumCustomStringConverter - where TEnum : struct, Enum -{ - public JsonEnumDescriptionConverter(StringComparison comparison = StringComparison.Ordinal) : - base(comparison) - { - } - - protected override string GetCustomString(TEnum value) => value.GetDescription(); - - protected override TEnum? GetValueFromString(string value, StringComparison comparison) => - EnumerationExtensions.GetEnumFromDescription(value, comparison); -} - -class JsonEnumNumericAsStringConverter : JsonEnumCustomStringConverter - where TEnum : struct, Enum -{ - public JsonEnumNumericAsStringConverter(StringComparison comparison = StringComparison.Ordinal) - : - base(comparison) - { - } - - protected override string GetCustomString(TEnum value) => - value.ToString("d"); - - protected override TEnum? GetValueFromString(string value, StringComparison comparison) => - Enum.Parse(value); -} diff --git a/src/JsonConverters/Base/JsonEnumCustomStringConverter.cs b/src/JsonConverters/Base/JsonEnumCustomStringConverter.cs deleted file mode 100644 index 0667b21..0000000 --- a/src/JsonConverters/Base/JsonEnumCustomStringConverter.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace CSharpPlus.JsonConverters.Base; - -abstract class JsonEnumCustomStringConverter : JsonConverter - where TEnum : struct, Enum -{ - readonly StringComparison comparison; - - protected abstract string GetCustomString(TEnum value); - protected abstract TEnum? GetValueFromString(string value, StringComparison comparison); - - protected JsonEnumCustomStringConverter( - StringComparison comparison = StringComparison.Ordinal) => - this.comparison = comparison; - - public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) => - writer.WriteStringValue(GetCustomString(value)); - - public override TEnum Read( - ref Utf8JsonReader reader, Type typeToConvert, - JsonSerializerOptions options) - { - if (reader.TokenType is not JsonTokenType.String) - throw new InvalidOperationException("Invalid enum description"); - - var enumString = reader.GetString(); - - if (enumString is null || - GetValueFromString(enumString, comparison) is not { } value) - throw new InvalidOperationException("Invalid enum description"); - - return value; - } -} - -/// -/// base string enum converter -/// -public abstract class JsonEnumCustomStringConverterFactory : JsonConverterFactory -{ - readonly StringComparison comparison; - - /// - /// Construct converter - /// - /// - /// string comparison to determine if the string matches - /// - protected JsonEnumCustomStringConverterFactory( - StringComparison stringComparison) => - this.comparison = stringComparison; - - /// - /// Construct converter - /// uses string comparison to determine if the string matches - /// - protected JsonEnumCustomStringConverterFactory() : this(StringComparison.Ordinal) - { - // An empty constructor is needed for construction via attributes - } - - /// - public sealed override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum; - - /// - /// Converter Type Factory - /// - /// - /// - protected abstract Type GetCustomConverter(Type typeToConvert); - - /// - public sealed override JsonConverter CreateConverter(Type typeToConvert, - JsonSerializerOptions options) => - (JsonConverter)Activator.CreateInstance(GetCustomConverter(typeToConvert), comparison)!; -} diff --git a/src/JsonConverters/Base/JsonNumericEnumValueConverter.cs b/src/JsonConverters/Base/JsonNumericEnumValueConverter.cs deleted file mode 100644 index eb08107..0000000 --- a/src/JsonConverters/Base/JsonNumericEnumValueConverter.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace CSharpPlus.JsonConverters.Base; - -class JsonNumericEnumValueConverter : JsonConverter - where TEnum : struct, Enum -{ - readonly Type underlyingType; - - public JsonNumericEnumValueConverter() => - underlyingType = Enum.GetUnderlyingType(typeof(TEnum)); - - public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) - { - var box = (object)value; - if (underlyingType == typeof(int)) writer.WriteNumberValue((int)box); - else if (underlyingType == typeof(uint)) writer.WriteNumberValue((uint)box); - else if (underlyingType == typeof(long)) writer.WriteNumberValue((long)box); - else if (underlyingType == typeof(ulong)) writer.WriteNumberValue((ulong)box); - else if (underlyingType == typeof(byte)) writer.WriteNumberValue((byte)box); - else if (underlyingType == typeof(sbyte)) writer.WriteNumberValue((sbyte)box); - else if (underlyingType == typeof(short)) writer.WriteNumberValue((short)box); - else if (underlyingType == typeof(ushort)) writer.WriteNumberValue((ushort)box); - else - throw new InvalidOperationException( - $"Writing {value} to numeric enum {typeof(TEnum).Name} as {underlyingType.Name}"); - } - - public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, - JsonSerializerOptions options) - { - if (underlyingType == typeof(int)) return (TEnum)(object)reader.GetInt32(); - if (underlyingType == typeof(uint)) return (TEnum)(object)reader.GetUInt32(); - if (underlyingType == typeof(long)) return (TEnum)(object)reader.GetInt64(); - if (underlyingType == typeof(ulong)) return (TEnum)(object)reader.GetUInt64(); - if (underlyingType == typeof(byte)) return (TEnum)(object)reader.GetByte(); - if (underlyingType == typeof(sbyte)) return (TEnum)(object)reader.GetSByte(); - if (underlyingType == typeof(short)) return (TEnum)(object)reader.GetInt16(); - if (underlyingType == typeof(ushort)) return (TEnum)(object)reader.GetUInt16(); - throw new InvalidOperationException( - $"Reading {reader.GetString()} to numeric enum {typeof(TEnum).Name} as {underlyingType.Name}"); - } -} diff --git a/src/JsonConverters/JsonConverterFactories.cs b/src/JsonConverters/JsonConverterFactories.cs deleted file mode 100644 index cb553e8..0000000 --- a/src/JsonConverters/JsonConverterFactories.cs +++ /dev/null @@ -1,84 +0,0 @@ -using CSharpPlus.JsonConverters.Base; - -// ReSharper disable once CheckNamespace -namespace System.Text.Json.Serialization; - -using System; - -/// -/// Converter for json using description attribute -/// -public sealed class JsonDescriptionEnumConverter : JsonEnumCustomStringConverterFactory -{ - /// - public JsonDescriptionEnumConverter(StringComparison comparison) : - base(comparison) - { - } - - /// - public JsonDescriptionEnumConverter() : - base(StringComparison.Ordinal) - { - } - - /// - protected override Type GetCustomConverter(Type typeToConvert) => - typeof(JsonEnumDescriptionConverter<>) - .MakeGenericType(typeToConvert); -} - -/// -/// Converter for json using description attribute -/// -public sealed class JsonEnumMemberValueConverter : JsonEnumCustomStringConverterFactory -{ - /// - public JsonEnumMemberValueConverter(StringComparison comparison) : - base(comparison) - { - } - - /// - public JsonEnumMemberValueConverter() : - base(StringComparison.Ordinal) - { - } - - /// - protected override Type GetCustomConverter(Type typeToConvert) => - typeof(JsonEnumMemberValueConverter<>) - .MakeGenericType(typeToConvert); -} - -/// -/// Converter for json using description attribute -/// -public sealed class JsonEnumNumericAsStringConverter : JsonEnumCustomStringConverterFactory -{ - /// - public JsonEnumNumericAsStringConverter() : base(StringComparison.Ordinal) - { - } - - /// - protected override Type GetCustomConverter(Type typeToConvert) => - typeof(JsonEnumNumericAsStringConverter<>) - .MakeGenericType(typeToConvert); -} - -/// -/// Converter for json using description attribute -/// -public sealed class JsonNumericEnumConverter : JsonConverterFactory -{ - /// - public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum; - - /// - public override JsonConverter CreateConverter( - Type typeToConvert, JsonSerializerOptions options) => - (JsonConverter)Activator.CreateInstance( - typeof(JsonNumericEnumValueConverter<>) - .MakeGenericType(typeToConvert))!; -} diff --git a/src/Result/Exceptions.cs b/src/Result/Exceptions.cs deleted file mode 100644 index 0e93df3..0000000 --- a/src/Result/Exceptions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace CSharpPlus.Result; - -/// -/// The exception that is thrown when a invalid explicit cast is made on a result. -/// -public sealed class ResultInvalidCastException : InvalidOperationException -{ - /// - internal ResultInvalidCastException(string message) : base(message) - { - } -} - -/// -/// The exception that is thrown for invalid result values. -/// -public sealed class ResultInvalidException : InvalidOperationException -{ - /// - internal ResultInvalidException(string message) : base(message) - { - } -} diff --git a/src/Result/Json/ResultJsonTypeConverter.cs b/src/Result/Json/ResultJsonTypeConverter.cs deleted file mode 100644 index f11097a..0000000 --- a/src/Result/Json/ResultJsonTypeConverter.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace CSharpPlus.Result.Json; - -/// -public class ResultJsonConverterFactory : JsonConverterFactory -{ - /// - public override bool CanConvert(Type typeToConvert) => - typeToConvert.IsGenericType && - typeToConvert.GetGenericTypeDefinition() == typeof(Result<,>); - - /// - public override JsonConverter? CreateConverter( - Type typeToConvert, - JsonSerializerOptions options) - { - var okType = typeToConvert.GetGenericArguments()[0]; - var errorType = typeToConvert.GetGenericArguments()[1]; - - return Activator.CreateInstance( - typeof(ResultJsonConverter<,>).MakeGenericType(okType, errorType), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - args: new object[] - { - options, - }, - culture: null) as JsonConverter; - } -} - -/// -public class ResultJsonConverter : JsonConverter> - -{ - readonly JsonConverter okConverter; - readonly JsonConverter errorConverter; - readonly Type okType = typeof(TOk); - readonly Type errorType = typeof(TError); - - const string propertyStatusName = "status"; - const string propertyValueName = "value"; - const string statusOk = "ok"; - const string statusError = "error"; - - /// - public ResultJsonConverter(JsonSerializerOptions options) - { - okConverter = (JsonConverter)options.GetConverter(okType); - errorConverter = (JsonConverter)options.GetConverter(errorType); - } - - /// - public override Result Read( - ref Utf8JsonReader reader, Type typeToConvert, - JsonSerializerOptions options) - { - var json = JsonElement.ParseValue(ref reader); - - if (!json.TryGetProperty(options.PropertyNamingPolicy?.ConvertName(propertyStatusName) ?? - propertyStatusName, out var status)) - throw new JsonException("Invalid status property"); - if (!json.TryGetProperty(options.PropertyNamingPolicy?.ConvertName(propertyValueName) ?? - propertyValueName, out var value)) - throw new JsonException("Invalid value property"); - - Utf8JsonReader valueReader = new(JsonSerializer.SerializeToUtf8Bytes(value)); - valueReader.Read(); - if (status.GetString() == statusOk) - return new Result( - okConverter.Read(ref valueReader, okType, options)!); - - if (status.GetString() == statusError) - return new Result( - errorConverter.Read(ref valueReader, errorType, options)!); - - throw new JsonException($"Invalid status {status}"); - } - - /// - public override void Write( - Utf8JsonWriter writer, Result value, - JsonSerializerOptions options) - { - writer.WriteStartObject(); - - if (value.IsOk) - { - writer.WritePropertyName - (options.PropertyNamingPolicy?.ConvertName(propertyStatusName) ?? - propertyStatusName); - writer.WriteStringValue(statusOk); - - writer.WritePropertyName - (options.PropertyNamingPolicy?.ConvertName(propertyValueName) ?? - propertyValueName); - okConverter.Write(writer, value.OkValue, options); - } - else - { - writer.WritePropertyName - (options.PropertyNamingPolicy?.ConvertName(propertyStatusName) ?? - propertyStatusName); - writer.WriteStringValue(statusError); - - writer.WritePropertyName - (options.PropertyNamingPolicy?.ConvertName(propertyValueName) ?? - propertyValueName); - errorConverter.Write(writer, value.ErrorValue, options); - } - - writer.WriteEndObject(); - } -} diff --git a/src/Result/Result.cs b/src/Result/Result.cs deleted file mode 100644 index 436b90e..0000000 --- a/src/Result/Result.cs +++ /dev/null @@ -1,327 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using CSharpPlus.Result.Json; - -namespace CSharpPlus.Result; - -/// -/// Helper type for errorValue handling without exceptions. -/// -[DebuggerDisplay("{DebuggerDisplay(),nq}")] -[System.Text.Json.Serialization.JsonConverter(typeof(ResultJsonConverterFactory))] -public readonly struct Result : IEquatable> -{ - internal TOk? OkValue { get; } - internal TError? ErrorValue { get; } - - /// - /// Returns true if the result is Ok. - /// - [MemberNotNullWhen(true, nameof(OkValue))] - [MemberNotNullWhen(false, nameof(ErrorValue))] - public bool IsOk { get; } - - /// - /// Returns true if the result is Error. - /// - [MemberNotNullWhen(false, nameof(OkValue))] - [MemberNotNullWhen(true, nameof(ErrorValue))] - public bool IsError => !IsOk; - - /// - /// The result value - /// - public object Value => IsOk ? OkValue : ErrorValue; - - /// - /// Represents an OK or a Successful result. The code succeeded with a value of 'T - /// - public Result(TOk okValue) - { - IsOk = true; - this.OkValue = okValue; - this.ErrorValue = default; - } - - /// - /// Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong. - /// - public Result(TError error) - { - IsOk = false; - this.OkValue = default; - this.ErrorValue = error; - } - - /// - /// Represents an OK or a Successful result. The code succeeded with a value of 'T - /// - public static Result Ok(TOk result) => new(result); - - /// - /// Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong. - /// - public static Result Error(TError error) => new(error); - - /// - /// Casts an Ok value to Result - /// - public static implicit operator Result(TOk value) => new(value); - - /// - /// Unsafely casts a Result to Ok value - /// - public static explicit operator TOk(Result value) => - value.IsOk - ? value.OkValue - : throw new ResultInvalidCastException( - $"Unable to cast 'Error' result value {value.ErrorValue} of type {typeof(TError).FullName} to type {typeof(TOk).FullName}"); - - /// - /// Unsafely casts a Result to Error value - /// - public static explicit operator TError(Result value) => - value.IsError - ? value.ErrorValue - : throw new ResultInvalidCastException( - $"Unable to cast 'Ok' result value {value.OkValue} of type {typeof(TOk).FullName} to type {typeof(TError).FullName}"); - - /// - /// Casts an Error value to Result - /// - public static implicit operator Result(TError value) => new(value); - - /// - /// Compare Results - /// - public static bool operator ==(Result left, Result right) => - left.Equals(right); - - /// - /// Compare Results - /// - public static bool operator !=(Result left, Result right) => - !(left == right); - - /// - public bool Equals(Result other) => - IsOk == other.IsOk && - EqualityComparer.Default.Equals(OkValue, other.OkValue) && - EqualityComparer.Default.Equals(ErrorValue, other.ErrorValue); - - /// - public override bool Equals(object? obj) => obj is Result other && Equals(other); - - /// - public override int GetHashCode() => HashCode.Combine(OkValue, ErrorValue, IsOk); - - string DebuggerDisplay() => IsOk ? $"Ok({OkValue})" : $"Error({ErrorValue})"; - - /// - public override string? ToString() => IsOk ? OkValue.ToString() : ErrorValue.ToString(); - - /// - /// Convert the result to an enumerable of length 0 or 1. - /// - /// - public IEnumerable AsEnumerable() - { - if (IsOk) - yield return OkValue; - } - - /// - /// Convert the result to an array of length 0 or 1. - /// - public TOk[] ToArray() => AsEnumerable().ToArray(); - - /// - /// Match the result to obtain the value - /// - public T Match(Func ok, Func error) => - IsOk ? ok(this.OkValue) : error(this.ErrorValue); - - /// - /// Switch the result to process value - /// - public void Switch(Action ok, Action error) - { - if (IsOk) - ok(this.OkValue); - else - error(this.ErrorValue); - } - - /// - /// Get value if result is Ok otherwise throws - /// - public TOk GetValueOrThrow() - { - if (IsError) - if (ErrorValue is Exception exception) - throw exception; - else - throw new ResultInvalidException($"{ErrorValue}"); - - return OkValue; - } - - /// - /// Get value if result is Ok otherwise throws - /// - public TOk GetValueOrThrow(Func formatMessage) - { - if (IsError) - throw new ResultInvalidException(formatMessage(ErrorValue)); - - return OkValue; - } - - /// - /// Get value if result is Ok otherwise throws - /// - public TOk GetValueOrThrow(Func getException) - { - if (IsError) - throw getException(ErrorValue); - - return OkValue; - } - - /// - /// Throws on error result - /// - public void ThrowIfError() - { - if (!IsError) return; - - if (ErrorValue is Exception exception) - throw exception; - - throw new ResultInvalidException($"{ErrorValue}"); - } - - /// - /// Get value if result is Ok otherwise throws - /// - public void ThrowIfError(Func formatMessage) - { - if (IsError) - throw new ResultInvalidException(formatMessage(ErrorValue)); - } - - /// - /// Get value if result is Ok otherwise throws - /// - public void ThrowIfError(Func getException) - { - if (IsError) - throw getException(ErrorValue); - } - - - /// - /// Attempts to extract value from container if it is present. - /// - /// Extracted value. - /// if value is present; otherwise, . - public bool TryGet([NotNullWhen(true)] out TOk? value) - { - value = this.OkValue; - return IsOk; - } - - /// - /// Attempts to extract value from container if it is present. - /// - /// Extracted value. - /// Extracted error. - /// if value is present; otherwise, . - public bool TryGet( - [NotNullWhen(true)] out TOk? value, - [NotNullWhen(false)] out TError? error - ) - { - value = this.OkValue; - error = this.ErrorValue; - return IsOk; - } - - /// - /// Projects ok result value into a new form. - /// - public Result Select(Func selector) => Match( - ok => new Result(selector(ok)), - error => new(error) - ); - - /// - /// Projects ok and error result values into a new form. - /// - public Result Select( - Func okSelector, - Func errorSelector - ) => Match( - ok => new Result(okSelector(ok)), - error => new(errorSelector(error)) - ); - - /// - /// Projects error result element into a new form. - /// - public Result SelectError(Func selector) => - Match( - ok => new Result(ok), - error => new(selector(error)) - ); - - /// - /// Projects ok result value into a new flattened form. - /// - public Result SelectMany(Func> bind) => - IsError ? new Result(ErrorValue) : bind(OkValue); - - /// - /// Projects ok result value into a new flattened form. - /// - public Result SelectMany( - Func> bind, - Func project) => - SelectMany(a => bind(a).Select(b => project(a, b))); - - /// - /// Gets the value of the result if the result is Ok, otherwise returns the specified default value. - /// - public TOk DefaultValue(TOk value) => Match(ok => ok, _ => value); - - /// - /// Gets the value of the result if the result is Ok, otherwise evaluates defThunk and returns the result - /// - public TOk DefaultWith(Func defThunk) => Match(ok => ok, defThunk); - - /// - /// Maps a Result value from a pair of Result values. - /// - public Result Zip( - Result other, - Func selector) - { - if (this.IsError) - return new Result(this.ErrorValue); - - if (other.IsError) - return new Result(other.ErrorValue); - - return new(selector(OkValue, other.OkValue)); - } - - /// - /// Creates a Result value from a pair of Result values. - /// - public Result<(TOk, TOther), TError> Zip(Result other) => - Zip(other, (a, b) => (a, b)); -} diff --git a/src/Result/ResultExtensions.cs b/src/Result/ResultExtensions.cs deleted file mode 100644 index 9dfa6c6..0000000 --- a/src/Result/ResultExtensions.cs +++ /dev/null @@ -1,349 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace CSharpPlus.Result; - -/// -/// Helper type for errorValue handling without exceptions. -/// -public static class Result -{ - /// - /// Represents an OK or a Successful result. The code succeeded with a value of 'T - /// - public static Result Ok(TOk result) => - Result.Ok(result); - - /// - /// Represents an OK or a Successful result. The code succeeded with a value of 'T - /// - public static Result Ok(TOk result) => - Result.Ok(result); - - /// - /// Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong. - /// - public static Result Error(TError error) => - Result.Error(error); - - /// - /// Try run function, catching exceptions as a result error value - /// - public static Result Try(Func func) - { - try - { - return func(); - } - catch (Exception e) - { - return e; - } - } - - /// - /// Try run function, catching exceptions as a result error value - /// - public static async Task> TryAsync(Func> func) - { - try - { - return await func(); - } - catch (Exception e) - { - return e; - } - } - - - /// - /// Deconstructs result value into (IsOk, OkValue?, ErrorValue?) - /// - public static void Deconstruct( - this Result result, - out bool success, - out TOk? ok, - out TError? error) - where TOk : struct - where TError : struct - => (success, ok, error) = (result.IsOk, result.OkValue, result.ErrorValue); - - /// - /// Deconstructs result value into (IsOk, OkValue?, ErrorValue?) - /// - public static void Deconstruct( - this Result result, - out bool success, - out TOk? ok, - out TError? error) - where TOk : class - where TError : class - => (success, ok, error) = (result.IsOk, result.OkValue, result.ErrorValue); - - /// - /// Deconstructs result value into (IsOk, OkValue?, ErrorValue?) - /// - public static void Deconstruct( - this Result result, - out bool success, - out TOk? ok, - out TError? error) - where TOk : struct - where TError : class - => (success, ok, error) = (result.IsOk, result.OkValue, result.ErrorValue); - - /// - /// Deconstructs result value into (IsOk, OkValue?, ErrorValue?) - /// - public static void Deconstruct( - this Result result, - out bool success, - out TOk? ok, - out TError? error) - where TOk : class - where TError : struct - => (success, ok, error) = (result.IsOk, result.OkValue, result.ErrorValue); - - /// - /// Convert value type result to nullable - /// - public static Result AsNullable( - this Result result) - where TOk : struct => - result.Select(x => (TOk?)x); - - - /// - /// Convert result of task into task of result - /// - public static async Task> ToTask( - this Result, TError> result) - { - if (result.IsOk) return await result.OkValue; - return result.ErrorValue; - } - - /// - /// Convert result of task into task of result - /// - public static async Task> ToTask( - this Result> result) - { - if (result.IsOk) return result.OkValue; - return await result.ErrorValue; - } - - /// - /// Convert result of task into task of result - /// - public static async Task> ToTask( - this Result, Task> result) - { - if (result.IsOk) return await result.OkValue; - return await result.ErrorValue; - } - - - /// - /// Convert result of task into task of result - /// - public static Result, TError> ToResult( - this IEnumerable> results) - { - List okResults = new(); - foreach (var result in results) - { - if (result.IsOk) - okResults.Add(result.OkValue); - else - return new(result.ErrorValue); - } - - return new(okResults.ToReadOnlyList()); - } - - /// - /// Return new collection with ok values only - /// - public static IEnumerable Choose( - this IEnumerable> results) => - from result in results where result.IsOk select result.OkValue; - - /// - /// Return new collection with ok values only - /// - public static IEnumerable ChooseErrors( - this IEnumerable> results) => - from result in results where result.IsError select result.ErrorValue; - - /// - /// If a result is successful, returns it, otherwise . - /// - /// Nullable value. - public static T? ToNullable(this Result valueResult) - where T : struct - => - valueResult.IsOk ? valueResult.OkValue : null; - - /// - /// Switch the result to process value - /// - public static async Task Switch(this Result result, - Func ok, - Func error) - { - if (result.IsOk) - await ok(result.OkValue); - else - await error(result.ErrorValue); - } - - /// - /// Switch the result to process value - /// - public static async Task Switch(this Result result, - Func ok, Action error) - { - if (result.IsOk) - await ok(result.OkValue); - else - error(result.ErrorValue); - } - - /// - /// Switch the result to process value - /// - public static async Task Switch(this Result result, - Action ok, - Func error) - { - if (result.IsOk) - ok(result.OkValue); - else - await error(result.ErrorValue); - } - - - /// - /// Run side effect when result is OK - /// - public static Result Tap( - this Result result, Action action) - { - if (result.IsOk) action(result.OkValue); - return result; - } - - /// - /// Run side effect when result is OK - /// - public static async Task> Tap( - this Result result, Func action) - { - if (result.IsOk) await action(result.OkValue); - return result; - } - - /// - /// Match the result to obtain the value - /// - public static async Task Match(this Result result, - Func> ok, Func> error) => - result.IsOk ? await ok(result.OkValue) : await error(result.ErrorValue); - - /// - /// Match the result to obtain the value - /// - public static async Task Match(this Result result, - Func> ok, - Func error) => - result.IsOk ? await ok(result.OkValue) : error(result.ErrorValue); - - /// - /// Match the result to obtain the value - /// - public static async Task - Match(this Result result, - Func ok, - Func> error) => - result.IsOk ? ok(result.OkValue) : await error(result.ErrorValue); - - /// - /// Projects ok result value into a new form. - /// e - public static Task> SelectAsync( - this Result result, - Func> selector - ) => result.Select(selector).ToTask(); - - /// - /// Projects ok result value into a new form. - /// e - public static Task> SelectAsync( - this Result result, - Func> okSelector, - Func> errorSelector - ) => result.Select(okSelector, errorSelector).ToTask(); - - /// - /// Projects ok result value into a new form. - /// e - public static Task> SelectAsync( - this Result result, - Func okSelector, - Func> errorSelector - ) => result.Select(okSelector, errorSelector).ToTask(); - - /// - /// Projects ok result value into a new form. - /// e - public static Task> SelectAsync( - this Result result, - Func> okSelector, - Func errorSelector - ) => result.Select(okSelector, errorSelector).ToTask(); - - /// - /// Projects ok result value into a new form. - /// e - public static Task> SelectErrorAsync( - this Result result, - Func> selector - ) => result.SelectError(selector).ToTask(); - - - /// - /// Projects ok result value into a new flattened form. - /// - public static Task> SelectManyAsync( - this Result result, - Func, TError>> bind) => - result.SelectMany(bind).ToTask(); - - /// - /// Projects ok result value into a new flattened form. - /// - public static Task> SelectManyAsync( - this Result result, - Func>> bind) => - result.SelectError(Task.FromResult).SelectMany(bind).ToTask(); - - /// - /// Projects ok result value into a new flattened form. - /// - public static Task> SelectManyAsync( - this Result result, - Func, Task>> bind) => - result.SelectError(Task.FromResult).SelectMany(bind).ToTask(); - - /// - /// Projects ok result value into a new flattened form. - /// - public static async Task> SelectManyAsync( - this Result result, - Func>> bind) => - result.IsError ? new Result(result.ErrorValue) : await bind(result.OkValue); -} diff --git a/tests/CSharpPlus.Tests/ResultTests.cs b/tests/CSharpPlus.Tests/ResultTests.cs deleted file mode 100644 index 355942f..0000000 --- a/tests/CSharpPlus.Tests/ResultTests.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System.Text.Json; - -namespace CSharpPlus.Tests; - -using Result; - -public class ResultTests -{ - [Test] - public void ShouldCompareEqualBeTrue() - { - var ok1 = Result.Ok(42); - var ok2 = Result.Ok(42); - (ok1 == ok2).Should().BeTrue(); - } - - [Test] - public void ShouldCompareEqualBeFalse() - { - var ok1 = Result.Ok(42); - var ok2 = Result.Ok(99); - (ok1 == ok2).Should().BeFalse(); - } - - [Test] - public void ShouldSerializeOkResult() - { - var ok = Result.Ok(42); - var strJson = JsonSerializer.Serialize(ok); - var okFromJson = JsonSerializer.Deserialize>(strJson); - okFromJson.Should().Be(ok); - } - - [Test] - public void ShouldSerializeErrorResult() - { - var error = Result.Error("foo"); - var strJson = JsonSerializer.Serialize(error); - var errorFromJson = JsonSerializer.Deserialize>(strJson); - errorFromJson.Should().Be(error); - } - - [Test] - public void ShouldCombineOkResults() - { - var result = - from ok1 in Result.Ok(42) - from ok2 in Result.Ok(100) - select ok1 + ok2; - - result.Should().Be(Result.Ok(142)); - } - - [Test] - public void ShouldShorCircuitErrorResult() - { - var result = - from ok1 in Result.Ok(42) - from ok2 in Result.Error("FAIL") - from ok3 in Result.Ok(100) - select ok1 + ok2 + ok3; - result.Should().Be(Result.Error("FAIL")); - } - - [Test] - public void ShouldBeEnumerable() - { - foreach (var value in Result.Ok(42).AsEnumerable()) - if (value == 42) - Assert.Pass(); - - Assert.Fail("unexpected!"); - } - - [Test] - public void ShouldMatchPropertyOk() - { - if (Result.Ok(42) is { Value: 42 }) - Assert.Pass(); - - Assert.Fail("unexpected!"); - } - - [Test] - public void ShouldMatchTupleOk() - { - if (Result.Ok(42) is (true, 42, null)) - Assert.Pass(); - - Assert.Fail("unexpected!"); - } - - [Test] - public void ShouldMapValue() => - Result.Ok(42) - .Select(x => x.ToString()) - .Should().Be(Result.Ok("42")); - - [Test] - public void ShouldTryGet() - { - var result = Result.Ok(42); - - if (result.TryGet(out var value) && value == 42) - Assert.Pass(); - - Assert.Fail(); - } - - [Test] - public void ShouldTryGetOrError() - { - var result = Result.Error("BAD"); - - if (!result.AsNullable().TryGet(out var value, out var error)) - { - Assert.IsNull(value); - Assert.IsNotNull(error); - Assert.Pass(); - } - - Assert.Fail(); - } - - - [Test] - public async Task ShouldMapAsync() - { - var result = await Result.Ok(42).SelectAsync(Task.FromResult); - result.Should().Be(Result.Ok(42)); - } - - [Test] - public async Task ShouldBindAsync() - { - var result = await Result.Ok(42) - .SelectManyAsync(x => Task.FromResult(Result.Ok(x + 10))); - - result.Should().Be(Result.Ok(52)); - } -}