Skip to main content

qrcode_core/
limits.rs

1//! Resource budgets applied at QR-code construction boundaries.
2
3use crate::types::{QrError, QrResult, Version};
4
5/// The largest input accepted by the default resource budget.
6///
7/// This is deliberately a conservative upper bound for a QR byte payload.
8/// Individual versions and error-correction levels usually have a smaller
9/// capacity and remain the final authority during encoding.
10pub const DEFAULT_MAX_DATA_LENGTH: usize = 7_089;
11
12/// The largest rendered dimension allowed by the default resource budget.
13pub const DEFAULT_MAX_RENDER_SIZE: (u32, u32) = (4_096, 4_096);
14
15/// Explicit resource budgets for bounded QR-code construction.
16///
17/// [`QrCode::with_limits`](https://docs.rs/qrcode-rs/latest/qrcode_rs/struct.QrCode.html#method.with_limits)
18/// applies these limits before allocating encoder state and after selecting
19/// the resulting symbol dimensions. The dimensions are the maximum width and
20/// height of the symbol passed to a renderer; a renderer may impose a stricter
21/// pixel budget of its own.
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct ResourceLimits {
25    /// Maximum number of input bytes accepted before parsing or allocation.
26    pub max_data_length: usize,
27    /// Maximum normal QR version considered by automatic version selection.
28    pub max_version: Version,
29    /// Maximum `(width, height)` accepted for the generated module symbol.
30    pub max_render_size: (u32, u32),
31}
32
33impl Default for ResourceLimits {
34    fn default() -> Self {
35        Self {
36            max_data_length: DEFAULT_MAX_DATA_LENGTH,
37            max_version: Version::Normal(40),
38            max_render_size: DEFAULT_MAX_RENDER_SIZE,
39        }
40    }
41}
42
43impl ResourceLimits {
44    /// Creates an explicit resource budget.
45    #[must_use]
46    pub const fn new(max_data_length: usize, max_version: Version, max_render_size: (u32, u32)) -> Self {
47        Self { max_data_length, max_version, max_render_size }
48    }
49
50    /// Validates the shape of this budget without inspecting input data.
51    ///
52    /// Automatic construction currently targets normal QR versions, so Micro
53    /// versions are rejected here rather than being silently interpreted as a
54    /// normal-version cap. Zero render dimensions cannot describe a symbol.
55    pub fn validate(self) -> QrResult<()> {
56        let valid_version = matches!(self.max_version, Version::Normal(1..=40));
57        let valid_render_size = self.max_render_size.0 != 0 && self.max_render_size.1 != 0;
58        if valid_version && valid_render_size { Ok(()) } else { Err(QrError::InvalidResourceLimits) }
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::{DEFAULT_MAX_DATA_LENGTH, DEFAULT_MAX_RENDER_SIZE, ResourceLimits};
65    use crate::types::Version;
66
67    #[test]
68    fn default_budget_is_bounded() {
69        let limits = ResourceLimits::default();
70        assert_eq!(limits.max_data_length, DEFAULT_MAX_DATA_LENGTH);
71        assert_eq!(limits.max_version, Version::Normal(40));
72        assert_eq!(limits.max_render_size, DEFAULT_MAX_RENDER_SIZE);
73        assert!(limits.validate().is_ok());
74    }
75
76    #[test]
77    fn invalid_budget_is_rejected() {
78        assert!(ResourceLimits::new(1, Version::Micro(4), (1, 1)).validate().is_err());
79        assert!(ResourceLimits::new(1, Version::Normal(1), (0, 1)).validate().is_err());
80    }
81}