qubit_fs/metadata/
file_system_limits.rs1use crate::error::FsError;
11use crate::error::FsErrorKind;
12use crate::error::FsOperation;
13use crate::error::FsResult;
14use crate::metadata::FileSystemLimit;
15use crate::path::Path;
16use crate::path::PathSemantics;
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub struct FileSystemLimits {
30 max_path_text_bytes: FileSystemLimit,
32 max_component_text_bytes: FileSystemLimit,
34 max_read_range_bytes: FileSystemLimit,
36 max_write_bytes: FileSystemLimit,
38 max_list_page_entries: FileSystemLimit,
40}
41
42impl FileSystemLimits {
43 #[inline]
45 #[must_use]
46 pub const fn unknown() -> Self {
47 Self {
48 max_path_text_bytes: FileSystemLimit::Unknown,
49 max_component_text_bytes: FileSystemLimit::Unknown,
50 max_read_range_bytes: FileSystemLimit::Unknown,
51 max_write_bytes: FileSystemLimit::Unknown,
52 max_list_page_entries: FileSystemLimit::Unknown,
53 }
54 }
55
56 #[inline]
58 #[must_use]
59 pub const fn with_max_path_text_bytes(mut self, limit: FileSystemLimit) -> Self {
60 self.max_path_text_bytes = limit;
61 self
62 }
63
64 #[inline]
66 #[must_use]
67 pub const fn with_max_component_text_bytes(mut self, limit: FileSystemLimit) -> Self {
68 self.max_component_text_bytes = limit;
69 self
70 }
71
72 #[inline]
74 #[must_use]
75 pub const fn with_max_read_range_bytes(mut self, limit: FileSystemLimit) -> Self {
76 self.max_read_range_bytes = limit;
77 self
78 }
79
80 #[inline]
82 #[must_use]
83 pub const fn with_max_write_bytes(mut self, limit: FileSystemLimit) -> Self {
84 self.max_write_bytes = limit;
85 self
86 }
87
88 #[inline]
91 #[must_use]
92 pub const fn with_max_list_page_entries(mut self, limit: FileSystemLimit) -> Self {
93 self.max_list_page_entries = limit;
94 self
95 }
96
97 #[inline]
100 #[must_use]
101 pub const fn max_path_text_bytes(&self) -> FileSystemLimit {
102 self.max_path_text_bytes
103 }
104
105 #[inline]
107 #[must_use]
108 pub const fn max_component_text_bytes(&self) -> FileSystemLimit {
109 self.max_component_text_bytes
110 }
111
112 #[inline]
114 #[must_use]
115 pub const fn max_read_range_bytes(&self) -> FileSystemLimit {
116 self.max_read_range_bytes
117 }
118
119 #[inline]
121 #[must_use]
122 pub const fn max_write_bytes(&self) -> FileSystemLimit {
123 self.max_write_bytes
124 }
125
126 #[inline]
128 #[must_use]
129 pub const fn max_list_page_entries(&self) -> FileSystemLimit {
130 self.max_list_page_entries
131 }
132
133 #[inline]
145 #[must_use]
146 pub fn clamp_list_page_size(&self, requested: Option<usize>) -> Option<usize> {
147 let requested = requested?;
148 match self.max_list_page_entries {
149 FileSystemLimit::Maximum(maximum) => {
150 usize::try_from(maximum).map_or(Some(requested), |maximum| Some(requested.min(maximum)))
151 }
152 FileSystemLimit::Unknown | FileSystemLimit::NotApplicable | FileSystemLimit::Unbounded => Some(requested),
153 }
154 }
155
156 pub fn validate_path(&self, path: &Path, semantics: PathSemantics, operation: FsOperation) -> FsResult<()> {
164 if exceeds_usize(self.max_path_text_bytes, path.as_str().len()) {
165 return Err(limit_error(
166 operation,
167 "path text exceeds the provider byte limit",
168 path,
169 ));
170 }
171 if semantics == PathSemantics::Hierarchical
172 && path
173 .as_str()
174 .split('/')
175 .any(|component| !component.is_empty() && exceeds_usize(self.max_component_text_bytes, component.len()))
176 {
177 return Err(limit_error(
178 operation,
179 "path component exceeds the provider byte limit",
180 path,
181 ));
182 }
183 Ok(())
184 }
185
186 pub fn validate_read_range(&self, path: &Path, length: Option<u64>) -> FsResult<()> {
195 if length.is_some_and(|length| self.max_read_range_bytes.is_exceeded_by(length)) {
196 Err(limit_error(
197 FsOperation::OpenReader,
198 "requested range exceeds the provider byte limit",
199 path,
200 ))
201 } else {
202 Ok(())
203 }
204 }
205
206 pub fn validate_write_size(&self, path: &Path, bytes: usize) -> FsResult<()> {
212 if exceeds_usize(self.max_write_bytes, bytes) {
213 Err(limit_error(
214 FsOperation::Write,
215 "write session exceeds the provider byte limit",
216 path,
217 ))
218 } else {
219 Ok(())
220 }
221 }
222
223 pub(crate) fn validate_write_size_u64(&self, path: &Path, bytes: u64) -> FsResult<()> {
229 if self.max_write_bytes.is_exceeded_by(bytes) {
230 Err(limit_error(
231 FsOperation::Write,
232 "write session exceeds the provider byte limit",
233 path,
234 ))
235 } else {
236 Ok(())
237 }
238 }
239}
240
241fn exceeds_usize(limit: FileSystemLimit, actual: usize) -> bool {
245 match u64::try_from(actual) {
246 Ok(actual) => limit.is_exceeded_by(actual),
247 Err(_) => matches!(limit, FileSystemLimit::Maximum(_)),
248 }
249}
250
251fn limit_error(operation: FsOperation, message: &'static str, path: &Path) -> FsError {
253 FsError::new(FsErrorKind::ResourceLimitExceeded, operation, message).with_path(path.clone())
254}