use serde_yaml::{Mapping, Value};
pub(super) fn rendered(value: &Value) -> String {
let mut text = String::new();
collect_scalars(value, &mut text);
text
}
fn collect_scalars(value: &Value, text: &mut String) {
match value {
Value::Null => {}
Value::Bool(flag) => push_line(text, &flag.to_string()),
Value::Number(number) => push_line(text, &number.to_string()),
Value::String(string) => push_line(text, string),
Value::Sequence(items) => items.iter().for_each(|item| collect_scalars(item, text)),
Value::Mapping(mapping) => collect_mapping(mapping, text),
Value::Tagged(tagged) => collect_scalars(&tagged.value, text),
}
}
fn collect_mapping(mapping: &Mapping, text: &mut String) {
for (key, item) in mapping {
let mut key_text = String::new();
collect_scalars(key, &mut key_text);
push_line(text, &format!("{}:", key_text.trim_end()));
collect_scalars(item, text);
}
}
fn push_line(text: &mut String, line: &str) {
text.push_str(line);
text.push('\n');
}
pub(super) fn rendered_mapping(mapping: &Mapping) -> String {
let mut text = String::new();
collect_mapping(mapping, &mut text);
text
}
pub(super) fn computes_a_secret(text: &str) -> bool {
let bytes = text.as_bytes();
text.match_indices("secrets").any(|(start, word)| {
let before = start.checked_sub(1).map(|index| bytes[index]);
let is_word_start = before
.is_none_or(|byte| !(byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'.'));
let next = text[start + word.len()..].trim_start().chars().next();
is_word_start
&& !matches!(next, Some('.' | ':') | None)
&& !next.is_some_and(|c| c.is_ascii_alphanumeric() || c == '_')
})
}