1use crate::parse::*;
2use std::collections::HashMap;
3
4pub struct Dotenv {
5 buf: String,
6}
7
8impl Dotenv {
9 pub(crate) fn new(buf: String) -> Self {
10 Self { buf }
11 }
12
13 pub fn iter(&self) -> Iter {
14 Iter::new(&self.buf)
15 }
16
17 pub fn load(self) {
18 self.set_vars(false)
19 }
20
21 pub fn load_override(self) {
22 self.set_vars(true)
23 }
24
25 fn set_vars(self, override_env: bool) {
26 for (key, value) in self.iter() {
27 if override_env || std::env::var(key).is_err() {
28 std::env::set_var(key, value);
29 }
30 }
31 }
32}
33
34pub struct Iter<'a> {
35 resolved: HashMap<&'a str, String>,
36 input: &'a str,
37}
38
39impl<'a> Iter<'a> {
40 pub(crate) fn new(input: &'a str) -> Self {
41 Self {
42 resolved: HashMap::new(),
43 input: strip_bom(input),
44 }
45 }
46
47 fn resolve_var(&self, name: &'a str) -> Option<String> {
48 std::env::var(name)
49 .ok()
50 .or_else(|| self.resolved.get(name).cloned())
51 }
52
53 fn resolve(&self, value: Value<'a>) -> Option<String> {
54 match value {
55 Value::Lit(text) => Some(text.to_string()),
56 Value::Var(name, default) => self
57 .resolve_var(name)
58 .or_else(|| default.and_then(|it| self.resolve(*it))),
59 Value::List(list) => Some(list.into_iter().flat_map(|it| self.resolve(it)).collect()),
60 }
61 }
62}
63
64impl<'a> Iterator for Iter<'a> {
65 type Item = (&'a str, String);
66
67 fn next(&mut self) -> Option<Self::Item> {
68 while let Ok((rest, maybe)) = parse(self.input) {
69 self.input = rest; if let Some((key, value)) = maybe {
72 if let Some(value) = self.resolve(value) {
73 self.resolved.insert(key, value.clone());
74 return Some((key, value));
75 }
76 }
77
78 if rest.is_empty() {
79 break;
80 }
81 }
82
83 None
84 }
85}
86
87fn strip_bom(input: &str) -> &str {
88 input.strip_prefix('\u{FEFF}').unwrap_or(input)
90}