// Copyright (c) Facebook, Inc. and its affiliates // SPDX-License-Identifier: MIT OR Apache-2.0 using System; using System.Collections.Generic; namespace Serde { public readonly struct Option : IEquatable> where T : IEquatable { public static Option None => default; public static Option Some(T value) { if (value == null) throw new ArgumentNullException(nameof(value)); return new Option(value); } readonly bool isSome; readonly T value; Option(T val) { isSome = val != null; value = val; } public bool IsSome(out T value) { value = this.value; return isSome; } public override bool Equals(object obj) => obj is Option option && Equals(option); public bool Equals(Option other) { if (isSome != other.isSome) return false; if (!isSome) return true; return value.Equals(other.value); } public override int GetHashCode() { var hashCode = -934799437; hashCode = hashCode * -1521134295 + isSome.GetHashCode(); hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(value); return hashCode; } public static bool operator ==(Option left, Option right) => Equals(left, right); public static bool operator !=(Option left, Option right) => !Equals(left, right); } public static class OptionExtensions { public static U Match(this Option option, Func onIsSome, Func onIsNone) where T : IEquatable where U : IEquatable => option.IsSome(out var value) ? onIsSome(value) : onIsNone(); public static Option Bind(this Option option, Func> binder) where T : IEquatable where U : IEquatable => option.Match(onIsSome: binder, onIsNone: () => Option.None); public static Option Map(this Option option, Func mapper) where T : IEquatable where U : IEquatable => option.Bind(value => Option.Some(mapper(value))); public static Option Filter(this Option option, Predicate predicate) where T : IEquatable => option.Bind(value => predicate(value) ? option : Option.None); public static T DefaultValue(this Option option, T defaultValue) where T : IEquatable => option.Match(onIsSome: value => value, onIsNone: () => defaultValue); } }