Compare commits

..

10 Commits

Author SHA1 Message Date
77396d252e Added Map and MapError
All checks were successful
.NET / build (push) Successful in 34s
2025-08-18 10:23:36 -04:00
d2fe7edbce null attributes 2025-05-09 17:12:08 -04:00
e60be738a9 Merge branch 'main' of https://github.com/Amatsugu/MaybeError 2025-04-19 15:05:53 -04:00
314b706d61 Added MaybeEx<T, Ex> for exception errors and implicit castings 2025-04-19 15:05:49 -04:00
e4129b1c09 Added MaybeEx<T, Ex> for exception errors and implicit castings 2025-04-19 15:05:15 -04:00
82428641b6 bump version 2025-04-03 13:50:47 -04:00
ce71765cb4 added implicit operator 2025-04-03 13:48:36 -04:00
ee5eeedb50 add tracing 2025-03-10 09:52:43 -04:00
27e891e80e fix attribute on HasValue 2025-02-14 14:09:24 -05:00
766eb8a8c2 bump version 2025-01-29 22:25:41 -05:00
16 changed files with 1590 additions and 1330 deletions

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -18,15 +19,23 @@ public class Error(string message, string? details = null, string? devDetails =
public readonly string? Details = details; public readonly string? Details = details;
public readonly string? DevDetails = devDetails; public readonly string? DevDetails = devDetails;
#if DEBUG
private readonly StackTrace _stackTrace = new();
#endif
public virtual Exception GetException() public virtual Exception GetException()
{ {
#if DEBUG
return new Exception(ToString());
#else
return new Exception(Message); return new Exception(Message);
#endif
} }
public override string ToString() public override string ToString()
{ {
#if DEBUG #if DEBUG
return $"{Message}\n{Details}\n{DevDetails}"; return $"{Message}\n{Details}\n{DevDetails}\nTrace:{_stackTrace}";
#else #else
return $"{Message}\n{Details}"; return $"{Message}\n{Details}";
#endif #endif

View File

@@ -33,4 +33,9 @@ public class ExceptionError<T> : Error where T : Exception
{ {
return $"{Message}\n{Exception}"; return $"{Message}\n{Exception}";
} }
public static implicit operator ExceptionError<T>(T exception)
{
return new ExceptionError<T>(exception);
}
} }

View File

@@ -46,13 +46,79 @@ public interface IMaybe<T, E> where E : Error
} }
} }
public readonly struct MaybeEx<T, Ex> : IMaybe<T, ExceptionError<Ex>> where Ex : Exception
{
[MemberNotNullWhen(true, nameof(Error))]
[MemberNotNullWhen(false, nameof(_value))]
public readonly bool HasError { get; }
public readonly bool HasValue => !HasError;
public readonly ExceptionError<Ex>? Error { get; init; }
public readonly T Value => HasError ? throw Error.GetException() : _value;
private readonly T? _value;
/// <summary>
/// Creates a new <see cref="Maybe{T, Ex}"/> with a default(null) value
/// </summary>
public MaybeEx()
{
_value = default;
}
/// <summary>
/// Creates a new <see cref="Maybe{T, Ex}"/> with a <paramref name="value"/>
/// </summary>
public MaybeEx(T value)
{
_value = value;
}
/// <summary>
/// Creates a new <see cref="Maybe{T, Ex}"/> with an exception error <paramref name="e"/>
/// </summary>
public MaybeEx(Ex e)
{
Error = e;
HasError = true;
_value = default;
}
public static implicit operator MaybeEx<T, Ex>(T value)
{
return new MaybeEx<T, Ex>(value);
}
public static implicit operator MaybeEx<T, Ex>(Ex e)
{
return new MaybeEx<T, Ex>(e);
}
public static implicit operator Maybe<T, ExceptionError<Ex>>(MaybeEx<T, Ex> value)
{
return value;
}
public static implicit operator Maybe<T>(MaybeEx<T, Ex> value)
{
return value;
}
public static implicit operator T(MaybeEx<T, Ex> value)
{
if (value.HasError)
throw value.Error.GetException();
return value.Value;
}
}
public readonly struct Maybe<T, E> : IMaybe<T, E> where E: Error public readonly struct Maybe<T, E> : IMaybe<T, E> where E: Error
{ {
[MemberNotNullWhen(true, nameof(Error))] [MemberNotNullWhen(true, nameof(Error))]
[MemberNotNullWhen(false, nameof(_value))]
public readonly bool HasError { get; } public readonly bool HasError { get; }
public readonly bool HasValue => !HasError; public readonly bool HasValue => !HasError;
public readonly E? Error { get; } public readonly E? Error { get; init; }
public readonly T Value => HasError ? throw Error.GetException() : _value!; public readonly T Value => HasError ? throw Error.GetException() : _value;
private readonly T? _value; private readonly T? _value;
@@ -82,6 +148,7 @@ public readonly struct Maybe<T, E> : IMaybe<T, E> where E: Error
_value = default; _value = default;
} }
public static implicit operator Maybe<T, E>(T value) public static implicit operator Maybe<T, E>(T value)
{ {
return new Maybe<T, E>(value); return new Maybe<T, E>(value);
@@ -92,6 +159,11 @@ public readonly struct Maybe<T, E> : IMaybe<T, E> where E: Error
return new Maybe<T, E>(e); return new Maybe<T, E>(e);
} }
public static implicit operator Maybe<T>(Maybe<T, E> value)
{
return value;
}
public static implicit operator T(Maybe<T, E> value) public static implicit operator T(Maybe<T, E> value)
{ {
if (value.HasError) if (value.HasError)
@@ -104,10 +176,11 @@ public readonly struct Maybe<T, E> : IMaybe<T, E> where E: Error
public readonly struct Maybe<T> : IMaybe<T, Error> public readonly struct Maybe<T> : IMaybe<T, Error>
{ {
[MemberNotNullWhen(true, nameof(Error))] [MemberNotNullWhen(true, nameof(Error))]
[MemberNotNullWhen(false, nameof(_value))]
public readonly bool HasError { get; } public readonly bool HasError { get; }
public readonly bool HasValue => !HasError; public readonly bool HasValue => !HasError;
public readonly Error? Error { get; } public readonly Error? Error { get; }
public readonly T Value => HasError ? throw Error.GetException() : _value!; public readonly T Value => HasError ? throw Error.GetException() : _value;
private readonly T? _value; private readonly T? _value;

View File

@@ -8,7 +8,7 @@
<PropertyGroup> <PropertyGroup>
<PackageId>MaybeError</PackageId> <PackageId>MaybeError</PackageId>
<Version>1.0.2</Version> <Version>1.2.0</Version>
<Authors>Amatsugu</Authors> <Authors>Amatsugu</Authors>
<PackageDescription>Errors as values</PackageDescription> <PackageDescription>Errors as values</PackageDescription>
<RepositoryUrl>https://github.com/Amatsugu/MaybeError</RepositoryUrl> <RepositoryUrl>https://github.com/Amatsugu/MaybeError</RepositoryUrl>

View File

@@ -37,6 +37,20 @@ public static class MaybeExtensions
return predicate(maybe.Value); return predicate(maybe.Value);
} }
public static ValueMaybe<T2, E> Map<T, T2, E>(this IValueMaybe<T, E> maybe, Func<T, T2> map) where T : struct where T2 : struct where E : Error
{
if (maybe.HasError)
return maybe.Error;
return map(maybe.Value);
}
public static ValueMaybe<T, E2> MapError<T, E, E2>(this IValueMaybe<T, E> maybe, Func<E, E2> map) where T : struct where E : Error where E2 : Error
{
if (!maybe.HasError)
return maybe.Value;
return map(maybe.Error);
}
public static T ValueOrDefault<T, E>(this IMaybe<T, E> maybe, T defaultValue) where E : Error public static T ValueOrDefault<T, E>(this IMaybe<T, E> maybe, T defaultValue) where E : Error
{ {
@@ -70,4 +84,19 @@ public static class MaybeExtensions
return defaultValue; return defaultValue;
return predicate(maybe.Value); return predicate(maybe.Value);
} }
public static Maybe<T2, E> Map<T, T2, E>(this IMaybe<T, E> maybe, Func<T, T2> map) where E : Error
{
if (maybe.HasError)
return maybe.Error;
return map(maybe.Value);
}
public static Maybe<T, E2> MapError<T, E, E2>(this IMaybe<T, E> maybe, Func<E, E2> map) where E : Error where E2 : Error
{
if (!maybe.HasError)
return maybe.Value;
return map(maybe.Error);
}
} }

View File

@@ -9,6 +9,7 @@ public interface IValueMaybe<T, E> where T : struct where E : Error
{ {
[MemberNotNullWhen(true, nameof(Error))] [MemberNotNullWhen(true, nameof(Error))]
bool HasError { get; } bool HasError { get; }
[MemberNotNullWhen(false, nameof(Error))]
bool HasValue => !HasError; bool HasValue => !HasError;
E? Error { get; } E? Error { get; }
T Value { get; } T Value { get; }
@@ -18,6 +19,7 @@ public readonly struct ValueMaybe<T, E> : IValueMaybe<T, E> where T : struct whe
{ {
[MemberNotNullWhen(true, nameof(Error))] [MemberNotNullWhen(true, nameof(Error))]
public readonly bool HasError { get; } public readonly bool HasError { get; }
[MemberNotNullWhen(false, nameof(Error))]
public readonly bool HasValue => !HasError; public readonly bool HasValue => !HasError;
public readonly E? Error { get; } public readonly E? Error { get; }
public readonly T Value => HasError ? throw Error.GetException() : _value; public readonly T Value => HasError ? throw Error.GetException() : _value;
@@ -61,6 +63,11 @@ public readonly struct ValueMaybe<T, E> : IValueMaybe<T, E> where T : struct whe
return new ValueMaybe<T, E>(e); return new ValueMaybe<T, E>(e);
} }
public static implicit operator ValueMaybe<T>(ValueMaybe<T, E> value)
{
return value;
}
public static implicit operator T(ValueMaybe<T, E> value) public static implicit operator T(ValueMaybe<T, E> value)
{ {
if (value.HasError) if (value.HasError)
@@ -138,3 +145,68 @@ public readonly struct ValueMaybe<T> : IValueMaybe<T, Error> where T : struct
return value.Value; return value.Value;
} }
} }
public readonly struct ValueMaybeEx<T, Ex> : IMaybe<T, ExceptionError<Ex>> where T: struct where Ex : Exception
{
[MemberNotNullWhen(true, nameof(Error))]
public readonly bool HasError { get; }
public readonly bool HasValue => !HasError;
public readonly ExceptionError<Ex>? Error { get; init; }
public readonly T Value => HasError ? throw Error.GetException() : _value!;
private readonly T _value;
/// <summary>
/// Creates a new <see cref="Maybe{T, Ex}"/> with a default(null) value
/// </summary>
public ValueMaybeEx()
{
_value = default;
}
/// <summary>
/// Creates a new <see cref="Maybe{T, Ex}"/> with a <paramref name="value"/>
/// </summary>
public ValueMaybeEx(T value)
{
_value = value;
}
/// <summary>
/// Creates a new <see cref="Maybe{T, Ex}"/> with an exception error <paramref name="e"/>
/// </summary>
public ValueMaybeEx(Ex e)
{
Error = e;
HasError = true;
_value = default;
}
public static implicit operator ValueMaybeEx<T, Ex>(T value)
{
return new ValueMaybeEx<T, Ex>(value);
}
public static implicit operator ValueMaybeEx<T, Ex>(Ex e)
{
return new ValueMaybeEx<T, Ex>(e);
}
public static implicit operator ValueMaybe<T, ExceptionError<Ex>>(ValueMaybeEx<T, Ex> value)
{
return value;
}
public static implicit operator ValueMaybe<T>(ValueMaybeEx<T, Ex> value)
{
return value;
}
public static implicit operator T(ValueMaybeEx<T, Ex> value)
{
if (value.HasError)
throw value.Error.GetException();
return value.Value;
}
}

View File

@@ -115,4 +115,40 @@ public class MaybeTests
value = new InvalidOperationException(); value = new InvalidOperationException();
Assert.That(value.AsOrDefault(v => v.Length, -4), Is.EqualTo(-4)); Assert.That(value.AsOrDefault(v => v.Length, -4), Is.EqualTo(-4));
} }
[Test]
public void MapTest()
{
Maybe<string> value = "Test";
var valueMapped = value.Map(v => $"A{v}");
Assert.That(valueMapped.HasValue, Is.True);
Assert.That(valueMapped.Value, Is.EqualTo("ATest"));
}
[Test]
public void MapTestError()
{
Maybe<string> value = new Error("Err");
var valueMapped = value.Map(v => $"A{v}");
Assert.That(valueMapped.HasError, Is.True);
}
[Test]
public void MapErrorTest()
{
Maybe<string> value = "Test";
var valueMapped = value.MapError(v => new Error($"A{v}"));
Assert.That(valueMapped.HasValue, Is.True);
Assert.That(valueMapped.Value, Is.EqualTo("Test"));
}
[Test]
public void MapErrorTestError()
{
Maybe<string> value = new Error("Err");
var valueMapped = value.MapError(v => new Error($"A{v.Message}"));
Assert.That(valueMapped.HasError, Is.True);
Assert.That(valueMapped.Error!.Message, Is.EqualTo("AErr"));
}
} }

View File

@@ -115,4 +115,40 @@ public class ValueMaybeTests
value = new InvalidOperationException(); value = new InvalidOperationException();
Assert.That(value.AsOrDefault(v => v.ToString(), "err"), Is.EqualTo("err")); Assert.That(value.AsOrDefault(v => v.ToString(), "err"), Is.EqualTo("err"));
} }
[Test]
public void MapTest()
{
ValueMaybe<int> value = 1;
var valueMapped = value.Map(v => v + 1);
Assert.That(valueMapped.HasValue, Is.True);
Assert.That(valueMapped.Value, Is.EqualTo(2));
}
[Test]
public void MapTestError()
{
ValueMaybe<int> value = new Error("Err");
var valueMapped = value.Map(v => v + 1);
Assert.That(valueMapped.HasError, Is.True);
}
[Test]
public void MapErrorTest()
{
Maybe<int> value = 1;
var valueMapped = value.MapError(v => new Error($"A{v}"));
Assert.That(valueMapped.HasValue, Is.True);
Assert.That(valueMapped.Value, Is.EqualTo(1));
}
[Test]
public void MapErrorTestError()
{
ValueMaybe<int> value = new Error("Err");
var valueMapped = value.MapError(v => new Error($"A{v.Message}"));
Assert.That(valueMapped.HasError, Is.True);
Assert.That(valueMapped.Error!.Message, Is.EqualTo("AErr"));
}
} }