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 character length for a text value (data/runtime input).
9pub const MAX_TEXT_VALUE_LENGTH: usize = 1024;
10
11/// Validate that a name does not exceed the given character limit.
12/// `kind` is a human-readable noun like "spec", "data", "rule", or "type".
13pub fn check_max_length(
14    name: &str,
15    limit: usize,
16    kind: &str,
17    source: Option<Source>,
18) -> Result<(), Error> {
19    if name.len() > limit {
20        return Err(Error::resource_limit_exceeded(
21            format!("max_{kind}_name_length"),
22            format!("{limit} characters"),
23            format!("{} characters", name.len()),
24            format!("Shorten the {kind} name to at most {limit} characters"),
25            source,
26            None,
27            None,
28        ));
29    }
30    Ok(())
31}
32
33/// Limits to prevent abuse and enable predictable resource usage
34///
35/// These limits protect against malicious inputs while being generous enough
36/// for all legitimate use cases.
37#[derive(Debug, Clone, serde::Serialize)]
38pub struct ResourceLimits {
39    /// Maximum size of one loaded source text in bytes.
40    /// Real usage: ~5KB, Limit: 5MB (1000x)
41    pub max_source_size_bytes: usize,
42
43    /// Maximum expression nesting depth
44    /// Real usage: ~3 levels, Limit: 7. Deeper logic via rule composition.
45    pub max_expression_depth: usize,
46
47    /// Maximum expression nodes per source (parser-level)
48    /// Quick-reject for pathological single sources.
49    pub max_expression_count: usize,
50
51    /// Maximum size of a single data value in bytes
52    /// Real usage: ~100 bytes, Limit: 1KB (10x)
53    /// Enables server pre-allocation for zero-allocation evaluation
54    pub max_data_value_bytes: usize,
55
56    /// Maximum total bytes to load in one batch (and/or in-memory size of loaded specs)
57    pub max_loaded_bytes: usize,
58
59    /// Maximum number of sources in one load batch (e.g. after expanding paths on disk)
60    pub max_sources: usize,
61
62    /// Maximum unique normal-form cells reachable from one rule root in the
63    /// shared graph after normalize. Bounds planning work and shipped table size.
64    /// Default: 30,000.
65    pub max_normalized_expression_nodes: usize,
66
67    /// Maximum depth of the spec dependency chain (`uses` imports) from the
68    /// root spec. Bounds recursion in dependency discovery and graph building.
69    /// Real usage: ~3 levels, Limit: 32 (10x).
70    pub max_spec_dependency_depth: usize,
71
72    /// Maximum number of specs in one dependency DAG (the root spec plus all
73    /// transitive dependencies). Bounds per-plan memory and planning work.
74    pub max_dag_specs: usize,
75
76    /// Maximum nesting depth of a rule's normalized NormalForm DAG (leaves =
77    /// depth 1). The evaluator walks recursively, so planning must guarantee
78    /// no rule root can overflow the stack at run time. Lemma's runtime does
79    /// not return errors — this limit is the guarantee.
80    pub max_normal_form_depth: usize,
81}
82
83impl Default for ResourceLimits {
84    fn default() -> Self {
85        Self {
86            max_source_size_bytes: 5 * 1024 * 1024, // 5 MB
87            max_expression_depth: 7,
88            max_expression_count: 65_536,
89            max_data_value_bytes: 1024,         // 1 KB
90            max_loaded_bytes: 50 * 1024 * 1024, // 50 MB
91            max_sources: 4096,
92            max_normalized_expression_nodes: 30_000,
93            max_spec_dependency_depth: 32,
94            max_dag_specs: 4096,
95            // Bounds recursive eval stack depth on the shared NormalForm DAG.
96            max_normal_form_depth: 4096,
97        }
98    }
99}