/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dotenvy-0.15.7/src/iter.rs
Line | Count | Source |
1 | | use std::collections::HashMap; |
2 | | use std::env; |
3 | | use std::io::prelude::*; |
4 | | use std::io::BufReader; |
5 | | |
6 | | use crate::errors::*; |
7 | | use crate::parse; |
8 | | |
9 | | pub struct Iter<R> { |
10 | | lines: QuotedLines<BufReader<R>>, |
11 | | substitution_data: HashMap<String, Option<String>>, |
12 | | } |
13 | | |
14 | | impl<R: Read> Iter<R> { |
15 | 0 | pub fn new(reader: R) -> Iter<R> { |
16 | 0 | Iter { |
17 | 0 | lines: QuotedLines { |
18 | 0 | buf: BufReader::new(reader), |
19 | 0 | }, |
20 | 0 | substitution_data: HashMap::new(), |
21 | 0 | } |
22 | 0 | } |
23 | | |
24 | | /// Loads all variables found in the `reader` into the environment, |
25 | | /// preserving any existing environment variables of the same name. |
26 | | /// |
27 | | /// If a variable is specified multiple times within the reader's data, |
28 | | /// then the first occurrence is applied. |
29 | 0 | pub fn load(mut self) -> Result<()> { |
30 | 0 | self.remove_bom()?; |
31 | | |
32 | 0 | for item in self { |
33 | 0 | let (key, value) = item?; |
34 | 0 | if env::var(&key).is_err() { |
35 | 0 | env::set_var(&key, value); |
36 | 0 | } |
37 | | } |
38 | | |
39 | 0 | Ok(()) |
40 | 0 | } |
41 | | |
42 | | /// Loads all variables found in the `reader` into the environment, |
43 | | /// overriding any existing environment variables of the same name. |
44 | | /// |
45 | | /// If a variable is specified multiple times within the reader's data, |
46 | | /// then the last occurrence is applied. |
47 | 0 | pub fn load_override(mut self) -> Result<()> { |
48 | 0 | self.remove_bom()?; |
49 | | |
50 | 0 | for item in self { |
51 | 0 | let (key, value) = item?; |
52 | 0 | env::set_var(key, value); |
53 | | } |
54 | | |
55 | 0 | Ok(()) |
56 | 0 | } |
57 | | |
58 | 0 | fn remove_bom(&mut self) -> Result<()> { |
59 | 0 | let buffer = self.lines.buf.fill_buf().map_err(Error::Io)?; |
60 | | // https://www.compart.com/en/unicode/U+FEFF |
61 | 0 | if buffer.starts_with(&[0xEF, 0xBB, 0xBF]) { |
62 | 0 | // remove the BOM from the bufreader |
63 | 0 | self.lines.buf.consume(3); |
64 | 0 | } |
65 | 0 | Ok(()) |
66 | 0 | } |
67 | | } |
68 | | |
69 | | struct QuotedLines<B> { |
70 | | buf: B, |
71 | | } |
72 | | |
73 | | enum ParseState { |
74 | | Complete, |
75 | | Escape, |
76 | | StrongOpen, |
77 | | StrongOpenEscape, |
78 | | WeakOpen, |
79 | | WeakOpenEscape, |
80 | | Comment, |
81 | | WhiteSpace, |
82 | | } |
83 | | |
84 | 0 | fn eval_end_state(prev_state: ParseState, buf: &str) -> (usize, ParseState) { |
85 | 0 | let mut cur_state = prev_state; |
86 | 0 | let mut cur_pos: usize = 0; |
87 | | |
88 | 0 | for (pos, c) in buf.char_indices() { |
89 | 0 | cur_pos = pos; |
90 | 0 | cur_state = match cur_state { |
91 | 0 | ParseState::WhiteSpace => match c { |
92 | 0 | '#' => return (cur_pos, ParseState::Comment), |
93 | 0 | '\\' => ParseState::Escape, |
94 | 0 | '"' => ParseState::WeakOpen, |
95 | 0 | '\'' => ParseState::StrongOpen, |
96 | 0 | _ => ParseState::Complete, |
97 | | }, |
98 | 0 | ParseState::Escape => ParseState::Complete, |
99 | 0 | ParseState::Complete => match c { |
100 | 0 | c if c.is_whitespace() && c != '\n' && c != '\r' => ParseState::WhiteSpace, |
101 | 0 | '\\' => ParseState::Escape, |
102 | 0 | '"' => ParseState::WeakOpen, |
103 | 0 | '\'' => ParseState::StrongOpen, |
104 | 0 | _ => ParseState::Complete, |
105 | | }, |
106 | 0 | ParseState::WeakOpen => match c { |
107 | 0 | '\\' => ParseState::WeakOpenEscape, |
108 | 0 | '"' => ParseState::Complete, |
109 | 0 | _ => ParseState::WeakOpen, |
110 | | }, |
111 | 0 | ParseState::WeakOpenEscape => ParseState::WeakOpen, |
112 | 0 | ParseState::StrongOpen => match c { |
113 | 0 | '\\' => ParseState::StrongOpenEscape, |
114 | 0 | '\'' => ParseState::Complete, |
115 | 0 | _ => ParseState::StrongOpen, |
116 | | }, |
117 | 0 | ParseState::StrongOpenEscape => ParseState::StrongOpen, |
118 | | // Comments last the entire line. |
119 | 0 | ParseState::Comment => panic!("should have returned early"), |
120 | | }; |
121 | | } |
122 | 0 | (cur_pos, cur_state) |
123 | 0 | } |
124 | | |
125 | | impl<B: BufRead> Iterator for QuotedLines<B> { |
126 | | type Item = Result<String>; |
127 | | |
128 | 0 | fn next(&mut self) -> Option<Result<String>> { |
129 | 0 | let mut buf = String::new(); |
130 | 0 | let mut cur_state = ParseState::Complete; |
131 | | let mut buf_pos; |
132 | | let mut cur_pos; |
133 | | loop { |
134 | 0 | buf_pos = buf.len(); |
135 | 0 | match self.buf.read_line(&mut buf) { |
136 | 0 | Ok(0) => match cur_state { |
137 | 0 | ParseState::Complete => return None, |
138 | | _ => { |
139 | 0 | let len = buf.len(); |
140 | 0 | return Some(Err(Error::LineParse(buf, len))); |
141 | | } |
142 | | }, |
143 | 0 | Ok(_n) => { |
144 | | // Skip lines which start with a # before iteration |
145 | | // This optimizes parsing a bit. |
146 | 0 | if buf.trim_start().starts_with('#') { |
147 | 0 | return Some(Ok(String::with_capacity(0))); |
148 | 0 | } |
149 | 0 | let result = eval_end_state(cur_state, &buf[buf_pos..]); |
150 | 0 | cur_pos = result.0; |
151 | 0 | cur_state = result.1; |
152 | | |
153 | 0 | match cur_state { |
154 | | ParseState::Complete => { |
155 | 0 | if buf.ends_with('\n') { |
156 | 0 | buf.pop(); |
157 | 0 | if buf.ends_with('\r') { |
158 | 0 | buf.pop(); |
159 | 0 | } |
160 | 0 | } |
161 | 0 | return Some(Ok(buf)); |
162 | | } |
163 | | ParseState::Escape |
164 | | | ParseState::StrongOpen |
165 | | | ParseState::StrongOpenEscape |
166 | | | ParseState::WeakOpen |
167 | | | ParseState::WeakOpenEscape |
168 | 0 | | ParseState::WhiteSpace => {} |
169 | | ParseState::Comment => { |
170 | 0 | buf.truncate(buf_pos + cur_pos); |
171 | 0 | return Some(Ok(buf)); |
172 | | } |
173 | | } |
174 | | } |
175 | 0 | Err(e) => return Some(Err(Error::Io(e))), |
176 | | } |
177 | | } |
178 | 0 | } |
179 | | } |
180 | | |
181 | | impl<R: Read> Iterator for Iter<R> { |
182 | | type Item = Result<(String, String)>; |
183 | | |
184 | 0 | fn next(&mut self) -> Option<Self::Item> { |
185 | | loop { |
186 | 0 | let line = match self.lines.next() { |
187 | 0 | Some(Ok(line)) => line, |
188 | 0 | Some(Err(err)) => return Some(Err(err)), |
189 | 0 | None => return None, |
190 | | }; |
191 | | |
192 | 0 | match parse::parse_line(&line, &mut self.substitution_data) { |
193 | 0 | Ok(Some(result)) => return Some(Ok(result)), |
194 | 0 | Ok(None) => {} |
195 | 0 | Err(err) => return Some(Err(err)), |
196 | | } |
197 | | } |
198 | 0 | } |
199 | | } |