using System.Text.Json.Serialization; namespace FinalFactory.Rendezvous.Contracts; [JsonConverter(typeof(SessionListingIdJsonConverter))] public readonly struct SessionListingId : IEquatable { public SessionListingId(Guid value) => Value = GuidIdentifier.RequireNonEmpty(value, nameof(value)); public Guid Value { get; } public static bool TryParse(string? value, out SessionListingId id) => GuidIdentifier.TryParse(value, static guid => new SessionListingId(guid), out id); public bool Equals(SessionListingId other) => Value.Equals(other.Value); public override bool Equals(object? obj) => obj is SessionListingId other && Equals(other); public override int GetHashCode() => Value.GetHashCode(); public override string ToString() => Value.ToString("D"); public static bool operator ==(SessionListingId left, SessionListingId right) => left.Equals(right); public static bool operator !=(SessionListingId left, SessionListingId right) => !left.Equals(right); } [JsonConverter(typeof(LeaseIdJsonConverter))] public readonly struct LeaseId : IEquatable { public LeaseId(Guid value) => Value = GuidIdentifier.RequireNonEmpty(value, nameof(value)); public Guid Value { get; } public static bool TryParse(string? value, out LeaseId id) => GuidIdentifier.TryParse(value, static guid => new LeaseId(guid), out id); public bool Equals(LeaseId other) => Value.Equals(other.Value); public override bool Equals(object? obj) => obj is LeaseId other && Equals(other); public override int GetHashCode() => Value.GetHashCode(); public override string ToString() => Value.ToString("D"); public static bool operator ==(LeaseId left, LeaseId right) => left.Equals(right); public static bool operator !=(LeaseId left, LeaseId right) => !left.Equals(right); } [JsonConverter(typeof(JoinAttemptIdJsonConverter))] public readonly struct JoinAttemptId : IEquatable { public JoinAttemptId(Guid value) => Value = GuidIdentifier.RequireNonEmpty(value, nameof(value)); public Guid Value { get; } public static bool TryParse(string? value, out JoinAttemptId id) => GuidIdentifier.TryParse(value, static guid => new JoinAttemptId(guid), out id); public bool Equals(JoinAttemptId other) => Value.Equals(other.Value); public override bool Equals(object? obj) => obj is JoinAttemptId other && Equals(other); public override int GetHashCode() => Value.GetHashCode(); public override string ToString() => Value.ToString("D"); public static bool operator ==(JoinAttemptId left, JoinAttemptId right) => left.Equals(right); public static bool operator !=(JoinAttemptId left, JoinAttemptId right) => !left.Equals(right); } [JsonConverter(typeof(MediationHandleJsonConverter))] public readonly struct MediationHandle : IEquatable { public MediationHandle(Guid value) => Value = GuidIdentifier.RequireNonEmpty(value, nameof(value)); public Guid Value { get; } public static bool TryParse(string? value, out MediationHandle id) => GuidIdentifier.TryParse(value, static guid => new MediationHandle(guid), out id); public bool Equals(MediationHandle other) => Value.Equals(other.Value); public override bool Equals(object? obj) => obj is MediationHandle other && Equals(other); public override int GetHashCode() => Value.GetHashCode(); public override string ToString() => Value.ToString("D"); public static bool operator ==(MediationHandle left, MediationHandle right) => left.Equals(right); public static bool operator !=(MediationHandle left, MediationHandle right) => !left.Equals(right); } internal static class GuidIdentifier { public static Guid RequireNonEmpty(Guid value, string parameterName) => value != Guid.Empty ? value : throw new ArgumentException("Opaque identifiers cannot be empty.", parameterName); public static bool TryParse( string? value, Func factory, out TIdentifier identifier) { if (Guid.TryParseExact(value, "D", out Guid guid) && guid != Guid.Empty) { identifier = factory(guid); return true; } identifier = default!; return false; } } internal abstract class GuidIdentifierJsonConverter : StringIdentifierJsonConverter { protected sealed override string Format(TIdentifier value) => value?.ToString() ?? string.Empty; } internal sealed class SessionListingIdJsonConverter : GuidIdentifierJsonConverter { protected override SessionListingId Parse(string value) => new(Guid.ParseExact(value, "D")); } internal sealed class LeaseIdJsonConverter : GuidIdentifierJsonConverter { protected override LeaseId Parse(string value) => new(Guid.ParseExact(value, "D")); } internal sealed class JoinAttemptIdJsonConverter : GuidIdentifierJsonConverter { protected override JoinAttemptId Parse(string value) => new(Guid.ParseExact(value, "D")); } internal sealed class MediationHandleJsonConverter : GuidIdentifierJsonConverter { protected override MediationHandle Parse(string value) => new(Guid.ParseExact(value, "D")); }