xtask/tasks/fmt/house_rules/
copyright.rsuse anyhow::anyhow;
use fs_err::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use std::io::Write;
use std::path::Path;
fn commit(source: File, target: &Path) -> std::io::Result<()> {
source.set_permissions(target.metadata()?.permissions())?;
let (file, path) = source.into_parts();
drop(file); fs_err::rename(path, target)
}
pub fn check_copyright(path: &Path, fix: bool) -> anyhow::Result<()> {
const HEADER_MIT_FIRST: &str = "Copyright (c) Microsoft Corporation.";
const HEADER_MIT_SECOND: &str = "Licensed under the MIT License.";
let ext = path
.extension()
.and_then(|e| e.to_str())
.unwrap_or_default();
if !matches!(
ext,
"rs" | "c" | "proto" | "toml" | "ts" | "js" | "py" | "ps1"
) {
return Ok(());
}
let f = BufReader::new(File::open(path)?);
let mut lines = f.lines();
let (script_interpreter_line, blank_after_script_interpreter_line, first_content_line) = {
let line = lines.next().unwrap_or(Ok(String::new()))?;
if line.starts_with("#!") && ext != "rs" {
let script_interpreter_line = line;
let after_script_interpreter_line = lines.next().unwrap_or(Ok(String::new()))?;
(
Some(script_interpreter_line),
Some(after_script_interpreter_line.is_empty()),
lines.next().unwrap_or(Ok(String::new()))?,
)
} else {
(None, None, line)
}
};
let second_content_line = lines.next().unwrap_or(Ok(String::new()))?;
let third_content_line = lines.next().unwrap_or(Ok(String::new()))?;
if first_content_line.contains("Copyright") && !first_content_line.contains("Microsoft") {
return Ok(());
}
let mut missing_banner = !first_content_line.contains(HEADER_MIT_FIRST)
|| !second_content_line.contains(HEADER_MIT_SECOND);
let mut missing_blank_line = !third_content_line.is_empty();
let mut header_lines = 2;
let is_msft_internal = std::env::var("XTASK_FMT_COPYRIGHT_ALLOW_MISSING_MIT").is_ok();
if is_msft_internal && missing_banner {
missing_banner =
!(first_content_line.contains("Copyright") && first_content_line.contains("Microsoft"));
missing_blank_line = !second_content_line.is_empty();
header_lines = 1;
}
if fix {
drop(lines);
if missing_banner || missing_blank_line {
let path_fix = &{
let mut p = path.to_path_buf();
let ok = p.set_extension(format!("{}.fix", ext));
assert!(ok);
p
};
let mut f = BufReader::new(File::open(path)?);
let mut f_fixed = File::create(path_fix)?;
if let Some(script_interpreter_line) = &script_interpreter_line {
writeln!(f_fixed, "{script_interpreter_line}")?;
f.read_line(&mut String::new())?;
}
if let Some(blank_after_script_interpreter_line) = blank_after_script_interpreter_line {
if !blank_after_script_interpreter_line {
writeln!(f_fixed)?;
}
}
if missing_banner {
let prefix = match ext {
"rs" | "c" | "proto" | "ts" | "js" => "//",
"toml" | "py" | "ps1" | "config" => "#",
_ => unreachable!(),
};
if script_interpreter_line.is_none() && first_content_line.starts_with('\u{feff}') {
write!(f_fixed, "\u{feff}")?;
f.read_exact(&mut [0; 3])?;
}
writeln!(f_fixed, "{} {}", prefix, HEADER_MIT_FIRST)?;
if !is_msft_internal {
writeln!(f_fixed, "{} {}", prefix, HEADER_MIT_SECOND)?;
}
writeln!(f_fixed)?; } else if missing_blank_line {
for _ in 0..header_lines {
let mut s = String::new();
f.read_line(&mut s)?;
write!(f_fixed, "{}", s)?;
}
writeln!(f_fixed)?;
}
std::io::copy(&mut f, &mut f_fixed)?;
drop(f);
commit(f_fixed, path)?;
}
}
let mut missing = vec![];
if missing_banner {
missing.push("the copyright & license header");
}
if missing_blank_line {
missing.push("a blank line after the copyright & license header");
}
if let Some(blank_after_script_interpreter_line) = blank_after_script_interpreter_line {
if !blank_after_script_interpreter_line {
missing.push("a blank line after the script interpreter line");
}
}
if missing.is_empty() {
return Ok(());
}
if fix {
log::info!(
"applied fixes for missing {:?} in {}",
missing,
path.display()
);
Ok(())
} else {
Err(anyhow!("missing {:?} in {}", missing, path.display()))
}
}