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}