1use std::collections::HashMap;
2
3use crate::parse::{findattr, getcfgbase, getkey};
4use thiserror::Error;
5use rnix::{self, SyntaxKind, SyntaxNode};
6
7#[derive(Error, Debug)]
8pub enum WriteError {
9 #[error("Error while parsing.")]
10 ParseError,
11 #[error("No attributes.")]
12 NoAttr,
13 #[error("Error with array.")]
14 ArrayError,
15 #[error("Writing value to attribute set.")]
16 WriteValueToSet,
17}
18
19pub fn write(f: &str, query: &str, val: &str) -> Result<String, WriteError> {
20 let ast = rnix::Root::parse(f);
21 let configbase = match getcfgbase(&ast.syntax()) {
22 Some(x) => x,
23 None => {
24 return Err(WriteError::ParseError);
25 }
26 };
27 if val.trim_start().starts_with('{') && val.trim_end().ends_with('}'){
28 if let Some(x) = getcfgbase(&rnix::Root::parse(val).syntax()) {
29 if x.kind() == SyntaxKind::NODE_ATTR_SET {
30 return addattrval(f, &configbase, query, &x);
31 }
32 }
33 }
34
35 let outnode = match findattr(&configbase, query) {
36 Some(x) => {
37 if let Some(n) = x.children().last() {
38 if n.kind() == SyntaxKind::NODE_ATTR_SET {
39 return Err(WriteError::WriteValueToSet);
40 }
41 }
42 modvalue(&x, val).unwrap()
43 }
44 None => {
45 let mut y = query.split('.').collect::<Vec<_>>();
46 y.pop();
47 let x = findattrset(&configbase, &y.join("."), 0);
48 match x {
49 Some((base, v, spaces)) => addvalue(
50 &base,
51 &format!("{}{}", " ".repeat(spaces), &query[v.len() + 1..]),
52 val,
53 ),
54 None => addvalue(&configbase, query, val),
55 }
56 }
57 };
58 Ok(outnode.to_string())
59}
60
61fn addvalue(configbase: &SyntaxNode, query: &str, val: &str) -> SyntaxNode {
62 let mut index = configbase.green().children().len() - 2;
63 if let Some(x) = matchval(configbase, query, query.split('.').count()) {
65 let i = configbase
66 .green()
67 .children()
68 .position(|y| match y.into_node() {
69 Some(y) => y.to_owned() == x.green().into_owned(),
70 None => false,
71 })
72 .unwrap();
73 let configgreen = configbase.green().to_owned();
74 let configafter = &configgreen.children().collect::<Vec<_>>()[i..];
75 for child in configafter {
76 if let Some(x) = child.as_token() {
77 if x.text().contains('\n') {
78 let cas = configafter.to_vec();
79 index = i + cas
80 .iter()
81 .position(|y| match y.as_token() {
82 Some(t) => t == x,
83 None => false,
84 })
85 .unwrap();
86 break;
87 }
88 }
89 }
90 }
91 let input = rnix::Root::parse(format!("\n {} = {};", &query, &val).as_str())
92 .syntax();
93 let input = input
94 .green()
95 .to_owned();
96 if index == 0 {
97 index += 1;
98 };
99 let new = configbase
100 .green()
101 .insert_child(index, rnix::NodeOrToken::Node(input.into_owned()));
102 let replace = configbase.replace_with(new);
103 rnix::Root::parse(&replace.to_string()).syntax()
104}
105
106fn findattrset(
108 configbase: &SyntaxNode,
109 name: &str,
110 spaces: usize,
111) -> Option<(SyntaxNode, String, usize)> {
112 for child in configbase.children() {
113 if child.kind() == SyntaxKind::NODE_ATTRPATH_VALUE {
114 for subchild in child.children() {
116 if subchild.kind() == SyntaxKind::NODE_ATTRPATH {
117 let key = getkey(&subchild);
119 let qkey = name
120 .split('.')
121 .map(|s| s.to_string())
122 .collect::<Vec<String>>();
123 if qkey == key {
124 for possibleset in child.children() {
126 if possibleset.kind() == SyntaxKind::NODE_ATTR_SET {
127 return Some((possibleset, name.to_string(), spaces + 2));
128 }
129 }
130 return None;
131 } else if qkey.len() > key.len() {
132 if key == qkey[0..key.len()] {
134 let subkey = &qkey[key.len()..].join(".").to_string();
136 let newbase = getcfgbase(&child).unwrap();
137 let subattr = findattrset(&newbase, subkey, spaces + 2);
138 if let Some((node, _, spaces)) = subattr {
139 return Some((node, name.to_string(), spaces));
140 }
141 }
142 }
143 }
144 }
145 }
146 }
147 None
148}
149
150fn matchval(configbase: &SyntaxNode, query: &str, acc: usize) -> Option<SyntaxNode> {
151 let qvec = &query
152 .split('.')
153 .map(|s| s.to_string())
154 .collect::<Vec<String>>();
155 let q = &qvec[..acc];
156 for child in configbase.children() {
157 if child.kind() == SyntaxKind::NODE_ATTRPATH_VALUE {
158 for subchild in child.children() {
159 if subchild.kind() == SyntaxKind::NODE_ATTRPATH {
160 let key = getkey(&subchild);
161 if key.len() >= q.len() && &key[..q.len()] == q {
162 return Some(child);
163 }
164 }
165 }
166 }
167 }
168 if acc == 1 {
169 None
170 } else {
171 matchval(configbase, query, acc - 1)
172 }
173}
174
175fn modvalue(node: &SyntaxNode, val: &str) -> Option<SyntaxNode> {
176 for child in node.children() {
178 if child.kind() != SyntaxKind::NODE_ATTRPATH {
179 let c = &child;
180 let input = val.to_string();
181 let rep = &rnix::Root::parse(&input)
182 .syntax()
183 .children()
184 .collect::<Vec<SyntaxNode>>()[0];
185 let index = node
186 .green()
187 .children()
188 .position(|y| match y.into_node() {
189 Some(y) => y.to_owned() == c.green().into_owned(),
190 None => false,
191 })
192 .unwrap();
193 let replaced = node
194 .green()
195 .replace_child(index, rnix::NodeOrToken::Node(rep.green().into_owned()));
196 let out = node.replace_with(replaced);
197 let rnode = rnix::Root::parse(&out.to_string()).syntax();
198 return Some(rnode);
199 }
200 }
201 None
202}
203
204fn addattrval(
206 f: &str,
207 configbase: &SyntaxNode,
208 query: &str,
209 val: &SyntaxNode,
210) -> Result<String, WriteError> {
211 let mut attrmap = HashMap::new();
212 buildattrvec(val, vec![], &mut attrmap);
213 let mut file = f.to_string();
214
215 if attrmap.iter().any(|(key, _)| findattr(configbase, &format!("{}.{}", query, key)).is_some()) {
216 for (key, val) in attrmap {
217 match write(&file, &format!("{}.{}", query, key), &val) {
218 Ok(x) => {
219 file = x
220 },
221 Err(e) => return Err(e),
222 }
223 }
224 } else if let Some(c) = getcfgbase(&rnix::Root::parse(&file).syntax()) {
225 file = addvalue(&c, query, &val.to_string()).to_string();
226 }
227 Ok(file)
228}
229
230fn buildattrvec(val: &SyntaxNode, prefix: Vec<String>, map: &mut HashMap<String, String>) {
231 for child in val.children() {
232 if child.kind() == SyntaxKind::NODE_ATTRPATH_VALUE {
233 if let Some(subchild) = child.children().last() {
234 if subchild.kind() == SyntaxKind::NODE_ATTR_SET {
235 for c in child.children() {
236 if c.kind() == SyntaxKind::NODE_ATTRPATH {
237 let key = getkey(&c);
238 let mut newprefix = prefix.clone();
239 newprefix.append(&mut key.clone());
240 buildattrvec(&subchild, newprefix, map);
241 break;
242 }
243 }
244 } else {
245 for c in child.children() {
246 if c.kind() == SyntaxKind::NODE_ATTRPATH {
247 let key = getkey(&c);
248 let mut newprefix = prefix.clone();
249 newprefix.append(&mut key.clone());
250 map.insert(newprefix.join("."), subchild.to_string());
251 }
252 }
253 }
254 }
255 }
256 }
257}
258
259pub fn addtoarr(f: &str, query: &str, items: Vec<String>) -> Result<String, WriteError> {
260 let ast = rnix::Root::parse(f);
261 let configbase = match getcfgbase(&ast.syntax()) {
262 Some(x) => x,
263 None => return Err(WriteError::ParseError),
264 };
265 let outnode = match findattr(&configbase, query) {
266 Some(x) => match addtoarr_aux(&x, items) {
267 Some(x) => x,
268 None => return Err(WriteError::ArrayError),
269 },
270 None => {
272 let newval = addvalue(&configbase, query, "[\n ]");
273 return addtoarr(&newval.to_string(), query, items);
274 }
275 };
276 Ok(outnode.to_string())
277}
278
279fn addtoarr_aux(node: &SyntaxNode, items: Vec<String>) -> Option<SyntaxNode> {
280 for child in node.children() {
281 if child.kind() == rnix::SyntaxKind::NODE_WITH {
282 return addtoarr_aux(&child, items);
283 }
284 if child.kind() == SyntaxKind::NODE_LIST {
285 let mut green = child.green().into_owned();
286
287 for elem in items {
288 let mut i = 0;
289 for c in green.children() {
290 if c.to_string() == "]" {
291 if green.children().collect::<Vec<_>>()[i - 1]
292 .as_token()
293 .unwrap()
294 .to_string()
295 .contains('\n')
296 {
297 i -= 1;
298 }
299 green = green.insert_child(
300 i,
301 rnix::NodeOrToken::Node(
302 rnix::Root::parse(&format!("\n{}{}", " ".repeat(4), elem))
303 .syntax()
304 .green()
305 .into_owned(),
306 ),
307 );
308 break;
309 }
310 i += 1;
311 }
312 }
313
314 let index = match node.green().children().position(|x| match x.into_node() {
315 Some(x) => x.to_owned() == child.green().into_owned(),
316 None => false,
317 }) {
318 Some(x) => x,
319 None => return None,
320 };
321
322 let replace = node
323 .green()
324 .replace_child(index, rnix::NodeOrToken::Node(green));
325 let out = node.replace_with(replace);
326 let output = rnix::Root::parse(&out.to_string()).syntax();
327 return Some(output);
328 }
329 }
330 None
331}
332
333pub fn rmarr(f: &str, query: &str, items: Vec<String>) -> Result<String, WriteError> {
334 let ast = rnix::Root::parse(f);
335 let configbase = match getcfgbase(&ast.syntax()) {
336 Some(x) => x,
337 None => return Err(WriteError::ParseError),
338 };
339 let outnode = match findattr(&configbase, query) {
340 Some(x) => match rmarr_aux(&x, items) {
341 Some(x) => x,
342 None => return Err(WriteError::ArrayError),
343 },
344 None => return Err(WriteError::NoAttr),
345 };
346 Ok(outnode.to_string())
347}
348
349fn rmarr_aux(node: &SyntaxNode, items: Vec<String>) -> Option<SyntaxNode> {
350 for child in node.children() {
351 if child.kind() == rnix::SyntaxKind::NODE_WITH {
352 return rmarr_aux(&child, items);
353 }
354 if child.kind() == SyntaxKind::NODE_LIST {
355 let green = child.green().into_owned();
356 let mut idx = vec![];
357 for elem in green.children() {
358 if elem.as_node() != None && items.contains(&elem.to_string()) {
359 let index = match green.children().position(|x| match x.into_node() {
360 Some(x) => {
361 if let Some(y) = elem.as_node() {
362 x.eq(y)
363 } else {
364 false
365 }
366 }
367 None => false,
368 }) {
369 Some(x) => x,
370 None => return None,
371 };
372 idx.push(index)
373 }
374 }
375 let mut acc = 0;
376 let mut replace = green;
377
378 for i in idx {
379 replace = replace.remove_child(i - acc);
380 let mut v = vec![];
381 for c in replace.children() {
382 v.push(c);
383 }
384 if let Some(x) = v.get(i - acc - 1).unwrap().as_token() {
385 if x.to_string().contains('\n') {
386 replace = replace.remove_child(i - acc - 1);
387 acc += 1;
388 }
389 }
390 acc += 1;
391 }
392 let out = child.replace_with(replace);
393
394 let output = rnix::Root::parse(&out.to_string()).syntax();
395 return Some(output);
396 }
397 }
398 None
399}
400
401pub fn deref(f: &str, query: &str) -> Result<String, WriteError> {
402 let ast = rnix::Root::parse(f);
403 let configbase = match getcfgbase(&ast.syntax()) {
404 Some(x) => x,
405 None => return Err(WriteError::ParseError),
406 };
407 let outnode = match deref_aux(&configbase, query) {
408 Some(x) => x,
409 None => return Err(WriteError::NoAttr),
410 };
411 Ok(outnode.to_string())
412}
413
414fn deref_aux(configbase: &SyntaxNode, name: &str) -> Option<SyntaxNode> {
415 for child in configbase.children() {
416 if child.kind() == SyntaxKind::NODE_ATTRPATH_VALUE {
417 for subchild in child.children() {
419 if subchild.kind() == SyntaxKind::NODE_ATTRPATH {
420 let key = getkey(&subchild);
422 let qkey = name
423 .split('.')
424 .map(|s| s.to_string())
425 .collect::<Vec<String>>();
426 if qkey == key {
427 let index =
428 match configbase
429 .green()
430 .children()
431 .position(|x| match x.into_node() {
432 Some(x) => x.to_owned() == child.green().into_owned(),
433 None => false,
434 }) {
435 Some(x) => x,
436 None => return None,
437 };
438 let mut del = configbase.green().remove_child(index);
439
440 if del.children().collect::<Vec<_>>()[index]
442 .to_string()
443 .contains('\n')
444 {
445 del = del.remove_child(index);
446 }
447 let out = configbase.replace_with(del);
448 return Some(rnix::Root::parse(&out.to_string()).syntax());
449 } else if qkey.len() > key.len() {
450 if key == qkey[0..key.len()] {
452 let subkey = &qkey[key.len()..].join(".").to_string();
454 let newbase = getcfgbase(&child).unwrap();
455 let subattr = deref_aux(&newbase, subkey);
456 if let Some(s) = subattr {
457 return Some(s);
458 }
459 }
460 }
461 }
462 }
463 }
464 }
465 None
466}