aboutsummaryrefslogtreecommitdiff
path: root/src/config/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/config/parse.rs')
-rw-r--r--src/config/parse.rs254
1 files changed, 0 insertions, 254 deletions
diff --git a/src/config/parse.rs b/src/config/parse.rs
deleted file mode 100644
index f1e33b0..0000000
--- a/src/config/parse.rs
+++ /dev/null
@@ -1,254 +0,0 @@
1use nom::{
2 branch::alt,
3 bytes::complete::{tag, take_while, take_while1},
4 character::complete::{alphanumeric0, alphanumeric1, multispace0, space1},
5 combinator::map,
6 multi::{many0, separated_list0},
7 sequence::{delimited, preceded},
8};
9
10type Span<'s> = nom_locate::LocatedSpan<&'s str>;
11type IResult<'s, I, O, E = ParserError<'s>> = nom::IResult<I, O, E>;
12
13#[derive(Debug, PartialEq, Eq)]
14struct ParserError<'s> {
15 location: Span<'s>,
16 message: Option<String>,
17}
18
19#[derive(Debug)]
20struct KeyValueParser<'s> {
21 span: Span<'s>,
22 kvs: Vec<KeyValue<'s>>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26struct KeyValue<'s> {
27 key: &'s str,
28 value: &'s str,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32struct LinkAction {
33 source: String,
34 target: String,
35}
36
37enum RichAction {
38 Link(LinkAction),
39}
40
41struct RichGroup {
42 name: String,
43 items: Vec<RichItem>,
44}
45
46enum RichItem {
47 Group(RichGroup),
48}
49
50struct RichConfig {}
51
52impl<'s> ParserError<'s> {
53 fn custom(location: Span<'s>, message: impl Into<String>) -> Self {
54 Self {
55 location,
56 message: Some(message.into()),
57 }
58 }
59
60 fn missing_key(span: Span<'s>, key: &'s str) -> Self {
61 Self::custom(span, format!("missing key: {key}"))
62 }
63}
64
65impl<'s> From<ParserError<'s>> for nom::Err<ParserError<'s>> {
66 fn from(e: ParserError<'s>) -> Self {
67 Self::Failure(e)
68 }
69}
70
71impl<'s> nom::error::ParseError<Span<'s>> for ParserError<'s> {
72 fn from_error_kind(input: Span<'s>, kind: nom::error::ErrorKind) -> Self {
73 Self::custom(input, format!("error kind: {kind:?}"))
74 }
75
76 fn append(input: Span, kind: nom::error::ErrorKind, other: Self) -> Self {
77 todo!()
78 }
79
80 fn or(self, other: Self) -> Self {
81 other
82 }
83
84 fn from_char(input: Span<'s>, c: char) -> Self {
85 Self::custom(input, format!("invalid character: {c}"))
86 }
87}
88
89impl<'s> KeyValueParser<'s> {
90 fn new(span: Span<'s>, kvs: Vec<KeyValue<'s>>) -> Self {
91 Self { span, kvs }
92 }
93
94 fn get(&self, key: &'static str) -> Option<&'s str> {
95 self.kvs.iter().find(|kv| kv.key == key).map(|kv| kv.value)
96 }
97
98 fn expect(&self, key: &'static str) -> Result<&'s str, ParserError<'s>> {
99 self.get(key)
100 .ok_or(ParserError::missing_key(self.span, key))
101 }
102}
103
104fn is_value_char(c: char) -> bool {
105 c.is_alphanumeric() || c == '_' || c == '-' || c == '.' || c == '/'
106}
107
108fn whitespace0(i: Span) -> IResult<Span, Span> {
109 take_while(char::is_whitespace)(i)
110}
111
112fn whitespace1(i: Span) -> IResult<Span, Span> {
113 take_while1(char::is_whitespace)(i)
114}
115
116fn linesep(i: Span) -> IResult<Span, Span> {
117 take_while(|c: char| c.is_whitespace() && c != '\n')(i)?;
118 take_while(|c: char| c == '\n')(i)?;
119 take_while(char::is_whitespace)(i)
120}
121
122fn keyvalue(i: Span) -> IResult<Span, KeyValue> {
123 let (i, key) = alphanumeric1(i)?;
124 let (i, _) = tag("=")(i)?;
125 let (i, val) = delimited(tag("\""), take_while(is_value_char), tag("\""))(i)?;
126 Ok((
127 i,
128 KeyValue {
129 key: key.fragment(),
130 value: val.fragment(),
131 },
132 ))
133}
134
135fn keyvalues(i: Span) -> IResult<Span, Vec<KeyValue>> {
136 separated_list0(space1, keyvalue)(i)
137}
138
139fn link_action(i: Span) -> IResult<Span, LinkAction> {
140 let (i, kvs) = preceded(tag("link"), preceded(space1, keyvalues))(i)?;
141 eprintln!("{kvs:#?}");
142 eprintln!("{i:?}");
143 let kvparser = KeyValueParser::new(i, kvs);
144 let src = kvparser.expect("src")?.to_string();
145 let dst = kvparser.expect("dst")?.to_string();
146 Ok((
147 i,
148 LinkAction {
149 source: src,
150 target: dst,
151 },
152 ))
153}
154
155fn rich_action(i: Span) -> IResult<Span, RichAction> {
156 todo!()
157}
158
159fn rich_group(i: Span) -> IResult<Span, RichGroup> {
160 let mut header = preceded(tag("group"), preceded(multispace0, alphanumeric1));
161 let mut open_bracket = delimited(multispace0, tag("{"), multispace0);
162 let mut close_bracket = preceded(multispace0, tag("}"));
163 let mut body = separated_list0(linesep, rich_item);
164
165 let (i, name) = header(i)?;
166 let (i, _) = open_bracket(i)?;
167 let (i, items) = body(i)?;
168 let (i, _) = close_bracket(i)?;
169
170 Ok((
171 i,
172 RichGroup {
173 name: name.to_string(),
174 items,
175 },
176 ))
177}
178
179fn rich_item(i: Span) -> IResult<Span, RichItem> {
180 alt((map(rich_group, RichItem::Group),))(i)
181}
182
183fn config(i: Span) -> IResult<Span, RichConfig> {
184 let (_, groups) = many0(rich_group)(i)?;
185 todo!()
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn parse_keyvalue() {
194 let input = Span::new(r#"key="value""#);
195 let (rem, kv) = keyvalue(input).unwrap();
196 assert!(rem.is_empty());
197 assert_eq!(
198 kv,
199 KeyValue {
200 key: "key",
201 value: "value"
202 }
203 );
204 }
205
206 #[test]
207 fn parse_keyvalues() {
208 let kvs = vec![
209 KeyValue {
210 key: "key1",
211 value: "value1",
212 },
213 KeyValue {
214 key: "key2",
215 value: "value2",
216 },
217 ];
218
219 let input = Span::new(r#"key1="value1" key2="value2""#);
220 let (rem, res) = keyvalues(input).unwrap();
221 assert!(rem.is_empty());
222 assert_eq!(res, kvs);
223
224 let kvs = vec![
225 KeyValue {
226 key: "src",
227 value: "tmux/",
228 },
229 KeyValue {
230 key: "dst",
231 value: ".config/tmux",
232 },
233 ];
234
235 let input = Span::new(r#"src="tmux/" dst=".config/tmux""#);
236 let (rem, res) = keyvalues(input).unwrap();
237 assert!(rem.is_empty());
238 assert_eq!(res, kvs);
239 }
240
241 #[test]
242 fn parse_link_action() {
243 let input = Span::new(r#"link src="tmux/" dst=".config/tmux""#);
244 let (rem, res) = link_action(input).unwrap();
245 assert!(rem.is_empty());
246 assert_eq!(
247 res,
248 LinkAction {
249 source: "tmux/".to_string(),
250 target: ".config/tmux".to_string()
251 }
252 );
253 }
254}