const MAX_RECURSION_DEPTH: usize = 1000;
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum Value {
Str(String),
Other,
Seq(Vec<Value>),
Map(Vec<Value>),
}
pub(crate) fn collect(value: &Value) -> Vec<String> {
let mut out = Vec::new();
walk(value, &mut out, 0);
out
}
fn walk(value: &Value, out: &mut Vec<String>, depth: usize) {
if depth > MAX_RECURSION_DEPTH {
return;
}
match value {
Value::Str(text) => {
let trimmed = super::text::trim(text);
if !trimmed.is_empty() {
out.push(trimmed.to_string());
}
}
Value::Seq(items) | Value::Map(items) => {
for item in items {
walk(item, out, depth + 1);
}
}
Value::Other => {}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn s(text: &str) -> Value {
Value::Str(text.to_string())
}
#[test]
fn a_string_leaf_is_collected() {
assert_eq!(collect(&s("hello")), ["hello"]);
}
#[test]
fn a_typed_leaf_is_dropped() {
assert!(collect(&Value::Other).is_empty());
}
#[test]
fn values_are_trimmed_and_empty_ones_dropped() {
assert_eq!(collect(&s(" padded ")), ["padded"]);
assert!(collect(&s(" ")).is_empty());
assert!(collect(&s("")).is_empty());
}
#[test]
fn nesting_is_walked_in_document_order() {
let document = Value::Map(vec![
s("first"),
Value::Seq(vec![s("second"), Value::Other, s("third")]),
Value::Map(vec![s("fourth")]),
]);
assert_eq!(collect(&document), ["first", "second", "third", "fourth"]);
}
#[test]
fn repeats_are_kept() {
let document = Value::Seq(vec![s("same"), s("same")]);
assert_eq!(collect(&document), ["same", "same"]);
}
#[test]
fn recursion_stops_at_the_cap() {
let mut deep = s("bottom");
for _ in 0..(MAX_RECURSION_DEPTH + 10) {
deep = Value::Seq(vec![deep]);
}
assert!(collect(&deep).is_empty());
}
#[test]
fn just_inside_the_cap_still_answers() {
let mut deep = s("bottom");
for _ in 0..(MAX_RECURSION_DEPTH - 1) {
deep = Value::Seq(vec![deep]);
}
assert_eq!(collect(&deep), ["bottom"]);
}
}