import 'dart:collection'; import 'exception.dart'; import 'parser.dart'; import 'range_header_item.dart'; import 'range_header_impl.dart'; /// Represents the contents of a parsed `Range` header. abstract class RangeHeader { /// Returns an immutable list of the ranges that were parsed. UnmodifiableListView get items; const factory RangeHeader(Iterable items, {String? rangeUnit}) = _ConstantRangeHeader; /// Eliminates any overlapping [items], sorts them, and folds them all into the most efficient representation possible. static UnmodifiableListView foldItems( Iterable items) { var out = {}; for (var item in items) { // Remove any overlapping items, consolidate them. while (out.any((x) => x.overlaps(item))) { var f = out.firstWhere((x) => x.overlaps(item)); out.remove(f); item = item.consolidate(f); } out.add(item); } return UnmodifiableListView(out.toList()..sort()); } /// Attempts to parse a [RangeHeader] from its [text] representation. /// /// You can optionally pass a custom list of [allowedRangeUnits]. /// The default is `['bytes']`. /// /// If [fold] is `true`, the items will be folded into the most compact /// possible representation. /// factory RangeHeader.parse(String text, {Iterable? allowedRangeUnits, bool fold = true}) { var tokens = scan(text, allowedRangeUnits?.toList() ?? ['bytes']); var parser = Parser(tokens); var header = parser.parseRangeHeader(); if (header == null) { throw InvalidRangeHeaderException('Header is null'); } var items = foldItems(header.items); return RangeHeaderImpl(header.rangeUnit, items); } /// Returns this header's range unit. Most commonly, this is `bytes`. String? get rangeUnit; } class _ConstantRangeHeader implements RangeHeader { final Iterable items_; @override final String? rangeUnit; const _ConstantRangeHeader(this.items_, {this.rangeUnit = 'bytes'}); @override UnmodifiableListView get items => UnmodifiableListView(items_); }