diff --git a/Agent/Phantom.Agent.Services/Instances/InstanceManagerActor.cs b/Agent/Phantom.Agent.Services/Instances/InstanceManagerActor.cs index 2f95e79..38942f7 100644 --- a/Agent/Phantom.Agent.Services/Instances/InstanceManagerActor.cs +++ b/Agent/Phantom.Agent.Services/Instances/InstanceManagerActor.cs @@ -12,7 +12,6 @@ using Phantom.Common.Data.Replies; using Phantom.Utils.Actor; using Phantom.Utils.IO; using Phantom.Utils.Logging; -using Phantom.Utils.Tasks; using Serilog; namespace Phantom.Agent.Services.Instances; @@ -140,8 +139,8 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand> } var ticket = instanceTicketManager.Reserve(instanceInfo.Configuration); - if (ticket is Result<InstanceTicketManager.Ticket, LaunchInstanceResult>.Fail fail) { - return InstanceActionResult.Concrete(fail.Error); + if (!ticket) { + return InstanceActionResult.Concrete(ticket.Error); } if (agentState.InstancesByGuid.TryGetValue(instanceGuid, out var instance)) { diff --git a/Agent/Phantom.Agent.Services/Instances/State/InstanceLaunchProcedure.cs b/Agent/Phantom.Agent.Services/Instances/State/InstanceLaunchProcedure.cs index 1308dfa..645c3bf 100644 --- a/Agent/Phantom.Agent.Services/Instances/State/InstanceLaunchProcedure.cs +++ b/Agent/Phantom.Agent.Services/Instances/State/InstanceLaunchProcedure.cs @@ -1,8 +1,8 @@ using Phantom.Agent.Minecraft.Instance; using Phantom.Agent.Minecraft.Launcher; using Phantom.Agent.Minecraft.Server; +using Phantom.Common.Data; using Phantom.Common.Data.Instance; -using Phantom.Utils.Tasks; namespace Phantom.Agent.Services.Instances.State; diff --git a/Common/Phantom.Common.Data/Result.cs b/Common/Phantom.Common.Data/Result.cs new file mode 100644 index 0000000..c448812 --- /dev/null +++ b/Common/Phantom.Common.Data/Result.cs @@ -0,0 +1,91 @@ +using System.Diagnostics.CodeAnalysis; +using MemoryPack; + +namespace Phantom.Common.Data; + +[MemoryPackable(GenerateType.VersionTolerant)] +public sealed partial class Result<TValue, TError> { + [MemoryPackOrder(0)] + [MemoryPackInclude] + private readonly bool hasValue; + + [MemoryPackOrder(1)] + [MemoryPackInclude] + private readonly TValue? value; + + [MemoryPackOrder(2)] + [MemoryPackInclude] + private readonly TError? error; + + [MemoryPackIgnore] + public TValue Value => hasValue ? value! : throw new InvalidOperationException("Attempted to get value from an error result."); + + [MemoryPackIgnore] + public TError Error => !hasValue ? error! : throw new InvalidOperationException("Attempted to get error from a success result."); + + private Result(bool hasValue, TValue? value, TError? error) { + this.hasValue = hasValue; + this.value = value; + this.error = error; + } + + public static implicit operator Result<TValue, TError>(TValue value) { + return new Result<TValue, TError>(hasValue: true, value, default); + } + + public static implicit operator Result<TValue, TError>(TError error) { + return new Result<TValue, TError>(hasValue: false, default, error); + } + + public static implicit operator bool(Result<TValue, TError> result) { + return result.hasValue; + } +} + +[MemoryPackable(GenerateType.VersionTolerant)] +public sealed partial class Result<TError> { + [MemoryPackOrder(0)] + [MemoryPackInclude] + private readonly bool hasValue; + + [MemoryPackOrder(1)] + [MemoryPackInclude] + private readonly TError? error; + + [MemoryPackIgnore] + public TError Error => !hasValue ? error! : throw new InvalidOperationException("Attempted to get error from a success result."); + + private Result(bool hasValue, TError? error) { + this.hasValue = hasValue; + this.error = error; + } + + public bool TryGetError([MaybeNullWhen(false)] out TError error) { + if (hasValue) { + error = default; + return false; + } + else { + error = this.error!; + return true; + } + } + + public static implicit operator Result<TError>([SuppressMessage("ReSharper", "UnusedParameter.Global")] Result.OkType _) { + return new Result<TError>(hasValue: true, default); + } + + public static implicit operator Result<TError>(TError error) { + return new Result<TError>(hasValue: false, error); + } + + public static implicit operator bool(Result<TError> result) { + return result.hasValue; + } +} + +public static class Result { + public static OkType Ok { get; } = new (); + + public readonly record struct OkType; +} diff --git a/Controller/Phantom.Controller.Database/Repositories/RoleRepository.cs b/Controller/Phantom.Controller.Database/Repositories/RoleRepository.cs index 735f781..f72cd45 100644 --- a/Controller/Phantom.Controller.Database/Repositories/RoleRepository.cs +++ b/Controller/Phantom.Controller.Database/Repositories/RoleRepository.cs @@ -1,9 +1,9 @@ using System.Collections.Immutable; using Microsoft.EntityFrameworkCore; +using Phantom.Common.Data; using Phantom.Common.Data.Web.Users; using Phantom.Controller.Database.Entities; using Phantom.Utils.Collections; -using Phantom.Utils.Tasks; namespace Phantom.Controller.Database.Repositories; diff --git a/Controller/Phantom.Controller.Database/Repositories/UserRepository.cs b/Controller/Phantom.Controller.Database/Repositories/UserRepository.cs index 597e99d..c7485df 100644 --- a/Controller/Phantom.Controller.Database/Repositories/UserRepository.cs +++ b/Controller/Phantom.Controller.Database/Repositories/UserRepository.cs @@ -1,12 +1,12 @@ using System.Collections.Immutable; using Microsoft.EntityFrameworkCore; +using Phantom.Common.Data; using Phantom.Common.Data.Web.Users; using Phantom.Common.Data.Web.Users.AddUserErrors; using Phantom.Common.Data.Web.Users.PasswordRequirementViolations; using Phantom.Common.Data.Web.Users.UsernameRequirementViolations; using Phantom.Controller.Database.Entities; using Phantom.Utils.Collections; -using Phantom.Utils.Tasks; namespace Phantom.Controller.Database.Repositories; diff --git a/Controller/Phantom.Controller.Services/Users/UserManager.cs b/Controller/Phantom.Controller.Services/Users/UserManager.cs index 2b6899d..571c533 100644 --- a/Controller/Phantom.Controller.Services/Users/UserManager.cs +++ b/Controller/Phantom.Controller.Services/Users/UserManager.cs @@ -54,9 +54,8 @@ sealed class UserManager { } } else { - var result = userRepository.SetUserPassword(user, password); - if (!result) { - return new Common.Data.Web.Users.CreateOrUpdateAdministratorUserResults.UpdatingFailed(result.Error); + if (userRepository.SetUserPassword(user, password).TryGetError(out var error)) { + return new Common.Data.Web.Users.CreateOrUpdateAdministratorUserResults.UpdatingFailed(error); } auditLogWriter.AdministratorUserModified(user); diff --git a/Utils/Phantom.Utils/Tasks/Result.cs b/Utils/Phantom.Utils/Tasks/Result.cs deleted file mode 100644 index 4345dd8..0000000 --- a/Utils/Phantom.Utils/Tasks/Result.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Phantom.Utils.Tasks; - -public abstract record Result<TValue, TError> { - private Result() {} - - public abstract TValue Value { get; init; } - public abstract TError Error { get; init; } - - public static implicit operator Result<TValue, TError>(TValue value) { - return new Ok(value); - } - - public static implicit operator Result<TValue, TError>(TError error) { - return new Fail(error); - } - - public static implicit operator bool(Result<TValue, TError> result) { - return result is Ok; - } - - public sealed record Ok(TValue Value) : Result<TValue, TError> { - public override TError Error { - get => throw new InvalidOperationException("Attempted to get error from Ok result."); - init {} - } - } - - public sealed record Fail(TError Error) : Result<TValue, TError> { - public override TValue Value { - get => throw new InvalidOperationException("Attempted to get value from Fail result."); - init {} - } - } -} - -public abstract record Result<TError> { - private Result() {} - - public abstract TError Error { get; init; } - - public static implicit operator Result<TError>(TError error) { - return new Fail(error); - } - - public static implicit operator Result<TError>([SuppressMessage("ReSharper", "UnusedParameter.Global")] Result.OkType _) { - return new Ok(); - } - - public static implicit operator bool(Result<TError> result) { - return result is Ok; - } - - public sealed record Ok : Result<TError> { - public override TError Error { - get => throw new InvalidOperationException("Attempted to get error from Ok result."); - init {} - } - } - - public sealed record Fail(TError Error) : Result<TError>; -} - -public static class Result { - public static OkType Ok { get; } = new (); - - public readonly record struct OkType; -} diff --git a/Web/Phantom.Web/Pages/Setup.razor b/Web/Phantom.Web/Pages/Setup.razor index 62a52ed..8f20555 100644 --- a/Web/Phantom.Web/Pages/Setup.razor +++ b/Web/Phantom.Web/Pages/Setup.razor @@ -1,14 +1,14 @@ @page "/setup" -@using Phantom.Utils.Tasks +@using Phantom.Common.Data +@using Phantom.Common.Data.Web.Users +@using Phantom.Common.Data.Web.Users.CreateOrUpdateAdministratorUserResults +@using Phantom.Common.Messages.Web.ToController +@using Phantom.Utils.Cryptography @using Phantom.Web.Services @using Phantom.Web.Services.Authentication @using Phantom.Web.Services.Rpc @using System.ComponentModel.DataAnnotations -@using Phantom.Utils.Cryptography @using System.Security.Cryptography -@using Phantom.Common.Messages.Web.ToController -@using Phantom.Common.Data.Web.Users -@using Phantom.Common.Data.Web.Users.CreateOrUpdateAdministratorUserResults @attribute [AllowAnonymous] @inject ApplicationProperties ApplicationProperties @inject UserLoginManager LoginManager @@ -65,8 +65,9 @@ return; } - if (await CreateOrUpdateAdministrator() is Result<string>.Fail fail) { - form.SubmitModel.StopSubmitting(fail.Error); + var createOrUpdateAdministratorResult = await CreateOrUpdateAdministrator(); + if (createOrUpdateAdministratorResult.TryGetError(out var error)) { + form.SubmitModel.StopSubmitting(error); return; }