Skip to content

Commit 9d98192

Browse files
committed
Use function ptrs for Rules
1 parent dd3cd09 commit 9d98192

6 files changed

Lines changed: 92 additions & 69 deletions

File tree

run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ fi
2424
cargo install --locked --quiet --version 3.12.0 bacon
2525

2626
# run the app
27-
bacon --headless run -- --bin namelint -- --rules ./test/custom_rules.yaml --config ./config/self.yaml
27+
bacon run -- --bin namelint -- --rules ./test/custom_rules.yaml --config ./config/self.yaml

src/main.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod run_lint;
99
mod schema;
1010
mod structs;
1111
mod rules;
12+
mod regex_rule;
1213

1314
use std::{collections::HashMap, ffi::OsString, fs::{self}};
1415
use clap::{arg, Command};
@@ -20,7 +21,7 @@ use parse_config::parse_config;
2021
use parse_rules::parse_rules;
2122
use run_lint::run_lint;
2223
use schema::{must_load_validator, SchemaType};
23-
use structs::{ConfigFile, FileData, Rule, RuleSet};
24+
use structs::{ConfigFile, FileData, Lint, RuleFn, RuleSet};
2425

2526
static RULES_DIR: Dir = include_dir!("./rules");
2627

@@ -55,7 +56,7 @@ fn main() {
5556
//LATER: convert to log level let verbose = binding.get_count("verbose");
5657
let rule_validator = must_load_validator(SchemaType::Rule);
5758

58-
let mut all_rules: HashMap<String, Rule> = HashMap::new();
59+
let mut all_rules: HashMap<String, RuleFn> = HashMap::new();
5960
let mut all_rulesets: HashMap<String, RuleSet> = HashMap::new();
6061

6162
for rule_file in RULES_DIR.find("*.yaml").unwrap() {
@@ -155,12 +156,14 @@ fn main() {
155156
println!("DEBUG: file '{}'", file.lintpath);
156157
}
157158

158-
for (index, lint) in config.lints.iter_mut().enumerate() {
159-
if lint.name.is_none() {
160-
lint.name = Some(format!("#{}", index));
161-
}
162-
println!("DEBUG: applying lint {}", lint.name.clone().unwrap());
163-
run_lint(lint, &mut files, &all_rules, &all_rulesets);
159+
for (index, cfg_lint) in config.lints.iter_mut().enumerate() {
160+
let lint:Lint = Lint {
161+
name: cfg_lint.name.clone().unwrap_or_else(|| format!("#{}", index)),
162+
paths: cfg_lint.paths.clone(),
163+
rules: cfg_lint.rules.clone(),
164+
};
165+
println!("DEBUG: applying lint {}", lint.name);
166+
run_lint(&lint, &mut files, &all_rules, &all_rulesets);
164167
}
165168

166169
for file in files.iter_mut() {

src/parse_rules.rs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::collections::HashMap;
2-
use regex::RegexBuilder;
32

43
use crate::load::{must_load_yaml, must_yaml_to_json};
5-
use crate::structs::{Rule, RuleFile, RuleSet};
4+
use crate::structs::{RuleFile, RuleFn, RuleSet};
5+
use crate::regex_rule::build_regex_rule_fn;
66

7-
pub fn parse_rules(body: &str, src: &str, validator: &jsonschema::Validator, all_rules: &mut HashMap<String, Rule>, all_rulesets: &mut HashMap<String, RuleSet>) {
7+
pub fn parse_rules(body: &str, src: &str, validator: &jsonschema::Validator, all_rules: &mut HashMap<String, RuleFn>, all_rulesets: &mut HashMap<String, RuleSet>) {
88
let yaml_data = must_load_yaml(body, &src);
99
let json_data = must_yaml_to_json(&yaml_data, &src);
1010
let is_valid = validator.validate(&json_data);
@@ -27,25 +27,11 @@ pub fn parse_rules(body: &str, src: &str, validator: &jsonschema::Validator, all
2727
}
2828

2929
if rulef.rules.is_some() {
30-
let mut rules = rulef.rules.unwrap();
31-
for rule in rules.iter_mut() {
30+
let rules = rulef.rules.unwrap();
31+
for rule in rules.iter() {
3232
println!("DEBUG: Loading rule: {} ({})", rule.title, rule.rule_id);
33-
if rule.regex.is_some() {
34-
let rule_regex = rule.regex.as_mut().unwrap();
35-
let mut regex_builder = RegexBuilder::new(&rule_regex.pattern);
36-
if rule_regex.case_insensitive {
37-
regex_builder.case_insensitive(true);
38-
}
39-
let regex = regex_builder.build();
40-
if regex.is_ok() {
41-
rule_regex.regex = regex.ok();
42-
} else {
43-
println!("ERROR: Invalid regex for rule_id {}: {}", rule.rule_id, regex.err().unwrap());
44-
continue;
45-
}
46-
47-
}
48-
all_rules.insert(rule.rule_id.clone(), rule.clone());
33+
let tmp_rule = rule.clone();
34+
all_rules.insert(rule.rule_id.clone(), build_regex_rule_fn(&tmp_rule));
4935
}
5036
}
5137
}

src/regex_rule.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use regex::{Regex, RegexBuilder};
2+
3+
use crate::structs::{ Rule, RuleFn };
4+
5+
pub fn build_regex_rule_fn<'a>(the_rule: &Rule) -> RuleFn<'a> {
6+
let rule_id: &'static str = Box::leak(the_rule.rule_id.clone().into_boxed_str());
7+
let rregex = the_rule.regex.clone();
8+
if rregex.is_none() {
9+
println!("ERROR: Rule {} has no regex", rule_id);
10+
std::process::exit(9);
11+
}
12+
let rregex = rregex.unwrap();
13+
let pattern = rregex.pattern;
14+
let case_insensitive = rregex.case_insensitive;
15+
let matches: bool;
16+
if rregex.expect == "match" {
17+
matches = true;
18+
} else if rregex.expect == "nonmatch" {
19+
matches = false;
20+
} else {
21+
println!("ERROR: Rule {} has invalid expect value: '{}'", rule_id, rregex.expect);
22+
std::process::exit(10);
23+
}
24+
25+
let mut regex_builder = RegexBuilder::new(&pattern);
26+
if case_insensitive {
27+
regex_builder.case_insensitive(true);
28+
}
29+
let regex = regex_builder.build();
30+
if regex.is_err() {
31+
let err = regex.as_ref().err().unwrap();
32+
println!("ERROR: Invalid regex for rule_id {}: {}", rule_id, err);
33+
}
34+
35+
let regex: &'static Regex = Box::leak(Box::new(regex.unwrap()));
36+
37+
Box::new(move |test: &str| {
38+
if regex.is_match(test) == matches {
39+
println!("DEBUG: Rule {} success for: '{}'", rule_id, test);
40+
return true;
41+
} else {
42+
println!("DEBUG: Rule {} failed for : '{}'", rule_id, test);
43+
return false;
44+
}
45+
})
46+
}
47+

src/run_lint.rs

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use std::collections::HashMap;
22

3-
use crate::structs::{ConfigLint, FileData, Rule, RuleSet};
3+
use crate::structs::{Lint, FileData, RuleFn, RuleSet};
44

5-
pub fn run_lint(lint: &ConfigLint, files: &mut Vec<FileData>, all_rules: &HashMap<String, Rule>, _all_rulesets: &HashMap<String, RuleSet>) {
5+
pub fn run_lint(lint: &Lint, files: &mut Vec<FileData>, all_rules: &HashMap<String, RuleFn>, _all_rulesets: &HashMap<String, RuleSet>) {
66

77

8-
let mut rules: Vec<Rule> = Vec::new();
9-
8+
// LATER: load rules from rulesets too
109
let lint_rules = &lint.rules;
1110
if lint_rules.is_some() {
1211
let lint_rules = lint_rules.as_ref().unwrap();
@@ -15,43 +14,21 @@ pub fn run_lint(lint: &ConfigLint, files: &mut Vec<FileData>, all_rules: &HashMa
1514

1615
let the_rule = all_rules.get(rule_id);
1716
if the_rule.is_none() {
18-
println!("ERROR: unknown rule_id '{}'", rule_id);
17+
println!("ERROR: lint {} has unknown rule_id '{}'", lint.name, rule_id);
1918
std::process::exit(7);
2019
}
21-
let the_rule = the_rule.unwrap();
22-
rules.push(the_rule.clone());
23-
}
24-
}
25-
26-
// LATER: load rules from rulesets
27-
if rules.len() == 0 {
28-
println!("WARNING: no rules for {}", lint.name.clone().unwrap());
29-
return;
30-
}
31-
32-
for rule in rules.iter() {
33-
34-
if rule.regex.is_none() {
35-
println!("ERROR: Rule {} has no regex", rule.rule_id);
36-
continue;
37-
}
38-
39-
let regex_data = rule.regex.as_ref().unwrap();
40-
if regex_data.regex.is_none() {
41-
println!("ERROR: Rule {} has invalid regex", rule.rule_id);
42-
continue;
43-
}
44-
let regex = regex_data.regex.as_ref().unwrap();
45-
let expect = regex_data.expect == "match";
46-
47-
for file in files.iter_mut() {
48-
let value = &file.file_name;
49-
let result = regex.is_match(value);
50-
if result != expect {
51-
println!("DEBUG: failed match for '{}': rule={} expect={}, got={}", value, regex_data.pattern, expect, result);
52-
file.failed.push(rule.rule_id.clone());
53-
} else {
54-
file.passed.push(rule.rule_id.clone());
20+
let rule_fn = the_rule.unwrap();
21+
22+
23+
for file in files.iter_mut() {
24+
let value = &file.file_name;
25+
let result = rule_fn(value);
26+
if result {
27+
file.passed.push(rule_id.clone());
28+
} else {
29+
println!("DEBUG: failed match for '{}': rule={}", file.file_name, rule_id);
30+
file.failed.push(rule_id.clone());
31+
}
5532
}
5633
}
5734
}

src/structs.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,20 @@ pub struct ConfigLint {
6262
pub rulesets: Option<Vec<String>>,
6363
}
6464

65+
pub struct Lint {
66+
pub name: String,
67+
pub paths: Vec<String>,
68+
pub rules: Option<Vec<String>>,
69+
}
70+
6571
#[derive(serde::Deserialize, Debug, Clone)]
6672
pub struct ConfigFile {
6773
pub dirs: Option<Vec<String>>,
6874
#[serde(rename = "ignore-dirs")]
6975
pub ignore_dirs: Option<Vec<String>>,
7076
pub lints: Vec<ConfigLint>,
7177
}
78+
79+
//pub type RuleFn = fn(String) -> bool;
80+
//pub type RuleFn = Box<dyn Fn(&str) -> bool>;
81+
pub type RuleFn<'a> = Box<dyn Fn(&str) -> bool + 'a>;

0 commit comments

Comments
 (0)