aboutsummaryrefslogtreecommitdiff
path: root/bin/src/fix.rs
diff options
context:
space:
mode:
Diffstat (limited to 'bin/src/fix.rs')
-rw-r--r--bin/src/fix.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/bin/src/fix.rs b/bin/src/fix.rs
new file mode 100644
index 0000000..478dbd9
--- /dev/null
+++ b/bin/src/fix.rs
@@ -0,0 +1,112 @@
1use std::borrow::Cow;
2
3use lib::{Report, LINTS};
4use rnix::{parser::ParseError as RnixParseErr, TextRange, WalkEvent};
5
6type Source<'a> = Cow<'a, str>;
7
8fn collect_fixes(source: &str) -> Result<Vec<Report>, RnixParseErr> {
9 let parsed = rnix::parse(source).as_result()?;
10
11 Ok(parsed
12 .node()
13 .preorder_with_tokens()
14 .filter_map(|event| match event {
15 WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| {
16 rules
17 .iter()
18 .filter_map(|rule| rule.validate(&child))
19 .filter(|report| report.total_suggestion_range().is_some())
20 .collect::<Vec<_>>()
21 }),
22 _ => None,
23 })
24 .flatten()
25 .collect())
26}
27
28fn reorder(mut reports: Vec<Report>) -> Vec<Report> {
29 use std::collections::VecDeque;
30
31 reports.sort_by(|a, b| {
32 let a_range = a.range();
33 let b_range = b.range();
34 a_range.end().partial_cmp(&b_range.end()).unwrap()
35 });
36
37 reports
38 .into_iter()
39 .fold(VecDeque::new(), |mut deque: VecDeque<Report>, new_elem| {
40 let front = deque.front();
41 let new_range = new_elem.range();
42 if let Some(front_range) = front.map(|f| f.range()) {
43 if new_range.start() > front_range.end() {
44 deque.push_front(new_elem);
45 }
46 } else {
47 deque.push_front(new_elem);
48 }
49 deque
50 })
51 .into()
52}
53
54#[derive(Debug)]
55pub struct FixResult<'a> {
56 pub src: Source<'a>,
57 pub fixed: Vec<Fixed>,
58}
59
60#[derive(Debug, Clone)]
61pub struct Fixed {
62 pub at: TextRange,
63 pub code: u32,
64}
65
66impl<'a> FixResult<'a> {
67 fn empty(src: Source<'a>) -> Self {
68 Self { src, fixed: vec![] }
69 }
70}
71
72fn next(mut src: Source) -> Result<FixResult, RnixParseErr> {
73 let all_reports = collect_fixes(&src)?;
74
75 if all_reports.is_empty() {
76 return Ok(FixResult::empty(src));
77 }
78
79 let reordered = reorder(all_reports);
80
81 let fixed = reordered
82 .iter()
83 .map(|r| Fixed {
84 at: r.range(),
85 code: r.code,
86 })
87 .collect::<Vec<_>>();
88 for report in reordered {
89 report.apply(src.to_mut());
90 }
91
92 Ok(FixResult {
93 src,
94 fixed
95 })
96}
97
98pub fn fix(src: &str) -> Result<FixResult, RnixParseErr> {
99 let src = Cow::from(src);
100 let _ = rnix::parse(&src).as_result()?;
101 let mut initial = FixResult::empty(src);
102
103 while let Ok(next_result) = next(initial.src) {
104 if next_result.fixed.is_empty() {
105 return Ok(next_result);
106 } else {
107 initial = FixResult::empty(next_result.src);
108 }
109 }
110
111 unreachable!("a fix caused a syntax error, please report a bug");
112}