/// A utility library for handling flexible data structures. /// /// This library provides the `ListOr` class, which normalizes single values or /// iterables into a unified `List` interface. It is particularly useful for /// parsing JSON or dynamic data where a field might interchangeably be provided /// as a single element or a collection of elements. library; import 'dart:collection'; /// A list implementation that normalizes a single value or an iterable into a /// [List]. /// /// This class mixes in [ListMixin] to provide a complete list API backed by an /// internal list. It is particularly useful when parsing flexible data /// structures (such as JSON) where a field might be provided as either a single /// element or a collection of elements. class ListOr extends Object with ListMixin { /// The underlying list storing the elements. final List _items; /// Creates a [ListOr] instance from an arbitrary [value]. /// /// The factory normalizes the [value] based on its type: /// * If [value] is an [Iterable] (e.g., `List` or `Set`), it creates a new /// modifiable list and casts the elements to [T]. /// * If [value] is exactly of type [T] (including `null` if [T] is nullable), /// it wraps the value in a single-element list. /// /// Throws an [ArgumentError] if [value] is `null` but the generic type [T] is /// non-nullable. /// Throws an [ArgumentError] if the [value] is neither of type [T] nor an /// [Iterable] compatible with [T]. factory ListOr(Object? value) { if (value is Iterable) { return ListOr._(List.from(value)); } if (value is T) { return ListOr._([value]); } if (value == null) { throw ArgumentError( 'The type $T does not accept null values. ' 'Pass a value of type $T or use ListOr<$T?> instead.', ); } throw ArgumentError( 'Value must be of type $T or Iterable containing $T. Got ${value.runtimeType}', ); } /// Internal constructor to initialize the backing list. ListOr._(this._items); /// Returns the number of elements in the list. @override int get length => _items.length; /// Updates the length of the list. @override set length(int newLength) { _items.length = newLength; } /// Returns the element at the given [index]. @override T operator [](int index) => _items[index]; /// Sets the element at the given [index] to [value]. @override void operator []=(int index, T value) { _items[index] = value; } /// Adds [element] to the end of this list. @override void add(T element) => _items.add(element); /// Appends all objects of [iterable] to the end of this list. @override void addAll(Iterable iterable) => _items.addAll(iterable); /// Inserts [element] at position [index] in this list. @override void insert(int index, T element) => _items.insert(index, element); /// Removes the object at position [index] from this list. /// /// Returns the removed element. @override T removeAt(int index) => _items.removeAt(index); /// Removes the first occurrence of [element] from this list. /// /// Returns `true` if [element] was in the list, `false` otherwise. @override bool remove(Object? element) => _items.remove(element); /// Removes all objects from this list; the length of the list becomes zero. @override void clear() => _items.clear(); /// Returns a string representation of this object. /// /// If the list contains exactly one element, this returns the string /// representation of that single element. Otherwise, it returns the standard /// bracketed list representation (e.g., `[a, b]` or `[]`). @override String toString() { if (_items.length == 1) { return _items.firstOrNull.toString(); } return _items.toString(); } }