ass_core/utils/errors/resource.rs
1//! Resource management error utilities for ASS-RS
2//!
3//! Provides specialized error creation and validation functions for resource
4//! limits including memory allocation, processing limits, and other system
5//! resource constraints. Focuses on preventing resource exhaustion attacks.
6
7use super::CoreError;
8use alloc::{format, string::ToString};
9use core::fmt;
10
11/// Create memory allocation error
12///
13/// Generates a `CoreError::OutOfMemory` with descriptive context about
14/// the failed memory allocation attempt.
15///
16/// # Arguments
17///
18/// * `context` - Description of what allocation failed
19///
20/// # Examples
21///
22/// ```rust
23/// use ass_core::utils::errors::{out_of_memory, CoreError};
24///
25/// let error = out_of_memory("Failed to allocate parser buffer");
26/// assert!(matches!(error, CoreError::OutOfMemory(_)));
27/// ```
28pub fn out_of_memory<T: fmt::Display>(context: T) -> CoreError {
29 CoreError::OutOfMemory(format!("{context}"))
30}
31
32/// Create resource limit exceeded error
33///
34/// Generates a `CoreError::ResourceLimitExceeded` with detailed information
35/// about which resource was exceeded and by how much.
36///
37/// # Arguments
38///
39/// * `resource` - Name of the resource that was exceeded
40/// * `current` - Current usage that triggered the limit
41/// * `limit` - Maximum allowed usage
42#[must_use]
43pub fn resource_limit_exceeded(resource: &str, current: usize, limit: usize) -> CoreError {
44 CoreError::ResourceLimitExceeded {
45 resource: resource.to_string(),
46 current,
47 limit,
48 }
49}
50
51/// Create feature not supported error
52///
53/// Generates a `CoreError::FeatureNotSupported` when functionality requires
54/// a feature that is not enabled in the current build configuration.
55///
56/// # Arguments
57///
58/// * `feature` - The feature that was requested
59/// * `required_feature` - The Cargo feature flag needed to enable it
60#[must_use]
61pub fn feature_not_supported(feature: &str, required_feature: &str) -> CoreError {
62 CoreError::FeatureNotSupported {
63 feature: feature.to_string(),
64 required_feature: required_feature.to_string(),
65 }
66}
67
68/// Check memory usage against limit
69///
70/// Validates current memory usage against a configured limit and returns
71/// an error if the limit would be exceeded. Used to prevent OOM conditions.
72///
73/// # Arguments
74///
75/// * `current_bytes` - Current memory usage in bytes
76/// * `additional_bytes` - Additional bytes that would be allocated
77/// * `limit_bytes` - Maximum allowed memory usage
78///
79/// # Errors
80///
81/// Returns an error if the memory limit would be exceeded.
82pub fn check_memory_limit(
83 current_bytes: usize,
84 additional_bytes: usize,
85 limit_bytes: usize,
86) -> Result<(), CoreError> {
87 let total = current_bytes
88 .checked_add(additional_bytes)
89 .ok_or_else(|| out_of_memory("Integer overflow calculating memory usage"))?;
90
91 if total > limit_bytes {
92 return Err(resource_limit_exceeded("memory", total, limit_bytes));
93 }
94
95 Ok(())
96}
97
98/// Check input size limit
99///
100/// Validates input size against configured limits to prevent processing
101/// of excessively large inputs that could cause resource exhaustion.
102///
103/// # Arguments
104///
105/// * `input_size` - Size of input data in bytes
106/// * `max_size` - Maximum allowed input size
107///
108/// # Errors
109///
110/// Returns an error if input size exceeds the maximum allowed size
111pub fn check_input_size_limit(input_size: usize, max_size: usize) -> Result<(), CoreError> {
112 if input_size > max_size {
113 return Err(resource_limit_exceeded("input_size", input_size, max_size));
114 }
115 Ok(())
116}
117
118/// Check nesting depth limit
119///
120/// Prevents stack overflow from deeply nested structures by validating
121/// nesting depth against a configured maximum.
122///
123/// # Arguments
124///
125/// * `current_depth` - Current nesting depth
126/// * `max_depth` - Maximum allowed nesting depth
127///
128/// # Errors
129///
130/// Returns an error if current depth exceeds the maximum allowed depth
131pub fn check_depth_limit(current_depth: usize, max_depth: usize) -> Result<(), CoreError> {
132 if current_depth > max_depth {
133 return Err(resource_limit_exceeded(
134 "nesting_depth",
135 current_depth,
136 max_depth,
137 ));
138 }
139 Ok(())
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn out_of_memory_creation() {
148 let error = out_of_memory("Buffer allocation failed");
149 assert!(matches!(error, CoreError::OutOfMemory(_)));
150 }
151
152 #[test]
153 fn resource_limit_creation() {
154 let error = resource_limit_exceeded("memory", 1000, 500);
155 assert!(matches!(error, CoreError::ResourceLimitExceeded { .. }));
156 }
157
158 #[test]
159 fn feature_not_supported_creation() {
160 let error = feature_not_supported("simd", "simd");
161 assert!(matches!(error, CoreError::FeatureNotSupported { .. }));
162 }
163
164 #[test]
165 fn memory_limit_within_bounds() {
166 assert!(check_memory_limit(100, 50, 200).is_ok());
167 assert!(check_memory_limit(0, 100, 100).is_ok());
168 }
169
170 #[test]
171 fn memory_limit_exceeded() {
172 assert!(check_memory_limit(100, 150, 200).is_err());
173 assert!(check_memory_limit(150, 100, 200).is_err());
174 }
175
176 #[test]
177 fn memory_limit_overflow() {
178 let result = check_memory_limit(usize::MAX, 1, usize::MAX);
179 assert!(result.is_err());
180 assert!(matches!(result.unwrap_err(), CoreError::OutOfMemory(_)));
181 }
182
183 #[test]
184 fn input_size_limit_check() {
185 assert!(check_input_size_limit(500, 1000).is_ok());
186 assert!(check_input_size_limit(1500, 1000).is_err());
187 }
188
189 #[test]
190 fn depth_limit_check() {
191 assert!(check_depth_limit(5, 10).is_ok());
192 assert!(check_depth_limit(15, 10).is_err());
193 }
194}