aws_unlock/
line_parser.rs

1use anyhow::{bail, Ok, Result};
2use std::{collections::HashMap, iter::from_fn};
3
4use crate::line_lexer::EntryLine;
5
6#[derive(Debug, Clone)]
7pub struct EntryLineParser<'a> {
8    lines: Vec<EntryLine<'a>>,
9    index: usize,
10}
11
12#[derive(Debug, Clone)]
13pub struct Entry {
14    pub comments: Vec<String>,
15    pub is_production: bool,
16    pub is_locked: bool,
17    pub header: String,
18    pub values: HashMap<String, String>,
19}
20
21impl<'a> EntryLineParser<'a> {
22    pub fn new(lines: Vec<EntryLine<'a>>) -> Self {
23        Self { lines, index: 0 }
24    }
25
26    pub fn parse(&mut self) -> Result<Vec<Entry>> {
27        from_fn(|| self.parse_one().transpose()).collect()
28    }
29
30    pub fn parse_one(&mut self) -> Result<Option<Entry>> {
31        self.skip_empty_line();
32        if self.is_finished() {
33            return Ok(None);
34        }
35
36        let mut all_comments = vec![];
37        let (comments, is_production) = self.parse_is_production()?;
38        all_comments.extend(comments);
39
40        let (comments, is_locked) = self.parse_is_locked()?;
41        all_comments.extend(comments);
42
43        let (comments, header) = self.parse_header(is_locked)?;
44        all_comments.extend(comments);
45
46        let (comments, values) = self.parse_values(is_locked)?;
47        all_comments.extend(comments);
48
49        Ok(Some(Entry {
50            comments: all_comments,
51            is_production,
52            is_locked,
53            header,
54            values,
55        }))
56    }
57
58    fn parse_is_production(&mut self) -> Result<(Vec<String>, bool)> {
59        let mut comments = vec![];
60        while let Some(line) = self.peek_line() {
61            match line {
62                EntryLine::Empty => {
63                    self.next_line().unwrap();
64                    continue;
65                }
66                EntryLine::Comment(comment) => {
67                    let comment = comment.to_string();
68                    self.next_line().unwrap();
69                    comments.push(comment);
70                    continue;
71                }
72                EntryLine::ProductionMarker => {
73                    self.next_line().unwrap();
74                    return Ok((comments, true));
75                }
76                _ => return Ok((comments, false)),
77            }
78        }
79
80        bail!("unexpected EOF while scanning is_production");
81    }
82
83    fn parse_is_locked(&mut self) -> Result<(Vec<String>, bool)> {
84        let mut comments = vec![];
85        while let Some(line) = self.peek_line() {
86            match line {
87                EntryLine::Empty => {
88                    self.next_line().unwrap();
89                    continue;
90                }
91                EntryLine::Comment(comment) => {
92                    let comment = comment.to_string();
93                    self.next_line().unwrap();
94                    comments.push(comment);
95                    continue;
96                }
97                EntryLine::ProductionMarker => bail!("unexpected production marker"),
98                EntryLine::LockedHeader(_) | EntryLine::LockedOption(_, _) => {
99                    return Ok((comments, true))
100                }
101                EntryLine::Header(_) | EntryLine::Option(_, _) => return Ok((comments, false)),
102            }
103        }
104
105        bail!("unexpected EOF while scanning is_locked");
106    }
107
108    fn parse_header(&mut self, is_locked: bool) -> Result<(Vec<String>, String)> {
109        let mut comments = vec![];
110        while let Some(line) = self.peek_line() {
111            match line {
112                EntryLine::Empty => {
113                    self.next_line().unwrap();
114                    continue;
115                }
116                EntryLine::Comment(comment) => {
117                    let comment = comment.to_string();
118                    self.next_line().unwrap();
119                    comments.push(comment);
120                    continue;
121                }
122                EntryLine::ProductionMarker => {
123                    bail!("unexpected production marker while parsing header")
124                }
125                EntryLine::Header(header) if !is_locked => {
126                    let header = header.to_string();
127                    self.next_line().unwrap();
128                    return Ok((comments, header));
129                }
130                EntryLine::LockedHeader(header) if is_locked => {
131                    let header = header.to_string();
132                    self.next_line().unwrap();
133                    return Ok((comments, header));
134                }
135                _ => bail!("unexpected line while scanning header"),
136            }
137        }
138
139        bail!("unexpected EOF while scanning header");
140    }
141
142    fn parse_values(&mut self, is_locked: bool) -> Result<(Vec<String>, HashMap<String, String>)> {
143        let mut values = HashMap::new();
144        while let Some(line) = self.peek_line() {
145            match line {
146                EntryLine::Empty => {
147                    self.next_line().unwrap();
148                    continue;
149                }
150                EntryLine::Option(key, value) if !is_locked => {
151                    let key = key.to_string();
152                    let value = value.to_string();
153                    self.next_line().unwrap();
154                    values.insert(key, value);
155                }
156                EntryLine::LockedOption(key, value) => {
157                    let key = key.to_string();
158                    let value = value.to_string();
159                    self.next_line().unwrap();
160                    values.insert(key, value);
161                }
162                EntryLine::ProductionMarker
163                | EntryLine::Comment(_)
164                | EntryLine::Header(_)
165                | EntryLine::LockedHeader(_) => return Ok((vec![], values)),
166                _ => bail!("unexpected line while scanning values"),
167            }
168        }
169
170        Ok((vec![], values))
171    }
172
173    fn skip_empty_line(&mut self) {
174        while self.peek_line() == Some(&EntryLine::Empty) {
175            self.next_line().unwrap();
176        }
177    }
178
179    fn is_finished(&self) -> bool {
180        self.index == self.lines.len()
181    }
182
183    fn peek_line(&self) -> Option<&EntryLine> {
184        self.lines.get(self.index)
185    }
186
187    fn next_line(&mut self) -> Option<&EntryLine> {
188        let res = self.lines.get(self.index);
189        if self.index < self.lines.len() {
190            self.index += 1;
191        }
192        res
193    }
194}