// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.ComponentModel; using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using EpicGames.Core; namespace EpicGames.Horde.Commits { /// /// Identifier for a commit from an arbitary version control system. /// [JsonSchemaString] [JsonConverter(typeof(CommitIdJsonConverter))] [TypeConverter(typeof(CommitIdTypeConverter))] public class CommitId : IEquatable { /// /// Commit id with an empty name /// public static CommitId Empty { get; } = new CommitId(String.Empty); /// /// Name of this commit. Compared as a case-insensitive string. /// [JsonPropertyOrder(0)] public string Name { get; } /// /// Constructor /// public CommitId(string name) => Name = name; /// /// Creates a commit it from a Perforce changelist number. Temporary helper method for migration purposes. /// public static CommitId FromPerforceChange(int change) => new CommitId($"{change}"); /// /// Creates a commit it from a Perforce changelist number. Temporary helper method for migration purposes. /// public static CommitId? FromPerforceChange(int? change) => (change == null) ? null : FromPerforceChange(change.Value); /// /// Gets the Perforce changelist number. Temporary helper method for migration purposes. /// public int GetPerforceChange() => Int32.Parse(Name); /// /// Gets the Perforce changelist number. Temporary helper method for migration purposes. /// public int GetPerforceChangeOrMinusOne() => TryGetPerforceChange() ?? -1; /// /// Gets the Perforce changelist number. Temporary helper method for migration purposes. /// public int? TryGetPerforceChange() => Int32.TryParse(Name, out int value) ? value : null; /// public override bool Equals(object? obj) => obj is CommitId other && Equals(other); /// public bool Equals(CommitId? other) => Name.Equals(other?.Name, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => Name.GetHashCode(StringComparison.OrdinalIgnoreCase); /// public override string ToString() => Name; /// /// Test two commits for equality /// public static bool operator ==(CommitId? a, CommitId? b) => a?.Equals(b) ?? (b is null); /// /// Test two commits for inequality /// public static bool operator !=(CommitId? a, CommitId? b) => !(a == b); } class CommitIdJsonConverter : JsonConverter { /// public override CommitId? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.TokenType switch { JsonTokenType.Number => CommitId.FromPerforceChange(reader.GetInt32()), JsonTokenType.String => new CommitId(reader.GetString()!), _ => throw new JsonException("Invalid commit id") }; /// public override void Write(Utf8JsonWriter writer, CommitId value, JsonSerializerOptions options) => writer.WriteStringValue(value.Name); } class CommitIdTypeConverter : TypeConverter { /// public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => sourceType == typeof(string); /// public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) => new CommitId((string)value); /// public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(string); /// public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) => value?.ToString(); } /// /// Variant of including a value that allows it to be used for ordering. /// public class CommitIdWithOrder : CommitId, IEquatable, IComparable { /// /// Commit id with an empty name /// public static new CommitIdWithOrder Empty { get; } = new CommitIdWithOrder(String.Empty, 0); /// /// Value used for ordering commits. /// [JsonPropertyOrder(1)] public int Order { get; } /// /// Constructor /// public CommitIdWithOrder(string name, int order) : base(name) { Order = order; } /// /// Creates a commit it from a Perforce changelist number. Temporary helper method for migration purposes. /// public static new CommitIdWithOrder FromPerforceChange(int change) => new CommitIdWithOrder($"{change}", change); /// /// Creates a commit it from a Perforce changelist number. Temporary helper method for migration purposes. /// public static new CommitIdWithOrder? FromPerforceChange(int? change) => (change == null) ? null : CommitIdWithOrder.FromPerforceChange(change.Value); /// public override bool Equals(object? obj) => Equals(obj as CommitIdWithOrder); /// public bool Equals(CommitIdWithOrder? other) => other is not null && Order == other.Order; /// public override int GetHashCode() => base.GetHashCode(); /// public int CompareTo(CommitIdWithOrder? other) { if (other is null) { return -1; } else { return Order.CompareTo(other.Order); } } #pragma warning disable CS1591 public static bool operator ==(CommitIdWithOrder? a, CommitIdWithOrder? b) => a?.Equals(b) ?? b is null; public static bool operator !=(CommitIdWithOrder? a, CommitIdWithOrder? b) => !(a == b); public static bool operator <(CommitIdWithOrder a, CommitIdWithOrder b) => a.CompareTo(b) < 0; public static bool operator <=(CommitIdWithOrder a, CommitIdWithOrder b) => a.CompareTo(b) <= 0; public static bool operator >(CommitIdWithOrder a, CommitIdWithOrder b) => a.CompareTo(b) > 0; public static bool operator >=(CommitIdWithOrder a, CommitIdWithOrder b) => a.CompareTo(b) >= 0; #pragma warning restore CS1591 } }