use std::fmt;
pub const MAX_FRAGMENT_BYTES: usize = 256 * 1024;
const ECHO_BYTES: usize = 120;
pub(super) fn clip(text: &str) -> std::borrow::Cow<'_, str> {
if text.len() <= ECHO_BYTES {
return text.into();
}
let mut end = ECHO_BYTES;
while !text.is_char_boundary(end) {
end -= 1;
}
format!("{}... ({} bytes)", &text[..end], text.len()).into()
}
pub(super) fn set_failed(path: &str, value: &str, e: &impl fmt::Display) -> String {
format!("set {} = {}: {e}", clip(path), clip(value))
}
pub(super) fn check_text(yaml: &str, config: &noyalib::ParserConfig) -> Result<(), String> {
if yaml.len() > config.max_document_length {
return Err(format!(
"the YAML text is {} bytes, over the {}-byte document limit",
yaml.len(),
config.max_document_length
));
}
Ok(())
}
pub(super) fn check_fragment(value: &str, config: &noyalib::ParserConfig) -> Result<(), String> {
if value.len() > MAX_FRAGMENT_BYTES {
return Err(format!(
"the value is {} bytes, over the {MAX_FRAGMENT_BYTES}-byte fragment limit",
value.len()
));
}
let depth = nesting_depth(value);
if depth > config.max_depth {
return Err(format!(
"the value nests {depth} levels deep, over the limit of {}",
config.max_depth
));
}
Ok(())
}
pub(super) fn nesting_depth(text: &str) -> usize {
let mut flow = 0usize;
let mut indents: Vec<usize> = Vec::new();
let mut deepest = 0;
for line in text.lines() {
let body = line.trim_start_matches(' ');
let indent = line.len() - body.len();
if !body.trim().is_empty() {
while indents.last().is_some_and(|&top| top > indent) {
let _ = indents.pop();
}
if indents.last().is_none_or(|&top| top < indent) {
indents.push(indent);
}
}
let (indicators, peak) = scan_line(body, &mut flow);
deepest = deepest.max(indents.len() + indicators + peak);
}
deepest
}
fn scan_line(body: &str, flow: &mut usize) -> (usize, usize) {
let bytes = body.as_bytes();
let mut indicators = 0;
let mut peak = *flow;
for (i, &b) in bytes.iter().enumerate() {
let spaced = bytes.get(i + 1).is_none_or(|n| *n == b' ' || *n == b'\t');
match b {
b'[' | b'{' => {
*flow += 1;
peak = peak.max(*flow);
}
b']' | b'}' => *flow = flow.saturating_sub(1),
b'-' | b'?' | b':' if spaced => indicators += 1,
_ => {}
}
}
(indicators, peak)
}