use super::{BlockKey, block_end, block_key, entries, join};
pub fn entry_names(text: &str, block: &str) -> Vec<String> {
let lines: Vec<&str> = text.lines().collect();
let BlockKey::At(at) = block_key(&lines, block) else {
return Vec::new();
};
entries(&lines, at)
.into_iter()
.map(|(name, _)| name)
.collect()
}
pub(super) fn entry_line(lines: &[&str], at: usize, entry: &str) -> Option<usize> {
entries(lines, at)
.into_iter()
.find(|(name, _)| name == entry)
.map(|(_, i)| i)
}
pub(super) fn entry_span(lines: &[&str], at: usize, entry: &str) -> Option<(usize, usize)> {
let found = entries(lines, at);
let from = found.iter().find(|(name, _)| name == entry)?.1;
let to = found
.iter()
.map(|(_, i)| *i)
.find(|i| *i > from)
.unwrap_or_else(|| block_end(lines, from));
Some((from, to))
}
pub fn set_entry(
text: &str,
block: &str,
entry: &str,
fields: &[(&str, String)],
) -> Option<String> {
let lines: Vec<&str> = text.lines().collect();
let mut out: Vec<String> = lines.iter().map(|l| (*l).to_string()).collect();
let body = std::iter::once(format!(" {entry}:"))
.chain(fields.iter().map(|(k, v)| format!(" {k}: {v}")));
match block_key(&lines, block) {
BlockKey::Inline => return None,
BlockKey::Absent => {
out.retain(|line| !line.trim().is_empty());
out.push(format!("{block}:"));
out.extend(body);
}
BlockKey::At(at) => {
let span = entry_span(&lines, at, entry);
if let Some((from, to)) = span {
out.splice(from..to, body);
} else {
out.splice((at + 1)..=at, body);
}
}
}
Some(join(&out))
}
pub fn remove_entry(text: &str, block: &str, entry: &str) -> String {
let lines: Vec<&str> = text.lines().collect();
let mut out: Vec<String> = lines.iter().map(|l| (*l).to_string()).collect();
if let BlockKey::At(at) = block_key(&lines, block)
&& let Some((from, to)) = entry_span(&lines, at, entry)
{
out.drain(from..to);
}
join(&out)
}