/****************************************************************************** * Copyright (C) Ultraleap, Inc. 2011-2021. * * * * Use subject to the terms of the Apache License 2.0 available at * * http://www.apache.org/licenses/LICENSE-2.0, or another agreement * * between Ultraleap and you, your company or other organization. * ******************************************************************************/ using System; namespace Leap.Unity { /// /// A data structure that represents either a value of type A or /// a value of type B. The value can never be both A and B. /// Neither A nor B can ever be null. /// public struct Either : IEquatable>, IComparable, IComparable> { /// /// Returns whether or not this Either contains the first value. /// public readonly bool isA; /// /// Returns whether or not this Either contains the second value. /// public bool isB { get { return !isA; } } private readonly A _a; private readonly B _b; /// /// Returns a Maybe that contains the value of A if it exists, /// or no value if it doesn't. /// public Maybe a { get { if (isA) { return Maybe.Some(_a); } else { return Maybe.None; } } } /// /// Returns a Maybe that contains the value of B if it exists, /// or no value if it doesn't. /// public Maybe b { get { if (isA) { return Maybe.None; } else { return Maybe.Some(_b); } } } /// /// Constructs an Either with a value of A. /// public Either(A a) { if (a == null) { throw new ArgumentNullException("Cannot initialize an Either with a null value."); } isA = true; _a = a; _b = default(B); } /// /// Constructs an Either with a value of B. /// public Either(B b) { if (b == null) { throw new ArgumentNullException("Cannot initialize an Either with a null value."); } isA = false; _b = b; _a = default(A); } /// /// Calls the first delegate with the value of A if it is present, /// else calls the second delegate with the value of B. /// public void Match(Action ifA, Action ifB) { if (isA) { if (ifA != null) ifA(_a); } else { if (ifB != null) ifB(_b); } } /// /// If this either contains the value of A, the out argument is filled with /// that value and this method returns true, else it returns false. /// public bool TryGetA(out A a) { a = _a; return isA; } /// /// If this either contains the value of B, the out argument is filled with /// that value and this method returns true, else it returns false. /// public bool TryGetB(out B b) { b = _b; return !isA; } public override int GetHashCode() { if (isA) { return _a.GetHashCode(); } else { return _b.GetHashCode(); } } public override bool Equals(object obj) { if (obj is Either) { return Equals((Either)obj); } else { return false; } } public bool Equals(Either other) { if (isA != other.isA) { return false; } else if (isA) { return _a.Equals(other._a); } else { return _b.Equals(other._b); } } public int CompareTo(object obj) { if (!(obj is Either)) { throw new ArgumentException(); } else { return CompareTo((Either)obj); } } public int CompareTo(Either other) { if (isA != other.isA) { return isA ? -1 : 1; } else if (isA) { IComparable ca = _a as IComparable; if (ca != null) { return ca.CompareTo(other._a); } else { IComparable c = _a as IComparable; if (c != null) { return c.CompareTo(other._b); } else { return 0; } } } else { IComparable cb = _b as IComparable; if (cb != null) { return cb.CompareTo(other._b); } else { IComparable c = _b as IComparable; if (c != null) { return c.CompareTo(other._b); } else { return 0; } } } } public static bool operator ==(Either either0, Either either1) { return either0.Equals(either1); } public static bool operator !=(Either either0, Either either1) { return !either0.Equals(either1); } public static bool operator >(Either either0, Either either1) { return either0.CompareTo(either1) > 0; } public static bool operator >=(Either either0, Either either1) { return either0.CompareTo(either1) >= 0; } public static bool operator <(Either either0, Either either1) { return either0.CompareTo(either1) < 0; } public static bool operator <=(Either either0, Either either1) { return either0.CompareTo(either1) <= 0; } public static implicit operator Either(A a) { return new Either(a); } public static implicit operator Either(B b) { return new Either(b); } } }