119 lines
3.8 KiB
Dart
119 lines
3.8 KiB
Dart
/// 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<T>].
|
|
///
|
|
/// 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<T> extends Object with ListMixin<T> {
|
|
/// The underlying list storing the elements.
|
|
final List<T> _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<T>.from(value));
|
|
}
|
|
|
|
if (value is T) {
|
|
return ListOr._(<T>[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<T> 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.first.toString();
|
|
}
|
|
return _items.toString();
|
|
}
|
|
}
|