using YKanBan.Search; namespace YKanBan.Tests.Search; /// /// Structural comparer for search AST nodes, used by assertions only. The AST /// records compare by reference for their Terms collections by design (see the /// YYC MARK note in SearchNodes.cs), so tests provide equality explicitly. /// public sealed class SearchNodeComparer : IEqualityComparer { /// /// Gets the shared comparer instance. /// public static SearchNodeComparer Instance { get; } = new(); /// /// Determines structural equality of two nodes. /// /// The first node. /// The second node. /// when the nodes are structurally equal. public bool Equals(SearchNode? x, SearchNode? y) { if (ReferenceEquals(x, y)) { return true; } if (x is null || y is null || x.GetType() != y.GetType()) { return false; } return (x, y) switch { (SearchNode.Or a, SearchNode.Or b) => SequencesEqual(a.Terms, b.Terms), (SearchNode.And a, SearchNode.And b) => SequencesEqual(a.Terms, b.Terms), (SearchNode.FreeText a, SearchNode.FreeText b) => a.Text == b.Text, (SearchNode.FieldText a, SearchNode.FieldText b) => a.Field == b.Field && a.Text == b.Text, (SearchNode.TagName a, SearchNode.TagName b) => a.Name == b.Name, (SearchNode.ColumnTitle a, SearchNode.ColumnTitle b) => a.Title == b.Title, (SearchNode.CardId a, SearchNode.CardId b) => a.Id == b.Id, _ => false, }; } /// /// Not supported: the comparer exists for assertions, not hashing. /// /// The node. /// Never returns. public int GetHashCode(SearchNode obj) => throw new NotSupportedException("Structural comparison for assertions only; hashing is not supported."); /// /// Order-sensitive element-wise comparison. /// /// The first sequence. /// The second sequence. /// when both sequences match element-wise. private static bool SequencesEqual(IReadOnlyList x, IReadOnlyList y) { if (x.Count != y.Count) { return false; } return x.Zip(y).All(pair => Instance.Equals(pair.First, pair.Second)); } }