use super::persistent_pagination;
use super::tree_mutation::TreeFormat;
use super::{CanonicalDecodeError, DecodeLimits, LogicalName, TreeEntry};
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 DirectoryPage {
pub entries: Vec<TreeEntry>,
pub has_more: bool,
pub next_residency: Option<ResidencyHint>,
pub work: WorkCounters,
}
pub fn list_tree_entries<S: crate::ImmediateObjectStore>(
store: &S,
root: ObjectId,
after: Option<&LogicalName>,
maximum_entries: u32,
limits: DecodeLimits,
budget: WorkBudget,
) -> Result<DirectoryPage, DirectoryReadFailure> {
persistent_pagination::paginate::<S, TreeFormat>(
store,
root,
after,
maximum_entries,
limits,
budget,
)
.map(to_page)
.map_err(map_failure)
}
pub async fn list_tree_entries_async<S: AsyncObjectStore>(
store: &S,
root: ObjectId,
after: Option<&LogicalName>,
maximum_entries: u32,
limits: DecodeLimits,
budget: WorkBudget,
cancellation: &CancellationToken,
) -> Result<DirectoryPage, DirectoryReadFailure> {
persistent_pagination::paginate_async::<S, TreeFormat>(
store,
root,
after,
maximum_entries,
limits,
budget,
cancellation,
)
.await
.map(to_page)
.map_err(map_failure)
}
pub(crate) async fn list_tree_entries_at_or_after_async<S: AsyncObjectStore>(
store: &S,
root: ObjectId,
at: &LogicalName,
maximum_entries: u32,
limits: DecodeLimits,
budget: WorkBudget,
cancellation: &CancellationToken,
) -> Result<DirectoryPage, DirectoryReadFailure> {
persistent_pagination::paginate_async_at_or_after::<S, TreeFormat>(
store,
root,
at,
maximum_entries,
limits,
budget,
cancellation,
)
.await
.map(to_page)
.map_err(map_failure)
}
fn to_page(receipt: persistent_pagination::Receipt<TreeEntry>) -> DirectoryPage {
DirectoryPage {
entries: receipt.values,
has_more: receipt.has_more,
next_residency: receipt.next_request.map(|request| ResidencyHint {
request,
reason: ResidencyReason::DirectorySuccessor,
}),
work: receipt.work,
}
}
pub type DirectoryReadFailure = OperationFailure<DirectoryReadError>;
fn map_failure(failure: persistent_pagination::Failure) -> DirectoryReadFailure {
OperationFailure::new(map_error(failure.error), *failure.work)
}
fn map_error(error: persistent_pagination::Error) -> DirectoryReadError {
match error {
persistent_pagination::Error::Cancelled => DirectoryReadError::Cancelled,
persistent_pagination::Error::ZeroLimit => DirectoryReadError::ZeroLimit,
persistent_pagination::Error::LimitOverflow => DirectoryReadError::LimitOverflow,
persistent_pagination::Error::WrongRootKind => DirectoryReadError::WrongRootKind,
persistent_pagination::Error::InvalidLimits => DirectoryReadError::InvalidLimits,
persistent_pagination::Error::HeightExceeded => DirectoryReadError::HeightExceeded,
persistent_pagination::Error::CycleOrAlias => DirectoryReadError::Cycle,
persistent_pagination::Error::ChildBoundsMismatch => {
DirectoryReadError::ChildBoundsMismatch
}
persistent_pagination::Error::TraversalState => DirectoryReadError::TraversalState,
persistent_pagination::Error::AllocationFailed => DirectoryReadError::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 DirectoryReadError {
#[error("directory listing was cancelled")]
Cancelled,
#[error("directory listing transition state is invalid")]
TraversalState,
#[error("directory page limit must be non-zero")]
ZeroLimit,
#[error("directory page limit cannot be represented")]
LimitOverflow,
#[error("directory root is not a tree page")]
WrongRootKind,
#[error("directory listing limits are invalid")]
InvalidLimits,
#[error("directory tree exceeds its admitted height")]
HeightExceeded,
#[error("directory tree contains a cycle or alias")]
Cycle,
#[error("directory child bounds do not match its page")]
ChildBoundsMismatch,
#[error("directory 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/list.rs"]
mod tests;