#nullable enable using System; using System.Collections; using System.Collections.Generic; using Dapper; using Newtonsoft.Json; namespace PluralKit.Core { [JsonConverter(typeof(PartialConverter))] public struct Partial: IEnumerable { public bool IsPresent { get; } public T Value { get; } private Partial(bool isPresent, T value) { IsPresent = isPresent; Value = value; } public static Partial Null() => new Partial(true, default!); public static Partial Present(T obj) => new Partial(true, obj); public static Partial Absent = new Partial(false, default!); public IEnumerable ToArray() => IsPresent ? new[] {Value} : new T[0]; public IEnumerator GetEnumerator() => ToArray().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ToArray().GetEnumerator(); public static implicit operator Partial(T val) => Present(val); } public class PartialConverter: JsonConverter { public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { var innerType = objectType.GenericTypeArguments[0]; var innerValue = serializer.Deserialize(reader, innerType); return typeof(Partial<>) .MakeGenericType(innerType) .GetMethod(nameof(Partial.Present))! .Invoke(null, new[] {innerValue}); } public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) => throw new NotImplementedException(); public override bool CanConvert(Type objectType) => true; public override bool CanRead => true; public override bool CanWrite => false; } public static class PartialExt { public static bool TryGet(this Partial pt, out T value) { value = pt.IsPresent ? pt.Value : default!; return pt.IsPresent; } public static T Or(this Partial pt, T fallback) => pt.IsPresent ? pt.Value : fallback; public static T Or(this Partial pt, Func fallback) => pt.IsPresent ? pt.Value : fallback.Invoke(); public static Partial Map(this Partial pt, Func fn) => pt.IsPresent ? Partial.Present(fn.Invoke(pt.Value)) : Partial.Absent; public static void Apply(this Partial pt, DynamicParameters bag, QueryBuilder qb, string fieldName) { if (!pt.IsPresent) return; bag.Add(fieldName, pt.Value); qb.Variable(fieldName, $"@{fieldName}"); } } }