//
// 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);
}
}