use std::collections::HashSet;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::LazyLock;
use anyhow::Result;
use log::debug;
use regex::Regex;
static MOD_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(pub\s+)?mod\s+(?P<name>[a-zA-Z_][a-zA-Z0-9_]*);").unwrap());
pub fn update_mod_rs(mod_dir: PathBuf) -> Result<()> {
let mod_rs_path = mod_dir.join("mod.rs");
if !mod_rs_path.exists() {
anyhow::bail!("目标目录{}不存在mod.rs ", mod_dir.display())
}
let file = std::fs::File::open(&mod_rs_path).expect("无法读取 mod.rs");
let reader = BufReader::new(file);
let mut existing_mods = HashSet::new();
for line in reader.lines() {
let line = line?;
if let Some(mod_name) = MOD_REGEX.captures(&line) {
existing_mods.insert(mod_name["name"].to_string());
}
}
let mut new_mods = vec![];
for entry in std::fs::read_dir(&mod_dir).expect("读取目录失败") {
let path = entry?.path();
if path.file_name().map_or(false, |v| v == "mod.rs") {
continue;
}
if path.is_file()
&& path.extension().map_or(false, |e| e == "rs")
&& let Some(mod_name) = path.file_stem().map(|v| v.to_string_lossy().to_string())
&& !existing_mods.contains(&mod_name)
{
new_mods.push(format!("pub mod {};", mod_name));
}
if path.is_dir()
&& path.join("mod.rs").exists()
&& let Some(mod_name) = path.file_name().map(|v| v.to_string_lossy().to_string())
&& !existing_mods.contains(&mod_name)
{
new_mods.push(format!("pub mod {};", mod_name));
}
}
if new_mods.is_empty() {
debug!("没有需要新增的 mod");
return Ok(());
}
add_mods_into_mod_rs(&mod_rs_path, new_mods)?;
Ok(())
}
fn add_mods_into_mod_rs(mod_rs_path: &Path, new_mods: Vec<String>) -> Result<()> {
let mod_content = std::fs::read_to_string(&mod_rs_path)?;
let mut updated_mod_content = mod_content.clone();
let mod_backup_filepath = mod_rs_path.with_extension("rs.bak");
std::fs::copy(&mod_rs_path, &mod_backup_filepath)?;
let mut mod_rs_file = std::fs::File::create(&mod_rs_path)?;
let mods: Vec<_> = MOD_REGEX.captures_iter(&mod_content).collect();
let mods_end = mods
.last()
.map(|last_match| last_match.get(0).map(|c| c.end() + 1))
.flatten()
.unwrap_or_else(|| mod_content.len());
for new_mod in new_mods {
updated_mod_content.insert_str(mods_end, &format!("{}\n", new_mod));
}
mod_rs_file.write_all(updated_mod_content.as_bytes())?;
std::fs::remove_file(&mod_backup_filepath)?;
let _ = Command::new("rustfmt")
.arg("+nightly")
.arg(mod_rs_path.to_string_lossy().to_string())
.args(vec!["--edition", "2024"])
.status();
Ok(())
}