Skip to main content

check_yaml_nesting_depth

Function check_yaml_nesting_depth 

Source
pub fn check_yaml_nesting_depth(
    content: &str,
    max_depth: usize,
) -> Result<(), usize>
Expand description

Scans raw YAML text for block/flow recursion deeper than max_depth.

YamlLoader::load_from_str has no public option to cap recursion, so callers must reject pathological input before handing it to the parser. This performs a single-pass structural scan — no actual parsing, so it cannot itself recurse or overflow — that bounds the two independent ways YAML content drives yaml-rust2’s recursion:

  • Flow-style bracket nesting: [/{ and ]/} pairs, as in [[[1]]] or {a: {a: 1}}.
  • Block-style indentation: each line whose leading indentation is deeper than the enclosing block context opens one nesting level (e.g. a mapping key or sequence item indented under its parent); each - in a compact chained sequence item (- - - 1) opens one level per dash, since it is equivalent to one nested single-item sequence per level.

Both counts accumulate into one shared depth budget bounded by max_depth. Line-start block indentation is scanned unconditionally on every line, even one that looks like a continuation of a still-open flow bracket from a previous line — an unclosed [/{ must never be able to suppress scanning for the rest of the file (impl-critic C2), so this guard accepts occasionally over-counting a multi-line flow collection’s continuation lines as extra block levels in exchange for never being able to go blind. A quote character is only treated as opening a quoted scalar when it sits at a token-start position (line start, or right after : , - , [, {, ,) — never mid-token — so an apostrophe inside a plain scalar like doesn't is left alone rather than mistaken for the start of a string (impl-critic C1). Once a quoted scalar is opened, it is only ever trusted to close on the same line: hitting an unescaped \n before the matching quote resynchronizes the scanner at that newline unconditionally (including across a \ right before it, which cannot extend the string past the line), rather than scanning forward indefinitely looking for a close — so neither a stray unquoted apostrophe nor a genuinely unterminated quoted scalar can ever blind the scanner to more than the remainder of one line. # outside a quoted scalar always starts a comment to end of line. Content indented under a literal/folded block scalar (|/>) is not specially exempted and is scanned like any other indentation, which can only make this guard more conservative, never less. Only ASCII space counts as indentation — a tab-indented line reads as indent 0, an assumption that currently holds only because yaml-rust2 itself rejects tabs used for block indentation before recursing deep enough to matter.

§Errors

Returns Err(depth) with the depth reached the instant nesting exceeds max_depth.

§Examples

use deps_core::parser::check_yaml_nesting_depth;

assert!(check_yaml_nesting_depth("a:\n  b:\n    c: 1\n", 4).is_ok());

let deeply_nested = format!("{}1", "- ".repeat(10));
assert!(check_yaml_nesting_depth(&deeply_nested, 4).is_err());

// An apostrophe mid-scalar must not blind the scanner to nesting later
// in the file (impl-critic C1).
let content = format!("a: it doesn't panic\n{}1", "- ".repeat(10));
assert!(check_yaml_nesting_depth(&content, 4).is_err());