1use super::persistent_pagination;
4use super::tree_mutation::TreeFormat;
5use super::{CanonicalDecodeError, DecodeLimits, LogicalName, TreeEntry};
6use crate::async_storage::AsyncObjectStore;
7use crate::cancellation::CancellationToken;
8use crate::performance::{OperationFailure, WorkBudget, WorkCounters, WorkError};
9use crate::speculation::{ResidencyHint, ResidencyReason};
10use crate::storage::{ObjectId, ObjectStoreError};
11use thiserror::Error;
12
13#[derive(Clone, Debug, Eq, PartialEq)]
15pub struct DirectoryPage {
16 pub entries: Vec<TreeEntry>,
18 pub has_more: bool,
20 pub next_residency: Option<ResidencyHint>,
23 pub work: WorkCounters,
25}
26
27pub fn list_tree_entries<S: crate::ImmediateObjectStore>(
37 store: &S,
38 root: ObjectId,
39 after: Option<&LogicalName>,
40 maximum_entries: u32,
41 limits: DecodeLimits,
42 budget: WorkBudget,
43) -> Result<DirectoryPage, DirectoryReadFailure> {
44 persistent_pagination::paginate::<S, TreeFormat>(
45 store,
46 root,
47 after,
48 maximum_entries,
49 limits,
50 budget,
51 )
52 .map(to_page)
53 .map_err(map_failure)
54}
55
56pub async fn list_tree_entries_async<S: AsyncObjectStore>(
63 store: &S,
64 root: ObjectId,
65 after: Option<&LogicalName>,
66 maximum_entries: u32,
67 limits: DecodeLimits,
68 budget: WorkBudget,
69 cancellation: &CancellationToken,
70) -> Result<DirectoryPage, DirectoryReadFailure> {
71 persistent_pagination::paginate_async::<S, TreeFormat>(
72 store,
73 root,
74 after,
75 maximum_entries,
76 limits,
77 budget,
78 cancellation,
79 )
80 .await
81 .map(to_page)
82 .map_err(map_failure)
83}
84
85pub(crate) async fn list_tree_entries_at_or_after_async<S: AsyncObjectStore>(
86 store: &S,
87 root: ObjectId,
88 at: &LogicalName,
89 maximum_entries: u32,
90 limits: DecodeLimits,
91 budget: WorkBudget,
92 cancellation: &CancellationToken,
93) -> Result<DirectoryPage, DirectoryReadFailure> {
94 persistent_pagination::paginate_async_at_or_after::<S, TreeFormat>(
95 store,
96 root,
97 at,
98 maximum_entries,
99 limits,
100 budget,
101 cancellation,
102 )
103 .await
104 .map(to_page)
105 .map_err(map_failure)
106}
107
108fn to_page(receipt: persistent_pagination::Receipt<TreeEntry>) -> DirectoryPage {
109 DirectoryPage {
110 entries: receipt.values,
111 has_more: receipt.has_more,
112 next_residency: receipt.next_request.map(|request| ResidencyHint {
113 request,
114 reason: ResidencyReason::DirectorySuccessor,
115 }),
116 work: receipt.work,
117 }
118}
119
120pub type DirectoryReadFailure = OperationFailure<DirectoryReadError>;
122
123fn map_failure(failure: persistent_pagination::Failure) -> DirectoryReadFailure {
124 OperationFailure::new(map_error(failure.error), *failure.work)
125}
126
127fn map_error(error: persistent_pagination::Error) -> DirectoryReadError {
128 match error {
129 persistent_pagination::Error::Cancelled => DirectoryReadError::Cancelled,
130 persistent_pagination::Error::ZeroLimit => DirectoryReadError::ZeroLimit,
131 persistent_pagination::Error::LimitOverflow => DirectoryReadError::LimitOverflow,
132 persistent_pagination::Error::WrongRootKind => DirectoryReadError::WrongRootKind,
133 persistent_pagination::Error::InvalidLimits => DirectoryReadError::InvalidLimits,
134 persistent_pagination::Error::HeightExceeded => DirectoryReadError::HeightExceeded,
135 persistent_pagination::Error::CycleOrAlias => DirectoryReadError::Cycle,
136 persistent_pagination::Error::ChildBoundsMismatch => {
137 DirectoryReadError::ChildBoundsMismatch
138 }
139 persistent_pagination::Error::TraversalState => DirectoryReadError::TraversalState,
140 persistent_pagination::Error::AllocationFailed => DirectoryReadError::AllocationFailed,
141 persistent_pagination::Error::Storage(error) => error.into(),
142 persistent_pagination::Error::Decode(error) => error.into(),
143 persistent_pagination::Error::Work(error) => error.into(),
144 }
145}
146
147#[derive(Debug, Error)]
149pub enum DirectoryReadError {
150 #[error("directory listing was cancelled")]
152 Cancelled,
153 #[error("directory listing transition state is invalid")]
155 TraversalState,
156 #[error("directory page limit must be non-zero")]
158 ZeroLimit,
159 #[error("directory page limit cannot be represented")]
161 LimitOverflow,
162 #[error("directory root is not a tree page")]
164 WrongRootKind,
165 #[error("directory listing limits are invalid")]
167 InvalidLimits,
168 #[error("directory tree exceeds its admitted height")]
170 HeightExceeded,
171 #[error("directory tree contains a cycle or alias")]
173 Cycle,
174 #[error("directory child bounds do not match its page")]
176 ChildBoundsMismatch,
177 #[error("directory listing allocation failed")]
179 AllocationFailed,
180 #[error(transparent)]
182 Storage(#[from] ObjectStoreError),
183 #[error(transparent)]
185 Decode(#[from] CanonicalDecodeError),
186 #[error(transparent)]
188 Work(#[from] WorkError),
189}
190
191#[cfg(all(test, feature = "memory"))]
192#[path = "tests/list.rs"]
193mod tests;