using System.Collections; using System.Collections.Immutable; namespace Just.Core.Collections; /// /// Represents an immutable, ordered sequence of items with value‑equality semantics. /// /// The type of elements in the sequence. /// /// /// This class is a thin wrapper around that implements /// , , and value‑based equality. /// All modifications return new instances, leaving the /// original unchanged. /// /// /// Subclasses may override to ensure mutation methods return /// the correct derived type. /// /// public class ImmutableSequence : IEnumerable, IReadOnlyList, IEquatable> { private static readonly Func CompareItem = EqualityComparer.Default.Equals; private readonly ImmutableList _values; /// /// Initializes a new empty instance of the class. /// public ImmutableSequence() => _values = []; /// /// Initializes a new instance of the class that /// wraps the specified . /// /// The immutable list to wrap. public ImmutableSequence(ImmutableList values) => _values = values; /// /// Initializes a new instance of the class with /// the elements from the provided enumerable sequence. /// /// The items to include in the sequence. public ImmutableSequence(IEnumerable values) => _values = [..values]; /// /// Initializes a new instance of the class with /// the elements from the provided read‑only span. /// /// The items to include in the sequence. public ImmutableSequence(ReadOnlySpan values) : this(ImmutableList.Create(values)) { } /// /// Gets a value indicating whether the sequence contains any elements. /// public bool IsEmpty => _values.IsEmpty; /// /// Gets the number of elements in the sequence. /// public int Count => _values.Count; /// /// Gets the element at the specified zero‑based index. /// /// The zero‑based index of the element to get. /// The element at the specified index. public T this[int index] => _values[index]; /// /// Gets the element at the specified position from the start or end of the sequence. /// /// An value (e.g., ^1 for the last element). /// The element at the specified position. public T this[Index index] => _values[index]; /// /// Gets a new containing the elements in the specified range. /// /// The range of elements to include. /// A new sequence representing the slice. public ImmutableSequence this[Range range] { get { var (offset, count) = range.GetOffsetAndLength(_values.Count); return ConstructNew(_values.GetRange(offset, count)); } } /// /// Creates a new from the provided immutable list. /// Subclasses can override this to return instances of a more specific type. /// /// The immutable list that will become the internal storage. /// A new sequence containing the given items. protected virtual ImmutableSequence ConstructNew(ImmutableList values) => [..values]; /// /// Returns a new sequence with the specified value appended to the end. /// /// The value to add. /// A new sequence containing the original items followed by . public ImmutableSequence Add(T value) => ConstructNew(_values.Add(value)); /// /// Returns a new sequence with the specified value inserted at the beginning. /// /// The value to add. /// A new sequence that starts with and then contains the original items. public ImmutableSequence AddFront(T value) => ConstructNew(_values.Insert(0, value)); /// /// Returns an enumerator that iterates through the sequence. /// /// A value type enumerator. public ImmutableList.Enumerator GetEnumerator() => _values.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_values).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_values).GetEnumerator(); public override string ToString() => string.Join(Environment.NewLine, _values); /// /// Determines whether this sequence is equal to another . /// Equality is based on the number of elements and the element‑wise equality comparison /// using the default equality comparer for . /// /// The sequence to compare with this instance. Can be null. /// /// true if the sequences have the same length and all elements are equal; /// false otherwise. /// public virtual bool Equals([NotNullWhen(true)] ImmutableSequence? other) { if (other is null) { return false; } if (ReferenceEquals(this, other)) { return true; } if (_values.Count != other._values.Count) { return false; } for (int i = 0; i < _values.Count; i++) { if (!CompareItem(_values[i], other._values[i])) { return false; } } return true; } /// /// Determines whether the specified object is equal to the current sequence. /// /// The object to compare with the current sequence. /// true if is an and equals this instance; otherwise false. public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as ImmutableSequence); /// /// Serves as a hash function for the sequence. /// /// A hash code that incorporates all elements in order. public override int GetHashCode() { HashCode hash = new(); foreach (var value in _values) { hash.Add(value); } return hash.ToHashCode(); } /// /// Determines whether two sequences are equal. /// /// The first sequence to compare. /// The second sequence to compare. /// true if both sequences are null or they are considered equal; otherwise false. public static bool operator ==(ImmutableSequence? left, ImmutableSequence? right) => left is null ? right is null : left.Equals(right); /// /// Determines whether two sequences are not equal. /// /// The first sequence to compare. /// The second sequence to compare. /// true if the sequences are not equal; otherwise false. public static bool operator !=(ImmutableSequence? left, ImmutableSequence? right) => !(left == right); }