use std::{
env,
ffi::OsStr,
fs::{self, File},
io::Read,
path::Path,
process,
};
use toml::Value;
use walkdir::WalkDir;
const EXPECTED_LICENSE_TEXT: &[u8] = include_bytes!(".resources/license_header");
const DIRS_TO_SKIP: [&str; 8] = [".cargo", ".circleci", ".git", ".github", ".resources", "examples", "js", "target"];
#[derive(Clone, Copy, PartialEq, Eq)]
enum ImportOfInterest {
Locktick,
ParkingLot,
Tokio,
}
fn check_locktick_imports<P: AsRef<Path>>(path: P) {
let mut iter = WalkDir::new(path).into_iter();
while let Some(entry) = iter.next() {
let entry = entry.unwrap();
let entry_type = entry.file_type();
if entry_type.is_dir() && DIRS_TO_SKIP.contains(&entry.file_name().to_str().unwrap_or("")) {
iter.skip_current_dir();
continue;
}
let path = entry.path();
if path.extension() != Some(OsStr::new("rs")) {
continue;
}
let file = fs::read_to_string(path).unwrap();
let lines = file
.lines()
.filter(|l| !l.is_empty()) .skip_while(|l| !l.starts_with("use")) .take_while(|l| { l.starts_with("use")
|| l.starts_with("#[cfg")
|| l.starts_with("//")
|| *l == "};"
|| l.starts_with(|c: char| c.is_ascii_whitespace())
});
let mut import_of_interest: Option<ImportOfInterest> = None;
let mut lock_balance: i8 = 0;
for line in lines {
if import_of_interest.is_none() {
if line.starts_with("use locktick::") {
import_of_interest = Some(ImportOfInterest::Locktick);
} else if line.starts_with("use parking_lot::") {
import_of_interest = Some(ImportOfInterest::ParkingLot);
} else if line.starts_with("use tokio::") {
import_of_interest = Some(ImportOfInterest::Tokio);
}
}
let Some(ioi) = import_of_interest else {
continue;
};
if [ImportOfInterest::ParkingLot, ImportOfInterest::Tokio].contains(&ioi) {
if line.contains("Mutex") {
lock_balance += 1;
}
if line.contains("RwLock") {
lock_balance += 1;
}
} else if ioi == ImportOfInterest::Locktick {
for _hit in line.matches("Mutex") {
lock_balance -= 1;
}
for _hit in line.matches("RwLock") {
lock_balance -= 1;
}
if line.contains("TMutex") {
lock_balance += 1;
}
}
if line.ends_with(";") {
import_of_interest = None;
}
}
assert!(
lock_balance == 0,
"The locks in \"{}\" don't seem to have `locktick` counterparts!",
entry.path().display()
);
}
}
fn check_file_licenses<P: AsRef<Path>>(path: P) {
let path = path.as_ref();
let mut iter = WalkDir::new(path).into_iter();
while let Some(entry) = iter.next() {
let entry = entry.unwrap();
let entry_type = entry.file_type();
if entry_type.is_dir() && DIRS_TO_SKIP.contains(&entry.file_name().to_str().unwrap_or("")) {
iter.skip_current_dir();
continue;
}
if entry_type.is_file() && entry.file_name().to_str().unwrap_or("").ends_with(".rs") {
let file = File::open(entry.path()).unwrap();
let mut contents = Vec::with_capacity(EXPECTED_LICENSE_TEXT.len());
file.take(EXPECTED_LICENSE_TEXT.len() as u64).read_to_end(&mut contents).unwrap();
assert!(
contents == EXPECTED_LICENSE_TEXT,
"The license in \"{}\" is either missing or it doesn't match the expected string!",
entry.path().display()
);
}
}
}
fn check_locktick_profile() {
let locktick_enabled = env::var("CARGO_FEATURE_LOCKTICK").is_ok();
if locktick_enabled {
let profile = env::var("PROFILE").unwrap_or_else(|_| "".to_string());
let manifest = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("Cargo.toml");
let contents = fs::read_to_string(&manifest).expect("failed to read Cargo.toml");
let doc = contents.parse::<Value>().expect("invalid TOML in Cargo.toml");
let profile_table = doc.get("profile").and_then(|p| p.get(profile));
if let Some(Value::Table(profile_settings)) = profile_table {
if let Some(debug) = profile_settings.get("debug") {
match debug {
Value::String(s) if s == "line-tables-only" => {
println!("cargo:info=manifest has debuginfo=line-tables-only");
}
_ => {
eprintln!(
"🔴 When enabling the locktick feature, the profile must have debug set to line-tables-only. Uncomment the relevant lines in Cargo.toml."
);
process::exit(1);
}
}
} else {
eprintln!(
"🔴 When enabling the locktick feature, the profile must have debug set to line-tables-only. Uncomment the relevant lines in Cargo.toml."
);
process::exit(1);
}
}
}
}
fn main() {
check_file_licenses(".");
check_locktick_imports(".");
check_locktick_profile();
built::write_built_file().expect("Failed to acquire build-time information");
}