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