keyhog_scanner/structured/parsers/
env.rs1use super::ExtractedPair;
2
3pub fn parse_env(text: &str) -> Vec<ExtractedPair> {
16 let mut pairs = Vec::new();
17 for (line_idx, line) in text.lines().enumerate() {
18 let trimmed = line.trim();
19 if trimmed.is_empty() || trimmed.starts_with('#') {
20 continue;
21 }
22 let after_export = trimmed.strip_prefix("export ").unwrap_or(trimmed);
23 if let Some((key, value)) = after_export.split_once('=') {
24 let key = key.trim();
25 let value = value.trim();
26 if key.is_empty() {
27 continue;
28 }
29 let unquoted = unquote_env_value(value);
30 pairs.push(ExtractedPair {
31 context: key.to_string(),
32 value: unquoted,
33 line: line_idx + 1,
34 });
35 }
36 }
37 pairs
38}
39
40fn unquote_env_value(s: &str) -> String {
44 if s.len() >= 2 {
45 let first = s.as_bytes()[0];
46 let last = s.as_bytes()[s.len() - 1];
47 if matches!(first, b'"' | b'\'' | b'`') && first == last {
48 return s[1..s.len() - 1].to_string();
49 }
50 }
51 if let Some(hash_idx) = find_inline_comment(s) {
52 return s[..hash_idx].trim_end().to_string();
53 }
54 s.to_string()
55}
56
57fn find_inline_comment(s: &str) -> Option<usize> {
60 let bytes = s.as_bytes();
61 bytes
62 .windows(2)
63 .position(|w| w[0].is_ascii_whitespace() && w[1] == b'#')
64 .map(|i| i + 1)
65}