use super::attribute_mutation::AttributeFormat;
use super::persistent_pagination;
use super::{AttributeEntry, AttributeName, CanonicalDecodeError, DecodeLimits};
use crate::async_storage::AsyncObjectStore;
use crate::cancellation::CancellationToken;
use crate::performance::{OperationFailure, WorkBudget, WorkCounters, WorkError};
use crate::speculation::{ResidencyHint, ResidencyReason};
use crate::storage::{ObjectId, ObjectStoreError};
use thiserror::Error;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AttributeListing {
pub entries: Vec<AttributeEntry>,
pub has_more: bool,
pub next_residency: Option<ResidencyHint>,
pub work: WorkCounters,
}
pub fn list_attributes<S: crate::ImmediateObjectStore>(
store: &S,
root: ObjectId,
after: Option<&AttributeName>,
maximum_entries: u32,
limits: DecodeLimits,
budget: WorkBudget,
) -> Result<AttributeListing, AttributeListFailure> {
persistent_pagination::paginate::<S, AttributeFormat>(
store,
root,
after,
maximum_entries,
limits,
budget,
)
.map(to_listing)
.map_err(map_failure)
}
pub async fn list_attributes_async<S: AsyncObjectStore>(
store: &S,
root: ObjectId,
after: Option<&AttributeName>,
maximum_entries: u32,
limits: DecodeLimits,
budget: WorkBudget,
cancellation: &CancellationToken,
) -> Result<AttributeListing, AttributeListFailure> {
persistent_pagination::paginate_async::<S, AttributeFormat>(
store,
root,
after,
maximum_entries,
limits,
budget,
cancellation,
)
.await
.map(to_listing)
.map_err(map_failure)
}
fn to_listing(receipt: persistent_pagination::Receipt<AttributeEntry>) -> AttributeListing {
AttributeListing {
entries: receipt.values,
has_more: receipt.has_more,
next_residency: receipt.next_request.map(|request| ResidencyHint {
request,
reason: ResidencyReason::MetadataSuccessor,
}),
work: receipt.work,
}
}
pub type AttributeListFailure = OperationFailure<AttributeListError>;
fn map_failure(failure: persistent_pagination::Failure) -> AttributeListFailure {
OperationFailure::new(map_error(failure.error), *failure.work)
}
fn map_error(error: persistent_pagination::Error) -> AttributeListError {
match error {
persistent_pagination::Error::Cancelled => AttributeListError::Cancelled,
persistent_pagination::Error::ZeroLimit => AttributeListError::ZeroLimit,
persistent_pagination::Error::LimitOverflow => AttributeListError::LimitOverflow,
persistent_pagination::Error::WrongRootKind => AttributeListError::WrongRootKind,
persistent_pagination::Error::InvalidLimits => AttributeListError::InvalidLimits,
persistent_pagination::Error::HeightExceeded => AttributeListError::HeightExceeded,
persistent_pagination::Error::CycleOrAlias => AttributeListError::CycleOrAlias,
persistent_pagination::Error::ChildBoundsMismatch => {
AttributeListError::ChildBoundsMismatch
}
persistent_pagination::Error::TraversalState => AttributeListError::TraversalState,
persistent_pagination::Error::AllocationFailed => AttributeListError::AllocationFailed,
persistent_pagination::Error::Storage(error) => error.into(),
persistent_pagination::Error::Decode(error) => error.into(),
persistent_pagination::Error::Work(error) => error.into(),
}
}
#[derive(Debug, Error)]
pub enum AttributeListError {
#[error("attribute listing was cancelled")]
Cancelled,
#[error("attribute page limit must be non-zero")]
ZeroLimit,
#[error("attribute page limit cannot be represented")]
LimitOverflow,
#[error("attribute root is not an attribute page")]
WrongRootKind,
#[error("attribute listing limits are invalid")]
InvalidLimits,
#[error("attribute tree exceeds its admitted height")]
HeightExceeded,
#[error("attribute tree contains a cycle or alias")]
CycleOrAlias,
#[error("attribute child bounds do not match its page")]
ChildBoundsMismatch,
#[error("attribute listing transition state is invalid")]
TraversalState,
#[error("attribute listing allocation failed")]
AllocationFailed,
#[error(transparent)]
Storage(#[from] ObjectStoreError),
#[error(transparent)]
Decode(#[from] CanonicalDecodeError),
#[error(transparent)]
Work(#[from] WorkError),
}
#[cfg(all(test, feature = "memory"))]
#[path = "tests/attribute_list.rs"]
mod tests;