// // Optional.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics.CodeAnalysis; using NosSmooth.LocalBinding.Errors; using Remora.Results; namespace NosSmooth.LocalBinding; /// /// An optional, used mainly for hooks and binding modules, /// to make it possible to check whether a module is loaded /// in runtime. /// /// The type of underlying value. public class Optional where T : notnull { /// /// An empty optional (no value present). /// public static readonly Optional Empty = new(); /// /// Initializes a new instance of the class. /// /// The underlying value of the optional. Not present when null. public Optional(T? value = default) { Value = value; } /// /// Gets whether the value is present. /// [MemberNotNullWhen(true, "Value")] public bool IsPresent => Value is not null; /// /// Gets the underlying value. /// public T? Value { get; } /// /// Tries to get the underlying value, if it's present. /// If it's not present, value won't be set. /// /// The underlying value. /// Whether the value is present. public bool TryGet([NotNullWhen(true)] out T? value) { value = Value; return IsPresent; } /// /// Tries to execute an action on the value, if it exists. /// /// /// Does nothing, if the value does not exist. /// /// The action to execute. /// Whether the value is present. public bool TryDo(Action action) { if (IsPresent) { action(Value); } return IsPresent; } /// /// Gets something from the underlying value, /// exposing it as optional that is empty, /// in case this optional is empty as well. /// /// The function to obtain something from the value with. /// The return type. /// An optional, present if this optional's value is present. public Optional Map(Func get) where TU : notnull { if (IsPresent) { return get(Value); } return Optional.Empty; } /// /// Gets something from the underlying value like . /// /// /// Returns in case this value is not present /// instead of returning an optional. /// /// The function to obtain something from the value with. /// The return type. /// A result, successful in case this optional's value is present. public Result MapResult(Func get) where TU : notnull { if (IsPresent) { return OptionalUtilities.TryGet(() => get(Value)); } return new OptionalNotPresentError(nameof(T)); } /// /// Does something on the underlying value like , but returns a result /// in case the value is not present. /// /// The function to execute on the value. /// The return type. /// A result, successful in case this optional's value is present. public Result MapResult(Action get) { if (IsPresent) { return OptionalUtilities.Try(() => get(Value)); } return new OptionalNotPresentError(nameof(T)); } /// /// Gets something from the underlying value like . /// /// /// Returns in case this value is not present /// instead of returning an optional. /// /// The get function returns a result that will be returned if this optional is present. /// /// The function to obtain something from the value with. /// The return type. /// A result from the function, in case this optional is not present. public Result MapResult(Func> get) where TU : notnull { if (IsPresent) { return get(Value); } return new OptionalNotPresentError(nameof(T)); } /// /// Does something on the underlying value like , but returns a result /// in case the value is not present. /// /// /// Returns in case this value is not present /// instead of returning an optional. /// /// The get function returns a result that will be returned if this optional is present. /// /// The function to obtain something from the value with. /// The return type. /// A result from the function, in case this optional is not present. public Result MapResult(Func get) { if (IsPresent) { return get(Value); } return new OptionalNotPresentError(nameof(T)); } /// /// Forcefully gets the underlying value. /// If it's not present, will be thrown. /// /// /// Try to use other methods that return results where possible as they are easier to handle. /// /// The underlying value. /// Thrown in case the value is not present. public T Get() { if (!IsPresent) { throw new InvalidOperationException ( $"Could not get {nameof(T)}. Did you forget to call initialization or was there an error?" ); } return Value; } /// /// Cast a value to optional. /// /// The value to cast. /// The created optional. public static implicit operator Optional(T val) { return new Optional(val); } }