use super::engine::TEMPLATE_KEY_ESCAPE;
use super::secrets::SECRET_OPERATOR as SECRET;
use crate::base::AuthoredPath;
use serde_json::Value;
pub fn escaped_keys(raw: &Value) -> impl Iterator<Item = AuthoredPath> + '_ {
let mut out = Vec::new();
let mut at = AuthoredPath::root();
walk_escaped(raw, &mut at, &mut out);
out.into_iter()
}
fn walk_escaped(value: &Value, at: &mut AuthoredPath, out: &mut Vec<AuthoredPath>) {
match value {
Value::Array(items) => {
for (i, item) in items.iter().enumerate() {
at.push(i);
walk_escaped(item, at, out);
at.pop();
}
}
Value::Object(map) => {
for key in map.keys() {
let mut chars = key.chars();
if chars.next() == Some(TEMPLATE_KEY_ESCAPE)
&& chars.next() != Some(TEMPLATE_KEY_ESCAPE)
{
out.push(at.join(key.as_str()));
}
}
for (key, child) in map {
at.push(key.as_str());
walk_escaped(child, at, out);
at.pop();
}
}
_ => {}
}
}
pub fn secret_refs(raw: &Value) -> impl Iterator<Item = (Option<&str>, AuthoredPath)> + '_ {
let mut stack = vec![(raw, AuthoredPath::root())];
std::iter::from_fn(move || {
while let Some((value, at)) = stack.pop() {
match value {
Value::Object(map) => {
if map.len() == 1
&& let Some(arg) = map.get(SECRET)
{
let literal = match arg {
Value::String(s) => Some(s.as_str()),
Value::Array(items) if items.len() == 1 => items[0].as_str(),
_ => None,
};
stack.push((arg, at.join(SECRET)));
return Some((literal, at));
}
for (key, child) in map.iter().rev() {
stack.push((child, at.join(key.as_str())));
}
}
Value::Array(items) => {
for (i, child) in items.iter().enumerate().rev() {
stack.push((child, at.join(i)));
}
}
_ => {}
}
}
None
})
}
pub(crate) fn reads_secret(raw: &Value) -> bool {
match raw {
Value::Object(map) => {
(map.len() == 1 && map.contains_key(SECRET)) || map.values().any(reads_secret)
}
Value::Array(items) => items.iter().any(reads_secret),
_ => false,
}
}