///|
/// A response body range, inclusive at both ends.
pub(all) struct ByteRange {
start : Int64
end : Int64
} derive(Eq, Debug)
///|
/// Result of evaluating an HTTP Range request.
pub(all) enum RangeResult {
Satisfiable(ByteRange)
NotSatisfiable
NotApplicable
} derive(Eq, Debug)
///|
/// Parse an HTTP Range header string according to RFC 7233 and ecstatic compatibility.
pub fn parse_range_spec(header_val : String, total : Int64) -> RangeResult {
let trimmed = header_val.trim()
let raw = if trimmed.has_prefix("bytes=") {
trimmed[6:].trim()
} else {
trimmed
}
// If unit is another unit (e.g. items=...), ignore Range (RFC 7233 ยง3.1)
if find_char_view(raw, '=') is Some(_) {
return NotApplicable
}
if total <= 0L {
return NotSatisfiable
}
let dash_idx = match find_char_view(raw, '-') {
Some(pos) => pos
None => return NotApplicable
}
let part_start = raw[:dash_idx].trim()
let part_end = raw[dash_idx + 1:].trim()
// Suffix range: "-len"
if part_start.length() == 0 {
guard parse_i64_strict(part_end) is Some(suffix_len) else {
return NotSatisfiable
}
if suffix_len <= 0L {
return NotSatisfiable
}
let start = if suffix_len >= total { 0L } else { total - suffix_len }
let end = total - 1L
return Satisfiable({ start, end, })
}
// Prefix range: "start-" or Full range: "start-end"
guard parse_i64_strict(part_start) is Some(start) else {
return NotSatisfiable
}
if start < 0L || start >= total {
return NotSatisfiable
}
if part_end.length() == 0 {
// "start-" -> start to EOF
return Satisfiable({ start, end: total - 1L, })
}
guard parse_i64_strict(part_end) is Some(raw_end) else {
return NotSatisfiable
}
if raw_end < start {
// Inverted range e.g. "333-222" (C004.05)
return NotSatisfiable
}
// Clamp end to EOF e.g. "3-500" (C004.02)
let end = if raw_end >= total { total - 1L } else { raw_end }
Satisfiable({ start, end, })
}
///|
/// Strict parsing of a non-negative Int64 from StringView.
fn parse_i64_strict(s : StringView) -> Int64? {
if s.length() == 0 {
return None
}
let mut n = 0L
for c in s {
let cp = c.to_int()
if cp < '0'.to_int() || cp > '9'.to_int() {
return None
}
n = n * 10L + (cp - '0'.to_int()).to_int64()
}
Some(n)
}
///|
/// Format Content-Range header for a successful 206 response.
/// e.g. "bytes 3-5/11"
pub fn format_content_range(
start : Int64,
end : Int64,
total : Int64,
) -> String {
"bytes \{start}-\{end}/\{total}"
}
///|
/// Format Content-Range header for an unsatisfiable 416 response.
/// e.g. "bytes */11" (per C004.03)
pub fn format_content_range_unsatisfiable(total : Int64) -> String {
"bytes */\{total}"
}
///|
/// Legacy compatibility helper matching original core.parse_range.
pub fn parse_range(value : String, size : Int64) -> ByteRange? {
match parse_range_spec(value, size) {
Satisfiable(r) => Some(r)
_ => None
}
}
///|
pub extend ByteRange with Eq::{not_equal, equal}
///|
pub extend ByteRange with Debug::{to_repr}
///|
pub extend RangeResult with Eq::{not_equal, equal}
///|
pub extend RangeResult with Debug::{to_repr}