Skip to main content

appcore_filemaker/
limits.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: limits.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/08/30 05:00:00 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/08/30 05:00:00 by dnettoRaw
8//      ###########      S: 1.0.2-rc
9// =============================================================================
10
11use serde::{Deserialize, Serialize};
12
13use crate::{ErrorCode, FileMakerError, Result};
14
15/// Explicit limits applied throughout parsing, compilation, and export.
16#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
17#[serde(default, deny_unknown_fields)]
18pub struct ResourceLimits {
19    /// Maximum YAML/template bytes.
20    pub max_template_bytes: usize,
21    /// Maximum total bytes loaded through include resolvers.
22    pub max_include_bytes: usize,
23    /// Maximum include nesting depth.
24    pub max_include_depth: usize,
25    /// Maximum resolved elements across all pages.
26    pub max_elements: usize,
27    /// Maximum vector path commands across one source template.
28    pub max_path_commands: usize,
29    /// Maximum generated pages.
30    pub max_pages: usize,
31    /// Maximum Unicode scalar bytes in one text node.
32    pub max_text_bytes: usize,
33    /// Maximum bytes accepted for one explicitly resolved asset or font.
34    pub max_asset_bytes: usize,
35    /// Maximum raster output pixels.
36    pub max_pixels: u64,
37    /// Maximum collision/reflow iterations.
38    pub max_reflows: usize,
39    /// Maximum spatial-rule comparisons during one layout.
40    pub max_collision_comparisons: usize,
41    /// Maximum expression operations per evaluation.
42    pub max_expression_steps: usize,
43    /// Maximum runtime patch operations in one transaction or complete bind batch.
44    pub max_patch_operations: usize,
45    /// Maximum geometry comparisons or retained diagnostics during preflight,
46    /// masks, overlays, and free-region inspection.
47    pub max_preflight_comparisons: usize,
48    /// Maximum streamed dataset rows.
49    pub max_rows: u64,
50    /// Maximum total output bytes written by in-memory helpers.
51    pub max_output_bytes: usize,
52}
53
54impl Default for ResourceLimits {
55    fn default() -> Self {
56        Self {
57            max_template_bytes: 4 * 1024 * 1024,
58            max_include_bytes: 16 * 1024 * 1024,
59            max_include_depth: 16,
60            max_elements: 100_000,
61            max_path_commands: 1_000_000,
62            max_pages: 10_000,
63            max_text_bytes: 4 * 1024 * 1024,
64            max_asset_bytes: 256 * 1024 * 1024,
65            max_pixels: 100_000_000,
66            max_reflows: 128,
67            max_collision_comparisons: 1_000_000,
68            max_expression_steps: 10_000,
69            max_patch_operations: 10_000,
70            max_preflight_comparisons: 1_000_000,
71            max_rows: 10_000_000,
72            max_output_bytes: 512 * 1024 * 1024,
73        }
74    }
75}
76
77impl ResourceLimits {
78    /// Rejects zero or internally inconsistent bounds.
79    pub fn validate(&self) -> Result<()> {
80        let all_nonzero = self.max_template_bytes > 0
81            && self.max_include_bytes > 0
82            && self.max_include_depth > 0
83            && self.max_elements > 0
84            && self.max_path_commands > 0
85            && self.max_pages > 0
86            && self.max_text_bytes > 0
87            && self.max_asset_bytes > 0
88            && self.max_pixels > 0
89            && self.max_reflows > 0
90            && self.max_collision_comparisons > 0
91            && self.max_expression_steps > 0
92            && self.max_patch_operations > 0
93            && self.max_preflight_comparisons > 0
94            && self.max_rows > 0
95            && self.max_output_bytes > 0;
96        if !all_nonzero {
97            return Err(FileMakerError::new(
98                ErrorCode::LimitExceeded,
99                "resource limits must be non-zero",
100            ));
101        }
102        Ok(())
103    }
104
105    /// Checks a named observed value against its limit.
106    #[allow(dead_code)]
107    pub(crate) fn check(name: &'static str, observed: usize, limit: usize) -> Result<()> {
108        if observed > limit {
109            return Err(FileMakerError::new(
110                ErrorCode::LimitExceeded,
111                format!("{name} is {observed}; limit is {limit}"),
112            ));
113        }
114        Ok(())
115    }
116}