pub(crate) fn extract(text: &str) -> Vec<String> {
text.lines().filter_map(value_of).collect()
}
fn value_of(raw_line: &str) -> Option<String> {
let line = raw_line.trim();
if line.is_empty() || line.starts_with('#') {
return None;
}
let content = line.strip_prefix("export ").map_or(line, str::trim);
let (_, raw_value) = content.split_once('=')?;
let raw_value = raw_value.trim();
if raw_value.is_empty() {
return None;
}
let value = unquote(strip_inline_comment(raw_value));
(!value.is_empty()).then(|| value.to_string())
}
fn strip_inline_comment(value: &str) -> &str {
if value.starts_with('"') || value.starts_with('\'') {
return value;
}
match value.split_once('#') {
Some((before, _)) => before.trim(),
None => value,
}
}
fn unquote(value: &str) -> &str {
for quote in ['"', '\''] {
if value.len() > 1 && value.starts_with(quote) && value.ends_with(quote) {
return &value[1..value.len() - 1];
}
}
value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_plain_assignment_yields_its_value() {
assert_eq!(extract("NAME=value"), ["value"]);
}
#[test]
fn keys_are_never_extracted() {
assert_eq!(extract("SECRET_KEY=abc"), ["abc"]);
}
#[test]
fn comments_and_blank_lines_are_skipped() {
assert_eq!(extract("# a comment\n\nA=one\n \nB=two"), ["one", "two"]);
}
#[test]
fn an_export_prefix_is_removed() {
assert_eq!(extract("export NAME=value"), ["value"]);
}
#[test]
fn quotes_are_removed() {
assert_eq!(extract("A=\"double\"\nB='single'"), ["double", "single"]);
}
#[test]
fn an_inline_comment_ends_an_unquoted_value_only() {
assert_eq!(extract("A=value # trailing"), ["value"]);
assert_eq!(extract("B=\"value # kept\""), ["value # kept"]);
}
#[test]
fn a_line_without_an_equals_is_not_an_assignment() {
assert!(extract("JUST_A_WORD").is_empty());
}
#[test]
fn an_empty_value_is_dropped() {
assert!(extract("EMPTY=").is_empty());
assert!(extract("SPACES= ").is_empty());
}
#[test]
fn a_numeric_looking_value_is_a_string_here() {
assert_eq!(extract("PORT=8080"), ["8080"]);
}
#[test]
fn a_value_may_contain_an_equals_sign() {
assert_eq!(extract("TOKEN=a=b=c"), ["a=b=c"]);
}
#[test]
fn carriage_returns_do_not_end_up_in_values() {
assert_eq!(extract("A=value\r\nB=other\r\n"), ["value", "other"]);
}
}