using System; namespace FruityFoundation.Base.Structures; public readonly record struct OneOf { private readonly Maybe _itemOne; private readonly Maybe _itemTwo; public T1 ItemOne => _itemOne.OrThrow($"{nameof(ItemOne)} must have a value in order to be accessed"); public T2 ItemTwo => _itemTwo.OrThrow($"{nameof(ItemTwo)} must have a value in order to be accessed"); /// /// Exposes the underlying object as an object. /// This is useful for pattern matching from C#. /// public object? RawItem { get { if (_itemOne.HasValue) return _itemOne.Value; else if (_itemTwo.HasValue) return _itemTwo.Value; // This is specifically a null and not a Maybe so we can pattern match on it return null; } } public T Map(Func itemOneMap, Func itemTwoMap) { if (_itemOne.Try(out var itemOne)) return itemOneMap(itemOne); else if (_itemTwo.Try(out var itemTwo)) return itemTwoMap(itemTwo); throw new NotImplementedException("Map condition not handled"); } private OneOf(Maybe one, Maybe two) { _itemOne = one; _itemTwo = two; } public static implicit operator OneOf(T1 oneOf) => new(oneOf, default); public static implicit operator OneOf(T2 oneOf) => new(default, oneOf); } public class OneOf { private readonly Maybe _itemOne; private readonly Maybe _itemTwo; private readonly Maybe _itemThree; public T1 ItemOne => _itemOne.OrThrow($"{nameof(ItemOne)} must have a value in order to be accessed"); public T2 ItemTwo => _itemTwo.OrThrow($"{nameof(ItemTwo)} must have a value in order to be accessed"); public T3 ItemThree => _itemThree.OrThrow($"{nameof(ItemThree)} must have a value in order to be accessed"); /// /// Exposes the underlying object as an object. /// This is useful for pattern matching from C#. /// public object? RawItem { get { if (_itemOne.HasValue) return _itemOne.Value; else if (_itemTwo.HasValue) return _itemTwo.Value; else if (_itemThree.HasValue) return _itemThree.Value; // This is specifically a null and not a Maybe so we can pattern match on it return null; } } public T Map(Func itemOneMap, Func itemTwoMap, Func itemThreeMap) { if (_itemOne.Try(out var itemOne)) return itemOneMap(itemOne); else if (_itemTwo.Try(out var itemTwo)) return itemTwoMap(itemTwo); else if (_itemThree.Try(out var itemThree)) return itemThreeMap(itemThree); throw new NotImplementedException("Map condition not handled"); } public Type? ItemType { get { if (_itemOne.HasValue) return typeof(T1); else if (_itemTwo.HasValue) return typeof(T2); else if (_itemThree.HasValue) return typeof(T3); // This is specifically a null and not a Maybe so we can pattern match on it return null; } } private OneOf(Maybe one, Maybe two, Maybe three) { _itemOne = one; _itemTwo = two; _itemThree = three; } public static implicit operator OneOf(T1 oneOf) => new(oneOf, default, default); public static implicit operator OneOf(T2 oneOf) => new(default, oneOf, default); public static implicit operator OneOf(T3 oneOf) => new(default, default, oneOf); }