lemma-engine 0.8.12

A language that means business.
Documentation
use crate::error::Error;
use crate::parsing::source::Source;

pub const MAX_SPEC_NAME_LENGTH: usize = 128;
pub const MAX_DATA_NAME_LENGTH: usize = 256;
pub const MAX_RULE_NAME_LENGTH: usize = 256;

/// Maximum significant digits in a number string. rust_decimal supports at most 28;
/// more can panic or overflow in parse or arithmetic.
pub const MAX_NUMBER_DIGITS: usize = 28;

/// Maximum character length for a text value (data/runtime input).
pub const MAX_TEXT_VALUE_LENGTH: usize = 1024;

/// Validate that a name does not exceed the given character limit.
/// `kind` is a human-readable noun like "spec", "data", "rule", or "type".
pub fn check_max_length(
    name: &str,
    limit: usize,
    kind: &str,
    source: Option<Source>,
) -> Result<(), Error> {
    if name.len() > limit {
        return Err(Error::resource_limit_exceeded(
            format!("max_{kind}_name_length"),
            format!("{limit} characters"),
            format!("{} characters", name.len()),
            format!("Shorten the {kind} name to at most {limit} characters"),
            source,
            None,
            None,
        ));
    }
    Ok(())
}

/// Limits to prevent abuse and enable predictable resource usage
///
/// These limits protect against malicious inputs while being generous enough
/// for all legitimate use cases.
#[derive(Debug, Clone)]
pub struct ResourceLimits {
    /// Maximum file size in bytes
    /// Real usage: ~5KB, Limit: 5MB (1000x)
    pub max_file_size_bytes: usize,

    /// Maximum expression nesting depth
    /// Real usage: ~3 levels, Limit: 7. Deeper logic via rule composition.
    pub max_expression_depth: usize,

    /// Maximum expression nodes per file (parser-level)
    /// Quick-reject for pathological single files.
    pub max_expression_count: usize,

    /// Maximum total expression nodes across all files (engine-level)
    /// The real capacity ceiling. pi (~3.1M) — generous for national-scale
    /// regulatory systems while bounding total engine workload.
    pub max_total_expression_count: usize,

    /// Maximum size of a single data value in bytes
    /// Real usage: ~100 bytes, Limit: 1KB (10x)
    /// Enables server pre-allocation for zero-allocation evaluation
    pub max_data_value_bytes: usize,

    /// Maximum total bytes to read in a single load_from_paths call (and/or in-memory size of loaded specs)
    pub max_loaded_bytes: usize,

    /// Maximum number of .lemma files to load in a single load_from_paths call
    pub max_files: usize,
}

impl Default for ResourceLimits {
    fn default() -> Self {
        Self {
            max_file_size_bytes: 5 * 1024 * 1024, // 5 MB
            max_expression_depth: 7,
            max_expression_count: 4096,
            max_total_expression_count: 3_141_592,
            max_data_value_bytes: 1024,         // 1 KB
            max_loaded_bytes: 50 * 1024 * 1024, // 50 MB
            max_files: 4096,
        }
    }
}