Skip to main content

helm_schema/
load_budget.rs

1use std::io::Read;
2
3use crate::error::{CliError, EngineResult};
4
5/// Byte budgets for input assembly and local preparation work.
6///
7/// These limits are intentionally generous defaults. They exist to make
8/// archive extraction and external schema ingestion bounded by policy instead
9/// of accidentally unbounded.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[expect(
12    clippy::struct_field_names,
13    reason = "the shared max prefix makes each public budget field's bound explicit"
14)]
15pub struct LoadBudget {
16    /// Maximum compressed chart archive size.
17    pub max_chart_archive_bytes: usize,
18    /// Maximum size of one loaded JSON Schema document.
19    pub max_schema_document_bytes: usize,
20    /// Maximum number of members accepted in a chart archive.
21    pub max_chart_archive_entries: usize,
22    /// Maximum aggregate uncompressed size of chart archive members.
23    pub max_chart_archive_unpacked_bytes: usize,
24}
25
26impl LoadBudget {
27    /// Creates a budget with explicit document limits and conservative archive defaults.
28    #[must_use]
29    pub const fn new(max_chart_archive_bytes: usize, max_schema_document_bytes: usize) -> Self {
30        Self {
31            max_chart_archive_bytes,
32            max_schema_document_bytes,
33            max_chart_archive_entries: 4096,
34            max_chart_archive_unpacked_bytes: 256 * 1024 * 1024,
35        }
36    }
37}
38
39impl Default for LoadBudget {
40    fn default() -> Self {
41        Self::new(64 * 1024 * 1024, 16 * 1024 * 1024)
42    }
43}
44
45pub(crate) fn read_to_end_capped(
46    reader: &mut impl Read,
47    limit_bytes: usize,
48    subject: impl Into<String>,
49) -> EngineResult<Vec<u8>> {
50    let subject = subject.into();
51    let limit_plus_one = u64::try_from(limit_bytes)
52        .unwrap_or(u64::MAX)
53        .saturating_add(1);
54    let mut limited = reader.take(limit_plus_one);
55    let mut bytes = Vec::new();
56    limited.read_to_end(&mut bytes)?;
57    if bytes.len() > limit_bytes {
58        return Err(CliError::LoadBudgetExceeded {
59            subject,
60            limit_bytes,
61        });
62    }
63    Ok(bytes)
64}