Skip to main content

lemma/
limits.rs

1use crate::error::Error;
2use crate::parsing::source::Source;
3
4pub const MAX_SPEC_NAME_LENGTH: usize = 128;
5pub const MAX_DATA_NAME_LENGTH: usize = 256;
6pub const MAX_RULE_NAME_LENGTH: usize = 256;
7
8/// Maximum significant digits in a number string. rust_decimal supports at most 28;
9/// more can panic or overflow in parse or arithmetic.
10pub const MAX_NUMBER_DIGITS: usize = 28;
11
12/// Maximum character length for a text value (data/runtime input).
13pub const MAX_TEXT_VALUE_LENGTH: usize = 1024;
14
15/// Validate that a name does not exceed the given character limit.
16/// `kind` is a human-readable noun like "spec", "data", "rule", or "type".
17pub fn check_max_length(
18    name: &str,
19    limit: usize,
20    kind: &str,
21    source: Option<Source>,
22) -> Result<(), Error> {
23    if name.len() > limit {
24        return Err(Error::resource_limit_exceeded(
25            format!("max_{kind}_name_length"),
26            format!("{limit} characters"),
27            format!("{} characters", name.len()),
28            format!("Shorten the {kind} name to at most {limit} characters"),
29            source,
30            None,
31            None,
32        ));
33    }
34    Ok(())
35}
36
37/// Limits to prevent abuse and enable predictable resource usage
38///
39/// These limits protect against malicious inputs while being generous enough
40/// for all legitimate use cases.
41#[derive(Debug, Clone)]
42pub struct ResourceLimits {
43    /// Maximum size of one loaded source text in bytes.
44    /// Real usage: ~5KB, Limit: 5MB (1000x)
45    pub max_source_size_bytes: usize,
46
47    /// Maximum expression nesting depth
48    /// Real usage: ~3 levels, Limit: 7. Deeper logic via rule composition.
49    pub max_expression_depth: usize,
50
51    /// Maximum expression nodes per source (parser-level)
52    /// Quick-reject for pathological single sources.
53    pub max_expression_count: usize,
54
55    /// Maximum total expression nodes across all sources (engine-level)
56    /// The real capacity ceiling. pi (~3.1M) — generous for national-scale
57    /// regulatory systems while bounding total engine workload.
58    pub max_total_expression_count: usize,
59
60    /// Maximum size of a single data value in bytes
61    /// Real usage: ~100 bytes, Limit: 1KB (10x)
62    /// Enables server pre-allocation for zero-allocation evaluation
63    pub max_data_value_bytes: usize,
64
65    /// Maximum total bytes to load in one batch (and/or in-memory size of loaded specs)
66    pub max_loaded_bytes: usize,
67
68    /// Maximum number of sources in one load batch (e.g. after expanding paths on disk)
69    pub max_sources: usize,
70
71    /// Maximum expression nodes for one rule after transitive rule inlining
72    /// during planning. Inlining materializes shared subtrees, so a short
73    /// chain of self-doubling rules grows exponentially; this limit rejects
74    /// such chains with a planning error before any tree is materialized.
75    ///
76    /// The default is chosen so the compiled instruction operands always fit
77    /// `u16`: compilation allocates at most two registers (and at most one
78    /// constant/data/veto table entry) per node, and normalization passes
79    /// grow the tree by at most a small constant factor, so 30,000 nodes
80    /// stays well below the 65,535 register ceiling.
81    pub max_normalized_expression_nodes: usize,
82}
83
84impl Default for ResourceLimits {
85    fn default() -> Self {
86        Self {
87            max_source_size_bytes: 5 * 1024 * 1024, // 5 MB
88            max_expression_depth: 7,
89            max_expression_count: 4096,
90            max_total_expression_count: 3_141_592,
91            max_data_value_bytes: 1024,         // 1 KB
92            max_loaded_bytes: 50 * 1024 * 1024, // 50 MB
93            max_sources: 4096,
94            max_normalized_expression_nodes: 30_000,
95        }
96    }
97}