aws_unlock/
line_parser.rs1use 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}