solidhunter_lib/
linter.rs

1use crate::errors::SolidHunterError;
2use crate::rules::create_default_rules;
3use crate::rules::factory::RuleFactory;
4use crate::rules::rule_impl::parse_rules;
5use crate::rules::types::*;
6use crate::types::*;
7use std::fs;
8
9use crate::ignore::get_excluded_files;
10use glob::glob;
11use std::path::Path;
12
13#[derive(Debug, Clone)]
14pub struct SolidFile {
15    pub data: osmium_libs_solidity_ast_extractor::File,
16    pub path: String,
17    pub content: String,
18}
19
20pub struct SolidLinter {
21    files: Vec<SolidFile>,
22    rule_factory: RuleFactory,
23    rules: Vec<Box<dyn RuleType>>,
24    excluded_files: Vec<String>,
25}
26
27impl Default for SolidLinter {
28    fn default() -> Self {
29        SolidLinter::new()
30    }
31}
32
33impl SolidLinter {
34    pub fn new() -> Self {
35        SolidLinter {
36            files: Vec::new(),
37            rule_factory: RuleFactory::default(),
38            rules: vec![],
39            excluded_files: Vec::new(),
40        }
41    }
42
43    pub fn new_fileless() -> Self {
44        let default_rules = create_default_rules();
45        let mut linter = SolidLinter {
46            files: Vec::new(),
47            rule_factory: RuleFactory::default(),
48            rules: Vec::new(),
49            excluded_files: Vec::new(),
50        };
51
52        for rule in default_rules {
53            linter.rules.push(linter.rule_factory.create_rule(rule));
54        }
55
56        linter
57    }
58
59    pub fn initialize_rules(&mut self, rules_config: &str) -> Result<(), SolidHunterError> {
60        let res = parse_rules(rules_config)?;
61        for rule in res.rules {
62            self.rules.push(self.rule_factory.create_rule(rule));
63        }
64        Ok(())
65    }
66
67    pub fn initialize_excluded_files(
68        &mut self,
69        excluded_filepaths: Option<&Vec<String>>,
70        filepaths: &Vec<String>,
71    ) -> Result<(), SolidHunterError> {
72        if let Some(excluded) = excluded_filepaths {
73            for path in excluded {
74                self.excluded_files.push(path.clone())
75            }
76        }
77        self.excluded_files
78            .append(&mut get_excluded_files(filepaths)?);
79
80        Ok(())
81    }
82
83    fn _file_exists(&self, path: &str) -> bool {
84        for file in &self.files {
85            if file.path == path {
86                return true;
87            }
88        }
89        false
90    }
91
92    fn _add_file(
93        &mut self,
94        path: &str,
95        ast: osmium_libs_solidity_ast_extractor::File,
96        content: &str,
97    ) {
98        if self._file_exists(path) {
99            for file in &mut self.files {
100                if file.path == path {
101                    file.data = ast.clone();
102                    file.content = String::from(content);
103                }
104            }
105        } else {
106            let file = SolidFile {
107                data: ast,
108                path: String::from(path),
109                content: String::from(content),
110            };
111            self.files.push(file);
112        }
113    }
114
115    pub fn parse_file(&mut self, filepath: String) -> LintResult {
116        let content = fs::read_to_string(filepath.clone())?;
117        if self.excluded_files.contains(&filepath) {
118            return Ok(FileDiags::new(content, Vec::new()));
119        }
120        self.parse_content(&filepath, content.as_str())
121    }
122
123    pub fn parse_content(&mut self, filepath: &str, content: &str) -> LintResult {
124        let res = osmium_libs_solidity_ast_extractor::extract::extract_ast_from_content(content)?;
125
126        self._add_file(filepath, res, content);
127        let mut res: Vec<LintDiag> = Vec::new();
128
129        for rule in &self.rules {
130            let mut diags = rule.diagnose(&self.files[self.files.len() - 1], &self.files);
131            res.append(&mut diags);
132        }
133        Ok(FileDiags::new(content.to_string(), res))
134    }
135
136    pub fn parse_folder(&mut self, folder: &str) -> Vec<LintResult> {
137        let mut result: Vec<LintResult> = Vec::new();
138        if let Ok(entries) = glob(&(folder.to_owned() + "/**/*.sol")) {
139            for entry in entries.flatten() {
140                result.push(self.parse_file(entry.into_os_string().into_string().unwrap()));
141            }
142        }
143        result
144    }
145    pub fn parse_path(&mut self, path: &str) -> Vec<LintResult> {
146        if Path::new(&path).is_file() {
147            vec![self.parse_file(path.to_string())]
148        } else {
149            self.parse_folder(path)
150        }
151    }
152
153    pub fn delete_file(&mut self, path: &str) {
154        loop {
155            let idx = self.files.iter().position(|x| x.path == path);
156            match idx {
157                Some(idx) => {
158                    self.files.remove(idx);
159                }
160                None => {
161                    break;
162                }
163            }
164        }
165    }
166}