Files
list_or/lib/list_or.dart
T
hans 2229c638f9
CI / format (pull_request) Successful in 38s
CI / analyze (pull_request) Successful in 18s
CI / test (pull_request) Successful in 42s
CI / pana (pull_request) Failing after 1m40s
CI / version-and-changelog (pull_request) Failing after 20s
Add issue templates, contribution guidelines, and code of conduct; update CI/CD workflows and project metadata
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
2026-05-13 13:58:28 +02:00

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