From 7142c4f09e2f532786660f3537b10698313e2d62 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 14 Dec 2020 13:43:42 +0100 Subject: [PATCH 1/3] Clean up code a bit --- src/main.rs | 199 ++----------------------------------------- src/manual_traits.rs | 193 +++++++++++++++++++++++++++++++++++++++++ src/types.rs | 1 + 3 files changed, 201 insertions(+), 192 deletions(-) create mode 100644 src/manual_traits.rs create mode 100644 src/types.rs diff --git a/src/main.rs b/src/main.rs index 9f63fc3..3967c2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,9 @@ extern crate toml; -use std::collections::HashSet; use std::env; -use std::fs; use std::path::Path; use std::process::exit; -use toml::Value; - -type Result = std::result::Result; - macro_rules! get_vec { ($toml:expr, $key:expr) => {{ match $toml.lookup_vec($key) { @@ -19,193 +13,14 @@ macro_rules! get_vec { }}; } -// This trait comes from https://github.com/gtk-rs/gir -pub trait TomlHelper -where - Self: Sized, -{ - fn lookup<'a>(&'a self, option: &str) -> Option<&'a toml::Value>; - fn lookup_str<'a>(&'a self, option: &'a str) -> Option<&'a str>; - fn lookup_vec<'a>(&'a self, option: &'a str) -> Option<&'a Vec>; -} - -impl TomlHelper for toml::Value { - fn lookup<'a>(&'a self, option: &str) -> Option<&'a toml::Value> { - let mut value = self; - for opt in option.split('.') { - let table = match value.as_table() { - Some(table) => table, - None => return None, - }; - value = match table.get(opt) { - Some(value) => value, - None => return None, - }; - } - Some(value) - } - fn lookup_str<'a>(&'a self, option: &'a str) -> Option<&'a str> { - self.lookup(option)?.as_str() - } - fn lookup_vec<'a>(&'a self, option: &'a str) -> Option<&'a Vec> { - self.lookup(option)?.as_array() - } -} - -#[derive(Debug)] -struct Info { - correctly_declared_manual_traits: HashSet, - listed_crate_objects: HashSet, -} - -// Return a map where the key is the full object name and has the manual associated traits. -fn get_objects(toml_file: &Path) -> Result { - println!("==> Getting objects from {:?}", toml_file.display()); - let mut correctly_declared_manual_traits: HashSet = HashSet::new(); - let mut listed_crate_objects: HashSet = HashSet::new(); - - let toml: Value = toml::from_str( - &fs::read_to_string(toml_file) - .map_err(|e| format!("Failed to read {:?}: {}", toml_file, e))?, - ) - .expect("invalid toml"); - - let current_lib = toml - .lookup_str("options.library") - .expect("failed to get current library"); - for entry in get_vec!(toml, "options.generate") - .iter() - .filter_map(|x| x.as_str()) - { - listed_crate_objects.insert( - entry - .split(".") - .skip(1) - .next() - .expect("couldn't extract name") - .to_owned(), - ); - } - for entry in get_vec!(toml, "options.builders") - .iter() - .filter_map(|x| x.as_str()) - { - listed_crate_objects.insert( - entry - .split(".") - .skip(1) - .next() - .expect("couldn't extract name") - .to_owned(), - ); - } - for entry in get_vec!(toml, "options.manual") - .iter() - .filter_map(|x| x.as_str()) - { - let mut parts = entry.split("."); - let lib = parts.next().expect("failed to extract lib"); - if lib != current_lib { - continue; - } - listed_crate_objects.insert(parts.next().expect("couldn't extract name").to_owned()); - } - for objs in toml.lookup("object").map(|a| a.as_array().unwrap()) { - for obj in objs { - if let Some(name) = obj.lookup_str("name") { - let mut parts = name.split("."); - let lib = parts.next().expect("failed to extract lib"); - if lib != current_lib { - continue; - } - if let Some(name) = parts.next() { - for elem in get_vec!(obj, "manual_traits") - .iter() - .filter_map(|x| x.as_str()) - .map(|x| x.to_owned()) - { - correctly_declared_manual_traits.insert(elem); - } - listed_crate_objects.insert(name.to_owned()); - } - } - } - } - println!("<== done"); - Ok(Info { - correctly_declared_manual_traits, - listed_crate_objects, - }) -} - -fn get_manual_traits_from_file( - src_file: &Path, - objects: &Info, - ret: &mut Vec, -) -> Result<()> { - let content = fs::read_to_string(src_file) - .map_err(|e| format!("Failed to read {:?}: {}", src_file, e))?; - for line in content.lines() { - let line = line.trim(); - if !line.starts_with("pub trait ") { - continue; - } - let line = &line[10..]; - let mut pos = (line.find('{').unwrap_or(line.len()), '{'); - for x in &['<', ':'] { - if let Some(p) = line.find(*x) { - if p < pos.0 { - pos.0 = p; - pos.1 = *x; - } - } - } - let name = line.split(pos.1).next().expect("failed to get trait name"); - if !name.ends_with("ExtManual") { - continue; - } - let obj = &name[..name.len() - 9]; - if !objects.correctly_declared_manual_traits.contains(name) - && objects.listed_crate_objects.contains(obj) - { - ret.push(name.to_owned()); - } - } - - Ok(()) -} - -fn get_manual_traits(src_dir: &Path, objects: &Info) -> Result> { - println!("==> Getting manual traits from {:?}", src_dir.display()); - let mut ret = Vec::new(); - - for entry in fs::read_dir(src_dir) - .map_err(|e| format!("Failed to read directory {:?}: {}", src_dir, e))? - { - let entry = entry.expect("Failed to enter directory"); - let path = entry.path(); - if !path.is_dir() { - get_manual_traits_from_file(&path, objects, &mut ret)?; - } - } - println!("<== done"); - Ok(ret) -} - -fn run_check>(folder: P, gir_file: &str) -> Result { - let folder = folder.as_ref(); - println!("=> Running for {}", folder.display()); +mod manual_traits; +mod types; - let objects = get_objects(&folder.join(gir_file))?; - let results = get_manual_traits(&folder.join("src"), &objects)?; - if !results.is_empty() { - println!("xx> Some manual traits are missing from the Gir.toml file:"); - for result in results.iter() { - println!("{}", result); - } - } +fn run_check>(folder: P, gir_file: &str) -> types::CheckResult { + println!("=> Running for {}", folder.as_ref().display()); + let result = manual_traits::run_check(folder, gir_file)?; println!("<= done"); - Ok(results.is_empty()) + Ok(result) } fn show_help() { @@ -214,7 +29,7 @@ fn show_help() { println!(" -h | --help : Display this help"); } -fn main() -> Result<()> { +fn main() -> types::CheckResult<()> { let mut gir_file = "Gir.toml".to_owned(); let mut result = true; let args = env::args().into_iter().skip(1).collect::>(); diff --git a/src/manual_traits.rs b/src/manual_traits.rs new file mode 100644 index 0000000..1f484f9 --- /dev/null +++ b/src/manual_traits.rs @@ -0,0 +1,193 @@ +use crate::types::CheckResult; + +use std::collections::HashSet; +use std::fs; +use std::path::Path; + +use toml::Value; + +// This trait comes from https://github.com/gtk-rs/gir +pub trait TomlHelper +where + Self: Sized, +{ + fn lookup<'a>(&'a self, option: &str) -> Option<&'a toml::Value>; + fn lookup_str<'a>(&'a self, option: &'a str) -> Option<&'a str>; + fn lookup_vec<'a>(&'a self, option: &'a str) -> Option<&'a Vec>; +} + +impl TomlHelper for toml::Value { + fn lookup<'a>(&'a self, option: &str) -> Option<&'a toml::Value> { + let mut value = self; + for opt in option.split('.') { + let table = match value.as_table() { + Some(table) => table, + None => return None, + }; + value = match table.get(opt) { + Some(value) => value, + None => return None, + }; + } + Some(value) + } + fn lookup_str<'a>(&'a self, option: &'a str) -> Option<&'a str> { + self.lookup(option)?.as_str() + } + fn lookup_vec<'a>(&'a self, option: &'a str) -> Option<&'a Vec> { + self.lookup(option)?.as_array() + } +} + +#[derive(Debug)] +struct Info { + correctly_declared_manual_traits: HashSet, + listed_crate_objects: HashSet, +} + +// Return a map where the key is the full object name and has the manual associated traits. +fn get_objects(toml_file: &Path) -> CheckResult { + println!("==> Getting objects from {:?}", toml_file.display()); + let mut correctly_declared_manual_traits: HashSet = HashSet::new(); + let mut listed_crate_objects: HashSet = HashSet::new(); + + let toml: Value = toml::from_str( + &fs::read_to_string(toml_file) + .map_err(|e| format!("Failed to read {:?}: {}", toml_file, e))?, + ) + .expect("invalid toml"); + + let current_lib = toml + .lookup_str("options.library") + .expect("failed to get current library"); + for entry in get_vec!(toml, "options.generate") + .iter() + .filter_map(|x| x.as_str()) + { + listed_crate_objects.insert( + entry + .split(".") + .skip(1) + .next() + .expect("couldn't extract name") + .to_owned(), + ); + } + for entry in get_vec!(toml, "options.builders") + .iter() + .filter_map(|x| x.as_str()) + { + listed_crate_objects.insert( + entry + .split(".") + .skip(1) + .next() + .expect("couldn't extract name") + .to_owned(), + ); + } + for entry in get_vec!(toml, "options.manual") + .iter() + .filter_map(|x| x.as_str()) + { + let mut parts = entry.split("."); + let lib = parts.next().expect("failed to extract lib"); + if lib != current_lib { + continue; + } + listed_crate_objects.insert(parts.next().expect("couldn't extract name").to_owned()); + } + for objs in toml.lookup("object").map(|a| a.as_array().unwrap()) { + for obj in objs { + if let Some(name) = obj.lookup_str("name") { + let mut parts = name.split("."); + let lib = parts.next().expect("failed to extract lib"); + if lib != current_lib { + continue; + } + if let Some(name) = parts.next() { + for elem in get_vec!(obj, "manual_traits") + .iter() + .filter_map(|x| x.as_str()) + .map(|x| x.to_owned()) + { + correctly_declared_manual_traits.insert(elem); + } + listed_crate_objects.insert(name.to_owned()); + } + } + } + } + println!("<== done"); + Ok(Info { + correctly_declared_manual_traits, + listed_crate_objects, + }) +} + +fn get_manual_traits_from_file( + src_file: &Path, + objects: &Info, + ret: &mut Vec, +) -> CheckResult<()> { + let content = fs::read_to_string(src_file) + .map_err(|e| format!("Failed to read {:?}: {}", src_file, e))?; + for line in content.lines() { + let line = line.trim(); + if !line.starts_with("pub trait ") { + continue; + } + let line = &line[10..]; + let mut pos = (line.find('{').unwrap_or(line.len()), '{'); + for x in &['<', ':'] { + if let Some(p) = line.find(*x) { + if p < pos.0 { + pos.0 = p; + pos.1 = *x; + } + } + } + let name = line.split(pos.1).next().expect("failed to get trait name"); + if !name.ends_with("ExtManual") { + continue; + } + let obj = &name[..name.len() - 9]; + if !objects.correctly_declared_manual_traits.contains(name) + && objects.listed_crate_objects.contains(obj) + { + ret.push(name.to_owned()); + } + } + + Ok(()) +} + +fn get_manual_traits(src_dir: &Path, objects: &Info) -> CheckResult> { + println!("==> Getting manual traits from {:?}", src_dir.display()); + let mut ret = Vec::new(); + + for entry in fs::read_dir(src_dir) + .map_err(|e| format!("Failed to read directory {:?}: {}", src_dir, e))? + { + let entry = entry.expect("Failed to enter directory"); + let path = entry.path(); + if !path.is_dir() { + get_manual_traits_from_file(&path, objects, &mut ret)?; + } + } + println!("<== done"); + Ok(ret) +} + +pub fn run_check>(folder: P, gir_file: &str) -> CheckResult { + let folder = folder.as_ref(); + let objects = get_objects(&folder.join(gir_file))?; + let results = get_manual_traits(&folder.join("src"), &objects)?; + if !results.is_empty() { + println!("xx> Some manual traits are missing from the Gir.toml file:"); + for result in results.iter() { + println!("{}", result); + } + } + Ok(results.is_empty()) +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..9f0a299 --- /dev/null +++ b/src/types.rs @@ -0,0 +1 @@ +pub type CheckResult = std::result::Result; From 69266a3492173ddb889c052f0c4915b04570075d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 14 Dec 2020 14:15:12 +0100 Subject: [PATCH 2/3] Add missing license header check --- src/license.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 5 ++++- src/manual_traits.rs | 2 +- 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/license.rs diff --git a/src/license.rs b/src/license.rs new file mode 100644 index 0000000..3831a39 --- /dev/null +++ b/src/license.rs @@ -0,0 +1,49 @@ +use std::fs; +use std::io::{BufRead, BufReader}; +use std::path::Path; + +use crate::types::CheckResult; + +fn check_if_license_is_present(path: &Path) -> CheckResult { + let f = + fs::File::open(path).map_err(|e| format!("Failed to open `{}`: {}", path.display(), e))?; + let f = BufReader::new(f); + let header: Vec = f.lines().take(2).map(|x| x.unwrap()).collect(); + if header.len() != 2 { + println!("xx> Missing header in `{}`", path.display()); + Ok(false) + } else if header[0] + != "// Take a look at the license at the top of the repository in the LICENSE file." + { + println!("xx> Missing header in `{}`", path.display()); + Ok(false) + } else if !header[1].is_empty() { + println!( + "xx> Expected empty line after license header in `{}`", + path.display() + ); + Ok(false) + } else { + Ok(true) + } +} + +pub fn run_check>(folder: &P) -> CheckResult { + let folder = folder.as_ref(); + let src_dir = folder.join("src"); + println!("==> Checking license headers from {:?}", src_dir.display()); + let mut nb_errors = 0; + for entry in fs::read_dir(&src_dir) + .map_err(|e| format!("Failed to read directory {:?}: {}", src_dir, e))? + { + let entry = entry.expect("Failed to enter directory"); + let path = entry.path(); + if !path.is_dir() { + if !check_if_license_is_present(&path)? { + nb_errors += 1; + } + } + } + println!("<== done"); + Ok(nb_errors == 0) +} diff --git a/src/main.rs b/src/main.rs index 3967c2b..af501d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,12 +13,13 @@ macro_rules! get_vec { }}; } +mod license; mod manual_traits; mod types; fn run_check>(folder: P, gir_file: &str) -> types::CheckResult { println!("=> Running for {}", folder.as_ref().display()); - let result = manual_traits::run_check(folder, gir_file)?; + let result = manual_traits::run_check(&folder, gir_file)? && license::run_check(&folder)?; println!("<= done"); Ok(result) } @@ -27,6 +28,8 @@ fn show_help() { println!("== checker options =="); println!(" --gir-file : Set gir file path to be used for all following folders"); println!(" -h | --help : Display this help"); + println!(""); + println!("Any other argument will be the folder to run `checker` into."); } fn main() -> types::CheckResult<()> { diff --git a/src/manual_traits.rs b/src/manual_traits.rs index 1f484f9..d9ba9da 100644 --- a/src/manual_traits.rs +++ b/src/manual_traits.rs @@ -179,7 +179,7 @@ fn get_manual_traits(src_dir: &Path, objects: &Info) -> CheckResult> Ok(ret) } -pub fn run_check>(folder: P, gir_file: &str) -> CheckResult { +pub fn run_check>(folder: &P, gir_file: &str) -> CheckResult { let folder = folder.as_ref(); let objects = get_objects(&folder.join(gir_file))?; let results = get_manual_traits(&folder.join("src"), &objects)?; From 8be96b3c6aaf8305798ed0cfe9430ea3101a242c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 14 Dec 2020 17:14:23 +0100 Subject: [PATCH 3/3] Add options to not run all checks if unneeded --- src/main.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index af501d0..21e0c45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,23 +17,38 @@ mod license; mod manual_traits; mod types; -fn run_check>(folder: P, gir_file: &str) -> types::CheckResult { +fn run_check>( + folder: P, + gir_file: &str, + check_manual_traits: bool, + check_license: bool, +) -> types::CheckResult { println!("=> Running for {}", folder.as_ref().display()); - let result = manual_traits::run_check(&folder, gir_file)? && license::run_check(&folder)?; + let mut result = true; + if check_manual_traits { + result = manual_traits::run_check(&folder, gir_file)?; + } + if check_license { + result &= license::run_check(&folder)?; + } println!("<= done"); Ok(result) } fn show_help() { println!("== checker options =="); - println!(" --gir-file : Set gir file path to be used for all following folders"); - println!(" -h | --help : Display this help"); + println!(" --gir-file : Set gir file path to be used for all following folders"); + println!(" -h | --help : Display this help"); + println!(" --no-manual-traits : Don't run manual_traits check"); + println!(" --no-license : Don't run license check"); println!(""); println!("Any other argument will be the folder to run `checker` into."); } fn main() -> types::CheckResult<()> { let mut gir_file = "Gir.toml".to_owned(); + let mut check_manual_traits = true; + let mut check_license = true; let mut result = true; let args = env::args().into_iter().skip(1).collect::>(); let mut i = 0; @@ -48,10 +63,15 @@ fn main() -> types::CheckResult<()> { } else if arg == "--help" || arg == "-h" { show_help(); return Ok(()); + } else if arg == "--no-manual-traits" { + check_manual_traits = false; + } else if arg == "--no-license" { + check_license = false; } else { - if !run_check(&arg, &gir_file)? { + if !run_check(&arg, &gir_file, check_manual_traits, check_license)? { result = false; } + break; } i += 1; }