///|
/// Per-part metadata for a caller-produced multipart/byteranges body.
pub struct MultipartPartPlan {
range : ConcreteRange
content_range : ContentRangeValue
content_length : Int64
} derive(Eq, Debug)
///|
pub struct MultipartPlan {
boundary : String
content_type : String
parts : Array[MultipartPartPlan]
} derive(Eq, Debug)
///|
pub fn plan_multipart_ranges(
ranges : Array[ConcreteRange],
representation_length : Int64,
boundary : String,
) -> Result[MultipartPlan, RangeError] {
if boundary.length() == 0 || boundary.length() > 70 {
return Err(
range_error(
Plan,
InvalidBoundary,
0,
"multipart boundary must contain 1 to 70 characters",
),
)
}
for c in boundary {
if c.to_int() <= 0x20 || c.to_int() >= 0x7F || c == '"' {
return Err(
range_error(
Plan,
InvalidBoundary,
0,
"multipart boundary must be visible ASCII without quotes",
),
)
}
}
if representation_length < 0L {
return Err(
range_error(
Plan,
InvalidRepresentationLength,
0,
"representation length cannot be negative",
),
)
}
let parts : Array[MultipartPartPlan] = []
for range in ranges {
if range.first() < 0L ||
range.last() < range.first() ||
range.last() >= representation_length {
return Err(
range_error(
Plan,
InvalidRange,
0,
"multipart range is outside the representation",
),
)
}
parts.push({
range,
content_range: Satisfied(
Bytes,
range.first(),
range.last(),
Some(representation_length),
),
content_length: range.length(),
})
}
Ok({ boundary, content_type: multipart_content_type(boundary), parts })
}
///|
pub fn multipart_content_type(boundary : String) -> String {
"multipart/byteranges; boundary=\{boundary}"
}
///|
pub fn MultipartPartPlan::range(self : MultipartPartPlan) -> ConcreteRange {
self.range
}
///|
pub fn MultipartPartPlan::content_range(
self : MultipartPartPlan,
) -> ContentRangeValue {
self.content_range
}
///|
pub fn MultipartPartPlan::content_length(self : MultipartPartPlan) -> Int64 {
self.content_length
}
///|
pub fn MultipartPlan::boundary(self : MultipartPlan) -> String {
self.boundary
}
///|
pub fn MultipartPlan::content_type(self : MultipartPlan) -> String {
self.content_type
}
///|
pub fn MultipartPlan::parts(self : MultipartPlan) -> Array[MultipartPartPlan] {
self.parts.copy()
}