use super::ServerState;
use bcode_session_models::MAX_SESSION_ARTIFACT_RANGE_BYTES;
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum ReadArtifactError {
#[error("artifact range length must be between 1 and {MAX_SESSION_ARTIFACT_RANGE_BYTES} bytes")]
InvalidLength,
#[error("artifact was not found")]
NotFound,
#[error("artifact is unavailable")]
Unavailable,
#[error("artifact read failed")]
Failed,
}
impl ReadArtifactError {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::InvalidLength => "invalid_artifact_range_length",
Self::NotFound => "artifact_not_found",
Self::Unavailable => "artifact_unavailable",
Self::Failed => "artifact_read_failed",
}
}
#[must_use]
pub fn message(self) -> String {
self.to_string()
}
}
pub async fn read_range(
state: &ServerState,
session_id: bcode_session_models::SessionId,
artifact_id: &str,
reference_key: &str,
offset: u64,
length: u32,
) -> Result<bcode_session_models::SessionArtifactRange, ReadArtifactError> {
if length == 0 || length > super::MAX_ARTIFACT_RANGE_BYTES {
return Err(ReadArtifactError::InvalidLength);
}
super::read_session_artifact_range(
state,
session_id,
artifact_id,
reference_key,
offset,
length,
)
.await
.map_err(|error| classify_internal_error(&error))
}
#[must_use]
pub fn classify_internal_error(error: &str) -> ReadArtifactError {
if error.contains("was not found in the finalized projection") {
ReadArtifactError::NotFound
} else if error.contains("artifact reference has no storage URI")
|| error.contains("artifact reference is unavailable")
|| error.contains("artifact reference is incomplete")
|| error.contains("artifact file is unavailable")
|| error.contains("No such file or directory")
{
ReadArtifactError::Unavailable
} else {
ReadArtifactError::Failed
}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn invalid_lengths_fail_before_session_lookup() {
let state = crate::tests::test_server_state(bcode_session::SessionManager::default());
for length in [0, super::super::MAX_ARTIFACT_RANGE_BYTES + 1, u32::MAX] {
let error = super::read_range(
&state,
bcode_session_models::SessionId::new(),
"missing-artifact",
"missing-reference",
0,
length,
)
.await
.expect_err("invalid length");
assert_eq!(error, super::ReadArtifactError::InvalidLength);
assert_eq!(error.code(), "invalid_artifact_range_length");
assert_eq!(error.to_string(), error.message());
}
drop(state);
}
}