solidhunter_lib/
linter.rs1use 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}