205 lines
5.1 KiB
Rust
205 lines
5.1 KiB
Rust
|
//! Simple script to verify the coding style of this library
|
||
|
//!
|
||
|
//! ## How to run
|
||
|
//!
|
||
|
//! The first argument to this script is the directory to run on, so running
|
||
|
//! this script should be as simple as:
|
||
|
//!
|
||
|
//! ```notrust
|
||
|
//! rustc ci/style.rs
|
||
|
//! ./style src
|
||
|
//! ```
|
||
|
//!
|
||
|
//! ## Guidelines
|
||
|
//!
|
||
|
//! The current style is:
|
||
|
//!
|
||
|
//! * No trailing whitespace
|
||
|
//! * No tabs
|
||
|
//! * 80-character lines
|
||
|
//! * `extern` instead of `extern "C"`
|
||
|
//! * Specific module layout:
|
||
|
//! 1. use directives
|
||
|
//! 2. typedefs
|
||
|
//! 3. structs
|
||
|
//! 4. constants
|
||
|
//! 5. f! { ... } functions
|
||
|
//! 6. extern functions
|
||
|
//! 7. modules + pub use
|
||
|
//!
|
||
|
//! Things not verified:
|
||
|
//!
|
||
|
//! * alignment
|
||
|
//! * 4-space tabs
|
||
|
//! * leading colons on paths
|
||
|
|
||
|
use std::env;
|
||
|
use std::fs;
|
||
|
use std::io::prelude::*;
|
||
|
use std::path::Path;
|
||
|
|
||
|
macro_rules! t {
|
||
|
($e:expr) => (match $e {
|
||
|
Ok(e) => e,
|
||
|
Err(e) => panic!("{} failed with {}", stringify!($e), e),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
fn main() {
|
||
|
let arg = env::args().skip(1).next().unwrap_or(".".to_string());
|
||
|
|
||
|
let mut errors = Errors { errs: false };
|
||
|
walk(Path::new(&arg), &mut errors);
|
||
|
|
||
|
if errors.errs {
|
||
|
panic!("found some lint errors");
|
||
|
} else {
|
||
|
println!("good style!");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn walk(path: &Path, err: &mut Errors) {
|
||
|
for entry in t!(path.read_dir()).map(|e| t!(e)) {
|
||
|
let path = entry.path();
|
||
|
if t!(entry.file_type()).is_dir() {
|
||
|
walk(&path, err);
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
let name = entry.file_name().into_string().unwrap();
|
||
|
match &name[..] {
|
||
|
n if !n.ends_with(".rs") => continue,
|
||
|
|
||
|
"dox.rs" |
|
||
|
"lib.rs" |
|
||
|
"macros.rs" => continue,
|
||
|
|
||
|
_ => {}
|
||
|
}
|
||
|
|
||
|
let mut contents = String::new();
|
||
|
t!(t!(fs::File::open(&path)).read_to_string(&mut contents));
|
||
|
|
||
|
check_style(&contents, &path, err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct Errors {
|
||
|
errs: bool,
|
||
|
}
|
||
|
|
||
|
#[derive(Clone, Copy, PartialEq)]
|
||
|
enum State {
|
||
|
Start,
|
||
|
Imports,
|
||
|
Typedefs,
|
||
|
Structs,
|
||
|
Constants,
|
||
|
FunctionDefinitions,
|
||
|
Functions,
|
||
|
Modules,
|
||
|
}
|
||
|
|
||
|
fn check_style(file: &str, path: &Path, err: &mut Errors) {
|
||
|
let mut state = State::Start;
|
||
|
let mut s_macros = 0;
|
||
|
let mut f_macros = 0;
|
||
|
let mut prev_blank = false;
|
||
|
|
||
|
for (i, line) in file.lines().enumerate() {
|
||
|
if line == "" {
|
||
|
if prev_blank {
|
||
|
err.error(path, i, "double blank line");
|
||
|
}
|
||
|
prev_blank = true;
|
||
|
} else {
|
||
|
prev_blank = false;
|
||
|
}
|
||
|
if line != line.trim_right() {
|
||
|
err.error(path, i, "trailing whitespace");
|
||
|
}
|
||
|
if line.contains("\t") {
|
||
|
err.error(path, i, "tab character");
|
||
|
}
|
||
|
if line.len() > 80 {
|
||
|
err.error(path, i, "line longer than 80 chars");
|
||
|
}
|
||
|
if line.contains("extern \"C\"") {
|
||
|
err.error(path, i, "use `extern` instead of `extern \"C\"");
|
||
|
}
|
||
|
if line.contains("#[cfg(") && !line.contains(" if ") {
|
||
|
if state != State::Structs {
|
||
|
err.error(path, i, "use cfg_if! and submodules \
|
||
|
instead of #[cfg]");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let line = line.trim_left();
|
||
|
let is_pub = line.starts_with("pub ");
|
||
|
let line = if is_pub {&line[4..]} else {line};
|
||
|
|
||
|
let line_state = if line.starts_with("use ") {
|
||
|
if is_pub {
|
||
|
State::Modules
|
||
|
} else {
|
||
|
State::Imports
|
||
|
}
|
||
|
} else if line.starts_with("const ") {
|
||
|
State::Constants
|
||
|
} else if line.starts_with("type ") {
|
||
|
State::Typedefs
|
||
|
} else if line.starts_with("s! {") {
|
||
|
s_macros += 1;
|
||
|
State::Structs
|
||
|
} else if line.starts_with("f! {") {
|
||
|
f_macros += 1;
|
||
|
State::FunctionDefinitions
|
||
|
} else if line.starts_with("extern ") {
|
||
|
State::Functions
|
||
|
} else if line.starts_with("mod ") {
|
||
|
State::Modules
|
||
|
} else {
|
||
|
continue
|
||
|
};
|
||
|
|
||
|
if state as usize > line_state as usize {
|
||
|
err.error(path, i, &format!("{} found after {} when \
|
||
|
it belongs before",
|
||
|
line_state.desc(), state.desc()));
|
||
|
}
|
||
|
|
||
|
if f_macros == 2 {
|
||
|
f_macros += 1;
|
||
|
err.error(path, i, "multiple f! macros in one module");
|
||
|
}
|
||
|
if s_macros == 2 {
|
||
|
s_macros += 1;
|
||
|
err.error(path, i, "multiple s! macros in one module");
|
||
|
}
|
||
|
|
||
|
state = line_state;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl State {
|
||
|
fn desc(&self) -> &str {
|
||
|
match *self {
|
||
|
State::Start => "start",
|
||
|
State::Imports => "import",
|
||
|
State::Typedefs => "typedef",
|
||
|
State::Structs => "struct",
|
||
|
State::Constants => "constant",
|
||
|
State::FunctionDefinitions => "function definition",
|
||
|
State::Functions => "extern function",
|
||
|
State::Modules => "module",
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Errors {
|
||
|
fn error(&mut self, path: &Path, line: usize, msg: &str) {
|
||
|
self.errs = true;
|
||
|
println!("{}:{} - {}", path.display(), line + 1, msg);
|
||
|
}
|
||
|
}
|