///|
/// Content-Range value for satisfied or unsatisfied responses.
pub(all) enum ContentRangeValue {
Satisfied(RangeUnit, Int64, Int64, Int64?)
Unsatisfied(RangeUnit, Int64)
OtherContentRange(String, String)
} derive(Eq, Debug)
///|
pub fn parse_content_range(
input : String,
) -> Result[ContentRangeValue, RangeError] {
parse_content_range_with_limits(input, Limits::default())
}
///|
pub fn parse_content_range_with_limits(
input : String,
limits : Limits,
) -> Result[ContentRangeValue, RangeError] {
if utf8_length(input) > limits.max_input_bytes() {
return Err(
range_error(
Limit,
LimitExceeded,
0,
"Content-Range exceeds max_input_bytes",
),
)
}
let trimmed = trim_ows(input)
if trimmed.length() == 0 {
return Err(
range_error(ContentRange, EmptyInput, 0, "Content-Range is empty"),
)
}
let space = find_ows(trimmed)
if space < 0 {
return Err(
range_error(
ContentRange,
InvalidRange,
trimmed.length(),
"Content-Range has no range value",
),
)
}
let unit_text = trimmed[:space].to_owned()
for c in unit_text {
if !is_tchar(c) {
return Err(
range_error(ContentRange, InvalidUnit, 0, "invalid Content-Range unit"),
)
}
}
let response = trim_ows(trimmed[space + 1:].to_owned())
if !ascii_equal(unit_text, "bytes") {
return Ok(OtherContentRange(unit_text, response))
}
let slash = find_char(response, '/')
if slash < 0 || find_char_from(response, '/', slash + 1) >= 0 {
return Err(
range_error(
ContentRange,
InvalidRange,
space + 1,
"Content-Range requires one '/'",
),
)
}
let range_part = trim_ows(response[:slash].to_owned())
let length_part = trim_ows(response[slash + 1:].to_owned())
if range_part == "*" {
if length_part == "*" {
return Err(
range_error(
ContentRange,
InvalidCompleteLength,
space + slash + 2,
"unsatisfied Content-Range requires known length",
),
)
}
return match
parse_decimal_int64_at(length_part, space + slash + 2, limits) {
Ok(length) => Ok(Unsatisfied(Bytes, length))
Err(error) => Err(error)
}
}
let dash = find_char(range_part, '-')
if dash < 0 {
return Err(
range_error(
ContentRange,
InvalidRange,
space + 1,
"satisfied Content-Range has no '-'",
),
)
}
let first = match
parse_decimal_int64_at(range_part[:dash].to_owned(), space + 1, limits) {
Ok(v) => v
Err(e) => return Err(e)
}
let last = match
parse_decimal_int64_at(
range_part[dash + 1:].to_owned(),
space + dash + 2,
limits,
) {
Ok(v) => v
Err(e) => return Err(e)
}
if last < first {
return Err(
range_error(
ContentRange,
LastBeforeFirst,
space + dash + 2,
"last position is before first",
),
)
}
let complete : Int64? = if length_part == "*" {
None
} else {
match parse_decimal_int64_at(length_part, space + slash + 2, limits) {
Ok(v) => Some(v)
Err(e) => return Err(e)
}
}
match complete {
Some(length) if length <= last =>
return Err(
range_error(
ContentRange,
InvalidCompleteLength,
space + slash + 2,
"complete length must be greater than last position",
),
)
_ => ()
}
Ok(Satisfied(Bytes, first, last, complete))
}
///|
pub fn serialize_content_range(value : ContentRangeValue) -> String {
match value {
Satisfied(unit, first, last, complete) => {
let length = match complete {
Some(v) => v.to_string()
None => "*"
}
"\{unit.name()} \{first}-\{last}/\{length}"
}
Unsatisfied(unit, complete) => "\{unit.name()} */\{complete}"
OtherContentRange(unit, response) => "\{unit} \{response}"
}
}
///|
pub fn unsatisfied_content_range(
length : Int64,
) -> Result[ContentRangeValue, RangeError] {
if length < 0L {
Err(
range_error(
ContentRange,
InvalidCompleteLength,
0,
"complete length cannot be negative",
),
)
} else {
Ok(Unsatisfied(Bytes, length))
}
}
///|
fn find_ows(input : String) -> Int {
for i = 0; i < input.length(); i = i + 1 {
let c = input[i].to_int().unsafe_to_char()
if is_ows(c) {
return i
}
}
-1
}