///|
/// Validate a collection POST and produce an immutable creation plan.
/// v0.1 deliberately creates metadata and length state only; bytes are sent by
/// PATCH so an adapter never needs to buffer two request semantics at once.
pub fn plan_creation(
request : TusRequest,
config : TusConfig,
) -> Result[CreationPlan, TusError] {
if request.http_method != Post {
return Err(invalid_method(request.http_method.name()))
}
if !config.allow_creation {
return Err(
tus_error(
UnsupportedExtension,
"TUS_CREATION_DISABLED",
"the Creation extension is disabled",
status=405,
),
)
}
match validate_request(request, config) {
Err(error) => return Err(error)
Ok(Resource(_)) => return Err(invalid_path(request.path))
Ok(Collection) => ()
}
if request.body.length() != 0 {
return Err(
tus_error(
UnsupportedExtension,
"TUS_CREATION_WITH_UPLOAD_UNSUPPORTED",
"v0.1 accepts upload bytes through PATCH only",
status=415,
actual=Some(request.body.length().to_string()),
),
)
}
match request.headers.singleton("content-length") {
Err(error) => return Err(error)
Ok(Some(value)) =>
match parse_non_negative_int("content-length", value) {
Err(error) => return Err(error)
Ok(length) =>
if length != 0 {
return Err(body_length_mismatch(length, 0))
}
}
Ok(None) => ()
}
let declared = match optional_upload_length(request.headers) {
Err(error) => return Err(error)
Ok(value) => value
}
let deferred = match optional_defer_length(request.headers) {
Err(error) => return Err(error)
Ok(value) => value
}
let length = match (declared, deferred) {
(Some(_), true) =>
return Err(
tus_error(
ConflictingUploadLength,
"TUS_LENGTH_HEADERS_CONFLICT",
"Upload-Length and Upload-Defer-Length cannot occur together",
header_name=Some("upload-length"),
),
)
(None, false) =>
return Err(missing_header("upload-length", MissingUploadLength))
(None, true) => {
if !config.allow_deferred_length {
return Err(
tus_error(
UnsupportedExtension,
"TUS_DEFER_LENGTH_DISABLED",
"Creation-Defer-Length is disabled",
status=400,
),
)
}
Deferred
}
(Some(value), false) => {
match validate_declared_size(value, config.limits) {
Err(error) => return Err(error)
Ok(_) => ()
}
Known(value)
}
}
let metadata = match request.headers.singleton("upload-metadata") {
Err(error) => return Err(error)
Ok(None) => []
Ok(Some(value)) =>
match parse_upload_metadata(value, config.limits) {
Err(error) => return Err(error)
Ok(entries) => entries
}
}
Ok({ length, metadata, })
}
///|
pub fn creation_trace(plan : CreationPlan) -> Array[TraceStep] {
let steps = [
trace(
"creation.headers.valid", "Creation headers are syntactically valid and mutually consistent",
"tus-1.0.0/creation",
),
]
match plan.length {
Known(total) =>
steps.push(
trace(
"creation.length.known",
"Upload-Length fixes the resource size at " + total.to_string(),
"tus-1.0.0/creation",
),
)
Deferred =>
steps.push(
trace(
"creation.length.deferred", "Upload-Defer-Length postpones the final size declaration",
"tus-1.0.0/creation-defer-length",
),
)
}
if plan.metadata.length() > 0 {
steps.push(
trace(
"creation.metadata.decoded",
plan.metadata.length().to_string() + " metadata entries were decoded",
"tus-1.0.0/creation",
),
)
}
steps
}