platform/packages/range_header/lib/src/range_header.dart

69 lines
2.2 KiB
Dart
Raw Normal View History

2021-05-01 02:48:36 +00:00
import 'dart:collection';
2021-05-01 03:39:09 +00:00
import 'exception.dart';
2021-05-01 02:48:36 +00:00
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,
2021-05-01 03:39:09 +00:00
{String? rangeUnit}) = _ConstantRangeHeader;
2021-05-01 02:48:36 +00:00
/// Eliminates any overlapping [items], sorts them, and folds them all into the most efficient representation possible.
static UnmodifiableListView<RangeHeaderItem> foldItems(
Iterable<RangeHeaderItem> items) {
2021-05-18 11:58:51 +00:00
var out = <RangeHeaderItem>{};
2021-05-01 02:48:36 +00:00
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);
}
2021-05-18 11:58:51 +00:00
return UnmodifiableListView(out.toList()..sort());
2021-05-01 02:48:36 +00:00
}
/// 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.
2021-05-01 03:39:09 +00:00
///
2021-05-01 02:48:36 +00:00
factory RangeHeader.parse(String text,
2021-05-18 11:58:51 +00:00
{Iterable<String>? allowedRangeUnits, bool fold = true}) {
2021-05-01 02:48:36 +00:00
var tokens = scan(text, allowedRangeUnits?.toList() ?? ['bytes']);
2021-05-18 11:58:51 +00:00
var parser = Parser(tokens);
2021-05-01 02:48:36 +00:00
var header = parser.parseRangeHeader();
2021-05-01 03:39:09 +00:00
if (header == null) {
throw InvalidRangeHeaderException('Header is null');
}
2021-05-01 02:48:36 +00:00
var items = foldItems(header.items);
return RangeHeaderImpl(header.rangeUnit, items);
}
/// Returns this header's range unit. Most commonly, this is `bytes`.
2021-05-01 03:39:09 +00:00
String? get rangeUnit;
2021-05-01 02:48:36 +00:00
}
class _ConstantRangeHeader implements RangeHeader {
final Iterable<RangeHeaderItem> items_;
2021-05-18 11:58:51 +00:00
@override
2021-05-01 03:39:09 +00:00
final String? rangeUnit;
2021-05-01 02:48:36 +00:00
2021-05-18 11:58:51 +00:00
const _ConstantRangeHeader(this.items_, {this.rangeUnit = 'bytes'});
2021-05-01 02:48:36 +00:00
@override
UnmodifiableListView<RangeHeaderItem> get items =>
2021-05-01 03:39:09 +00:00
UnmodifiableListView(items_);
2021-05-01 02:48:36 +00:00
}