Add exact resumable object source streaming

This commit is contained in:
ms2k 2026-07-17 05:57:27 +09:00
commit 0ad79c597f

View file

@ -0,0 +1,83 @@
use remora_stream::{StreamError, StreamSide};
use remora_target::TargetError;
#[derive(Clone, Copy, Debug, Eq, thiserror::Error, PartialEq)]
pub enum ObjectSourceError {
#[error("object source request is invalid")]
InvalidRequest,
#[error("object source access is denied")]
SourceAuthorization,
#[error("destination access is denied")]
DestinationAuthorization,
#[error("object source was not found")]
SourceNotFound,
#[error("object source changed")]
SourceChanged,
#[error("destination resume state changed")]
DestinationChanged,
#[error("object source is temporarily unavailable")]
TransientSource,
#[error("destination is temporarily unavailable")]
TransientDestination,
#[error("object source adapter failed")]
Internal,
}
impl ObjectSourceError {
#[must_use]
pub fn into_stream_error(self) -> StreamError {
match self {
Self::SourceAuthorization => StreamError::Authorization {
side: StreamSide::Source,
},
Self::DestinationAuthorization => StreamError::Authorization {
side: StreamSide::Target,
},
Self::SourceNotFound => StreamError::SourceNotFound,
Self::SourceChanged => StreamError::SourceChanged,
Self::TransientSource => StreamError::transient_source("object source unavailable"),
Self::TransientDestination => StreamError::transient_target("destination unavailable"),
Self::DestinationChanged => {
StreamError::internal("destination resume proof is no longer reusable")
}
Self::InvalidRequest | Self::Internal => {
StreamError::internal("object source contract violation")
}
}
}
}
pub(crate) fn map_open_error(error: opendal::Error) -> ObjectSourceError {
use opendal::ErrorKind;
match error.kind() {
ErrorKind::NotFound => ObjectSourceError::SourceNotFound,
ErrorKind::PermissionDenied => ObjectSourceError::SourceAuthorization,
ErrorKind::ConditionNotMatch | ErrorKind::RangeNotSatisfied => {
ObjectSourceError::SourceChanged
}
ErrorKind::RateLimited => ObjectSourceError::TransientSource,
ErrorKind::Unsupported | ErrorKind::ConfigInvalid => ObjectSourceError::InvalidRequest,
_ => ObjectSourceError::TransientSource,
}
}
pub(crate) fn map_resume_error(error: opendal::Error) -> ObjectSourceError {
match map_open_error(error) {
ObjectSourceError::SourceNotFound => ObjectSourceError::SourceChanged,
other => other,
}
}
pub(crate) fn map_destination_error(error: TargetError) -> ObjectSourceError {
match error {
TargetError::Authorization => ObjectSourceError::DestinationAuthorization,
TargetError::NotFound | TargetError::AlreadyExists | TargetError::Integrity(_) => {
ObjectSourceError::DestinationChanged
}
TargetError::Capacity | TargetError::Transient(_) => {
ObjectSourceError::TransientDestination
}
TargetError::Validation(_) | TargetError::Internal(_) => ObjectSourceError::Internal,
}
}