use anyhow::Context;
pub const QUOTE_PATTERN: &[char] = &['"', '\''];
pub fn parse_key_value_list(
input: &str,
) -> anyhow::Result<Vec<(String, String)>> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Ok(Vec::new());
}
trimmed
.split('\n')
.map(|line| line.trim())
.enumerate()
.filter(|(_, line)| {
!line.is_empty()
&& !line.starts_with('#')
&& !line.starts_with("//")
})
.map(|(i, line)| {
let line = line
.split_once(" #")
.unwrap_or((line, ""))
.0
.trim()
.trim_start_matches('-')
.trim();
let (key, value) = line
.split_once(['=', ':'])
.with_context(|| {
format!(
"line {i} missing assignment character ('=' or ':')"
)
})
.map(|(key, value)| {
let key = key.trim();
let value = value.trim();
let (key, value) = if key.starts_with(QUOTE_PATTERN)
&& !key.ends_with(QUOTE_PATTERN)
&& value.ends_with(QUOTE_PATTERN)
{
(
key.strip_prefix(QUOTE_PATTERN).unwrap().trim(),
value.strip_suffix(QUOTE_PATTERN).unwrap().trim(),
)
} else {
(key, value)
};
(key.to_string(), value.to_string())
})?;
anyhow::Ok((key, value))
})
.collect::<anyhow::Result<Vec<_>>>()
}
pub fn parse_multiline_command(command: impl AsRef<str>) -> String {
command
.as_ref()
.split('\n')
.map(str::trim)
.filter(|line| !line.is_empty() && !line.starts_with('#'))
.filter_map(|line| line.split(" #").next())
.collect::<Vec<_>>()
.join("\n")
.split(" \\")
.map(str::trim)
.fold(String::new(), |acc, el| acc + " " + el)
.split('\n')
.map(str::trim)
.filter(|line| !line.is_empty() && !line.starts_with('#'))
.filter_map(|line| line.split(" #").next())
.map(str::trim)
.collect::<Vec<_>>()
.join(" && ")
}
pub fn parse_string_list(source: impl AsRef<str>) -> Vec<String> {
source
.as_ref()
.split('\n')
.map(str::trim)
.filter(|line| !line.is_empty() && !line.starts_with('#'))
.filter_map(|line| line.split(" #").next())
.flat_map(|line| line.split(','))
.map(str::trim)
.filter(|entry| !entry.is_empty())
.map(str::to_string)
.collect()
}