platform/common/range_header/lib/src/range_header.dart

69 lines
2.2 KiB
Dart
Raw Normal View History

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<RangeHeaderItem> get items;
const factory RangeHeader(Iterable<RangeHeaderItem> items,
{String? rangeUnit}) = _ConstantRangeHeader;
/// Eliminates any overlapping [items], sorts them, and folds them all into the most efficient representation possible.
static UnmodifiableListView<RangeHeaderItem> foldItems(
Iterable<RangeHeaderItem> items) {
var out = <RangeHeaderItem>{};
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<String>? 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<RangeHeaderItem> items_;
@override
final String? rangeUnit;
const _ConstantRangeHeader(this.items_, {this.rangeUnit = 'bytes'});
@override
UnmodifiableListView<RangeHeaderItem> get items =>
UnmodifiableListView(items_);
}