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));
}
}