using Microsoft.AspNetCore.WebUtilities; using System.ComponentModel; using System.Text.Json.Serialization; using System.Text.Json; namespace Nuuru.Server.Utilities { [TypeConverter(typeof(ShortGuidTypeConverter))] [JsonConverter(typeof(ShortGuidJsonConverter))] public readonly struct ShortGuid { private readonly Guid? _value; public ShortGuid(Guid? value) => _value = value; public static implicit operator Guid?(ShortGuid shortGuid) => shortGuid._value; public static implicit operator ShortGuid(Guid? guid) => new(guid); public static ShortGuid? Parse(string? input) { if (string.IsNullOrEmpty(input)) return null; try { return new ShortGuid(new Guid(WebEncoders.Base64UrlDecode(input))); } catch { return null; } } public override string? ToString() { if (!_value.HasValue) return null; Span bytes = stackalloc byte[16]; _value.Value.TryWriteBytes(bytes); return WebEncoders.Base64UrlEncode(bytes); } private sealed class ShortGuidTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => sourceType == typeof(string); public override object? ConvertFrom(ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) => value is string str ? Parse(str) : null; public override bool CanConvertTo(ITypeDescriptorContext? context, Type destinationType) => destinationType == typeof(string); public override object? ConvertTo(ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, Type destinationType) => value is ShortGuid shortGuid ? shortGuid.ToString() : null; } private sealed class ShortGuidJsonConverter : JsonConverter { public override ShortGuid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var str = reader.GetString(); if (str == null) return new ShortGuid(null); var parsed = Parse(str); return parsed ?? new ShortGuid(null); } public override void Write(Utf8JsonWriter writer, ShortGuid value, JsonSerializerOptions options) { var str = value.ToString(); if (str == null) writer.WriteNullValue(); else writer.WriteStringValue(str); } } } }