use crate::error::FsError;
use crate::error::FsErrorKind;
use crate::error::FsOperation;
use crate::error::FsResult;
use crate::metadata::FileSystemLimit;
use crate::path::Path;
use crate::path::PathSemantics;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FileSystemLimits {
max_path_text_bytes: FileSystemLimit,
max_component_text_bytes: FileSystemLimit,
max_read_range_bytes: FileSystemLimit,
max_write_bytes: FileSystemLimit,
max_list_page_entries: FileSystemLimit,
}
impl FileSystemLimits {
#[inline]
#[must_use]
pub const fn unknown() -> Self {
Self {
max_path_text_bytes: FileSystemLimit::Unknown,
max_component_text_bytes: FileSystemLimit::Unknown,
max_read_range_bytes: FileSystemLimit::Unknown,
max_write_bytes: FileSystemLimit::Unknown,
max_list_page_entries: FileSystemLimit::Unknown,
}
}
#[inline]
#[must_use]
pub const fn with_max_path_text_bytes(mut self, limit: FileSystemLimit) -> Self {
self.max_path_text_bytes = limit;
self
}
#[inline]
#[must_use]
pub const fn with_max_component_text_bytes(mut self, limit: FileSystemLimit) -> Self {
self.max_component_text_bytes = limit;
self
}
#[inline]
#[must_use]
pub const fn with_max_read_range_bytes(mut self, limit: FileSystemLimit) -> Self {
self.max_read_range_bytes = limit;
self
}
#[inline]
#[must_use]
pub const fn with_max_write_bytes(mut self, limit: FileSystemLimit) -> Self {
self.max_write_bytes = limit;
self
}
#[inline]
#[must_use]
pub const fn with_max_list_page_entries(mut self, limit: FileSystemLimit) -> Self {
self.max_list_page_entries = limit;
self
}
#[inline]
#[must_use]
pub const fn max_path_text_bytes(&self) -> FileSystemLimit {
self.max_path_text_bytes
}
#[inline]
#[must_use]
pub const fn max_component_text_bytes(&self) -> FileSystemLimit {
self.max_component_text_bytes
}
#[inline]
#[must_use]
pub const fn max_read_range_bytes(&self) -> FileSystemLimit {
self.max_read_range_bytes
}
#[inline]
#[must_use]
pub const fn max_write_bytes(&self) -> FileSystemLimit {
self.max_write_bytes
}
#[inline]
#[must_use]
pub const fn max_list_page_entries(&self) -> FileSystemLimit {
self.max_list_page_entries
}
#[inline]
#[must_use]
pub fn clamp_list_page_size(&self, requested: Option<usize>) -> Option<usize> {
let requested = requested?;
match self.max_list_page_entries {
FileSystemLimit::Maximum(maximum) => {
usize::try_from(maximum).map_or(Some(requested), |maximum| Some(requested.min(maximum)))
}
FileSystemLimit::Unknown | FileSystemLimit::NotApplicable | FileSystemLimit::Unbounded => Some(requested),
}
}
pub fn validate_path(&self, path: &Path, semantics: PathSemantics, operation: FsOperation) -> FsResult<()> {
if exceeds_usize(self.max_path_text_bytes, path.as_str().len()) {
return Err(limit_error(
operation,
"path text exceeds the provider byte limit",
path,
));
}
if semantics == PathSemantics::Hierarchical
&& path
.as_str()
.split('/')
.any(|component| !component.is_empty() && exceeds_usize(self.max_component_text_bytes, component.len()))
{
return Err(limit_error(
operation,
"path component exceeds the provider byte limit",
path,
));
}
Ok(())
}
pub fn validate_read_range(&self, path: &Path, length: Option<u64>) -> FsResult<()> {
if length.is_some_and(|length| self.max_read_range_bytes.is_exceeded_by(length)) {
Err(limit_error(
FsOperation::OpenReader,
"requested range exceeds the provider byte limit",
path,
))
} else {
Ok(())
}
}
pub fn validate_write_size(&self, path: &Path, bytes: usize) -> FsResult<()> {
if exceeds_usize(self.max_write_bytes, bytes) {
Err(limit_error(
FsOperation::Write,
"write session exceeds the provider byte limit",
path,
))
} else {
Ok(())
}
}
pub(crate) fn validate_write_size_u64(&self, path: &Path, bytes: u64) -> FsResult<()> {
if self.max_write_bytes.is_exceeded_by(bytes) {
Err(limit_error(
FsOperation::Write,
"write session exceeds the provider byte limit",
path,
))
} else {
Ok(())
}
}
}
fn exceeds_usize(limit: FileSystemLimit, actual: usize) -> bool {
match u64::try_from(actual) {
Ok(actual) => limit.is_exceeded_by(actual),
Err(_) => matches!(limit, FileSystemLimit::Maximum(_)),
}
}
fn limit_error(operation: FsOperation, message: &'static str, path: &Path) -> FsError {
FsError::new(FsErrorKind::ResourceLimitExceeded, operation, message).with_path(path.clone())
}