///|
/// Pure response plan. The caller remains responsible for HTTP policy and I/O.
pub(all) enum ResponsePlan {
Ignore
Unsatisfiable(Int64)
Single(ConcreteRange)
Multiple(Array[ConcreteRange])
} derive(Eq, Debug)
///|
pub fn plan_byte_range_response(
request : RangeRequest,
representation_length : Int64,
method_is_get : Bool,
range_supported : Bool,
apply_ranges : Bool,
) -> Result[ResponsePlan, RangeError] {
if !method_is_get ||
!range_supported ||
!apply_ranges ||
!request.unit().is_bytes() {
return Ok(Ignore)
}
let resolved = match resolve_byte_ranges(request, representation_length) {
Ok(value) => value
Err(error) => return Err(error)
}
match resolved.ranges() {
[] => Ok(Unsatisfiable(representation_length))
[single] => Ok(Single(single))
many => Ok(Multiple(many))
}
}
///|
/// Plan a range response under an optional If-Range validator (RFC 9110 ยง13.1.5).
/// When a validator is present and does not match the current representation,
/// the Range field must be ignored and the caller serves the full
/// representation, so the plan is `Ignore` (status 200). A matching validator
/// falls through to the unconditional planner.
pub fn plan_conditional_range_response(
request : RangeRequest,
representation_length : Int64,
if_range : IfRangeValue?,
current_etag : String,
current_last_modified_unix : Int64,
method_is_get : Bool,
range_supported : Bool,
apply_ranges : Bool,
) -> Result[ResponsePlan, RangeError] {
match if_range {
Some(validator) =>
if !if_range_matches(validator, current_etag, current_last_modified_unix) {
return Ok(Ignore)
}
None => ()
}
plan_byte_range_response(
request, representation_length, method_is_get, range_supported, apply_ranges,
)
}
///|
/// Suggested status assuming the caller applies this plan.
pub fn ResponsePlan::recommended_status(self : ResponsePlan) -> Int {
match self {
Ignore => 200
Single(_) | Multiple(_) => 206
Unsatisfiable(_) => 416
}
}
///|
pub fn ResponsePlan::ranges(self : ResponsePlan) -> Array[ConcreteRange] {
match self {
Single(range) => [range]
Multiple(ranges) => ranges.copy()
_ => []
}
}