#[allow(unused_imports)]
use whereat::at;
use super::api::DecodeError;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Limits {
pub max_width: Option<u32>,
pub max_height: Option<u32>,
pub max_total_pixels: Option<u64>,
pub max_frame_count: Option<u64>,
pub max_file_size: Option<u64>,
pub max_memory: Option<u64>,
}
impl Default for Limits {
fn default() -> Self {
Self {
max_width: Some(16384),
max_height: Some(16384),
max_total_pixels: Some(100_000_000),
max_frame_count: Some(10_000),
max_file_size: Some(100 * 1024 * 1024),
max_memory: Some(1024 * 1024 * 1024),
}
}
}
impl Limits {
#[must_use]
pub fn none() -> Self {
Self {
max_width: None,
max_height: None,
max_total_pixels: None,
max_frame_count: None,
max_file_size: None,
max_memory: None,
}
}
#[must_use]
pub fn max_dimensions(mut self, width: u32, height: u32) -> Self {
self.max_width = Some(width);
self.max_height = Some(height);
self
}
#[must_use]
pub fn max_total_pixels(mut self, pixels: u64) -> Self {
self.max_total_pixels = Some(pixels);
self
}
#[must_use]
pub fn max_frame_count(mut self, count: u64) -> Self {
self.max_frame_count = Some(count);
self
}
#[must_use]
pub fn max_file_size(mut self, bytes: u64) -> Self {
self.max_file_size = Some(bytes);
self
}
#[must_use]
pub fn max_memory(mut self, bytes: u64) -> Self {
self.max_memory = Some(bytes);
self
}
pub fn check_dimensions(
&self,
width: u32,
height: u32,
) -> Result<(), whereat::At<DecodeError>> {
if let Some(max_w) = self.max_width
&& width > max_w
{
return Err(at!(DecodeError::InvalidParameter(alloc::format!(
"width {} exceeds limit {}",
width,
max_w
))));
}
if let Some(max_h) = self.max_height
&& height > max_h
{
return Err(at!(DecodeError::InvalidParameter(alloc::format!(
"height {} exceeds limit {}",
height,
max_h
))));
}
let total_pixels = width as u64 * height as u64;
if let Some(max_pixels) = self.max_total_pixels
&& total_pixels > max_pixels
{
return Err(at!(DecodeError::InvalidParameter(alloc::format!(
"total pixels {} exceeds limit {}",
total_pixels,
max_pixels
))));
}
Ok(())
}
pub fn check_frame_count(&self, count: usize) -> Result<(), whereat::At<DecodeError>> {
if let Some(max) = self.max_frame_count
&& count as u64 >= max
{
return Err(at!(DecodeError::InvalidParameter(alloc::format!(
"frame count {} exceeds limit {}",
count,
max
))));
}
Ok(())
}
pub fn check_file_size(&self, size: u64) -> Result<(), whereat::At<DecodeError>> {
if let Some(max) = self.max_file_size
&& size > max
{
return Err(at!(DecodeError::InvalidParameter(alloc::format!(
"file size {} bytes exceeds limit {} bytes",
size,
max
))));
}
Ok(())
}
pub fn check_memory(&self, bytes: usize) -> Result<(), whereat::At<DecodeError>> {
if let Some(max) = self.max_memory
&& bytes as u64 > max
{
return Err(at!(DecodeError::MemoryLimitExceeded));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_limits() {
let limits = Limits::default();
assert!(limits.max_width.is_some());
assert!(limits.max_height.is_some());
}
#[test]
fn check_dimensions_ok() {
let limits = Limits::default().max_dimensions(1000, 1000);
assert!(limits.check_dimensions(500, 500).is_ok());
assert!(limits.check_dimensions(1000, 1000).is_ok());
}
#[test]
fn check_dimensions_too_large() {
let limits = Limits::default().max_dimensions(1000, 1000);
let result = limits.check_dimensions(1001, 500);
assert!(result.is_err());
}
#[test]
fn check_total_pixels() {
let limits = Limits::default().max_total_pixels(1_000_000);
assert!(limits.check_dimensions(1000, 1000).is_ok());
assert!(limits.check_dimensions(1001, 1000).is_err());
}
#[test]
fn no_limits() {
let limits = Limits::none();
assert!(limits.check_dimensions(u32::MAX, u32::MAX).is_ok());
assert!(limits.check_frame_count(usize::MAX).is_ok());
}
#[test]
fn builder_pattern() {
let limits = Limits::default()
.max_dimensions(4096, 4096)
.max_frame_count(100)
.max_file_size(10 * 1024 * 1024)
.max_memory(512 * 1024 * 1024);
assert_eq!(limits.max_width, Some(4096));
assert_eq!(limits.max_frame_count, Some(100));
}
}