feat(init): 支持补齐内置模板空目录
新增 init 子命令,根据项目中的 world_*_packs.json 解析实际包目录并创建标准空目录。 改用 .empty-dirs 维护内置模板空目录清单,删除会污染用户项目和网易 Bedrock 加载流程的 .gitkeep 占位文件。
This commit is contained in:
86
build.rs
86
build.rs
@@ -1,8 +1,11 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
env, fs, io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
const EMPTY_DIRS_FILE: &str = ".empty-dirs";
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=examples");
|
||||
|
||||
@@ -12,25 +15,37 @@ fn main() {
|
||||
|
||||
let mut dirs = Vec::new();
|
||||
let mut files = Vec::new();
|
||||
let mut empty_dirs_by_example = BTreeMap::new();
|
||||
|
||||
if examples_root.is_dir() {
|
||||
collect_examples(&examples_root, &examples_root, &mut dirs, &mut files)
|
||||
.expect("failed to collect example templates");
|
||||
collect_examples(
|
||||
&examples_root,
|
||||
&examples_root,
|
||||
&mut dirs,
|
||||
&mut files,
|
||||
&mut empty_dirs_by_example,
|
||||
)
|
||||
.expect("failed to collect example templates");
|
||||
}
|
||||
|
||||
dirs.sort();
|
||||
dirs.dedup();
|
||||
files.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
for empty_dirs in empty_dirs_by_example.values_mut() {
|
||||
empty_dirs.sort();
|
||||
empty_dirs.dedup();
|
||||
}
|
||||
|
||||
let mut generated = String::new();
|
||||
generated.push_str(
|
||||
"struct EmbeddedFile {\n path: &'static str,\n contents: &'static [u8],\n}\n\n",
|
||||
"#[allow(dead_code)]\nstruct EmbeddedFile {\n path: &'static str,\n contents: &'static [u8],\n}\n\n",
|
||||
);
|
||||
generated.push_str("static EMBEDDED_EXAMPLE_DIRS: &[&str] = &[\n");
|
||||
generated.push_str("#[allow(dead_code)]\nstatic EMBEDDED_EXAMPLE_DIRS: &[&str] = &[\n");
|
||||
for dir in &dirs {
|
||||
generated.push_str(&format!(" {:?},\n", dir));
|
||||
}
|
||||
generated.push_str("];\n\n");
|
||||
generated.push_str("static EMBEDDED_EXAMPLE_FILES: &[EmbeddedFile] = &[\n");
|
||||
generated.push_str("#[allow(dead_code)]\nstatic EMBEDDED_EXAMPLE_FILES: &[EmbeddedFile] = &[\n");
|
||||
for (relative_path, absolute_path) in &files {
|
||||
generated.push_str(&format!(
|
||||
" EmbeddedFile {{ path: {:?}, contents: include_bytes!(r#\"{}\"#) }},\n",
|
||||
@@ -38,6 +53,23 @@ fn main() {
|
||||
absolute_path.display()
|
||||
));
|
||||
}
|
||||
generated.push_str("];\n\n");
|
||||
generated.push_str(
|
||||
"#[allow(dead_code)]\nstruct EmbeddedExampleEmptyDirs {\n example: &'static str,\n dirs: &'static [&'static str],\n}\n\n",
|
||||
);
|
||||
generated.push_str(
|
||||
"#[allow(dead_code)]\nstatic EMBEDDED_EXAMPLE_EMPTY_DIRS: &[EmbeddedExampleEmptyDirs] = &[\n",
|
||||
);
|
||||
for (example, empty_dirs) in &empty_dirs_by_example {
|
||||
generated.push_str(&format!(
|
||||
" EmbeddedExampleEmptyDirs {{ example: {:?}, dirs: &[\n",
|
||||
example
|
||||
));
|
||||
for dir in empty_dirs {
|
||||
generated.push_str(&format!(" {:?},\n", dir));
|
||||
}
|
||||
generated.push_str(" ] },\n");
|
||||
}
|
||||
generated.push_str("];\n");
|
||||
|
||||
fs::write(out_file, generated).expect("failed to write embedded example list");
|
||||
@@ -48,6 +80,7 @@ fn collect_examples(
|
||||
dir: &Path,
|
||||
dirs: &mut Vec<String>,
|
||||
files: &mut Vec<(String, PathBuf)>,
|
||||
empty_dirs_by_example: &mut BTreeMap<String, Vec<String>>,
|
||||
) -> io::Result<()> {
|
||||
for entry in fs::read_dir(dir)? {
|
||||
let entry = entry?;
|
||||
@@ -60,11 +93,50 @@ fn collect_examples(
|
||||
|
||||
if path.is_dir() {
|
||||
dirs.push(relative_path);
|
||||
collect_examples(root, &path, dirs, files)?;
|
||||
collect_examples(root, &path, dirs, files, empty_dirs_by_example)?;
|
||||
} else if path.is_file() {
|
||||
files.push((relative_path, path.canonicalize()?));
|
||||
if path.file_name().and_then(|name| name.to_str()) == Some(EMPTY_DIRS_FILE) {
|
||||
collect_empty_dirs(&relative_path, &path, dirs, empty_dirs_by_example)?;
|
||||
} else {
|
||||
files.push((relative_path, path.canonicalize()?));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_empty_dirs(
|
||||
relative_path: &str,
|
||||
path: &Path,
|
||||
dirs: &mut Vec<String>,
|
||||
empty_dirs_by_example: &mut BTreeMap<String, Vec<String>>,
|
||||
) -> io::Result<()> {
|
||||
let (example, file_name) = relative_path.split_once('/').ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("{} must be inside an example directory", EMPTY_DIRS_FILE),
|
||||
)
|
||||
})?;
|
||||
if file_name != EMPTY_DIRS_FILE {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("unexpected {} path: {}", EMPTY_DIRS_FILE, relative_path),
|
||||
));
|
||||
}
|
||||
|
||||
let contents = fs::read_to_string(path)?;
|
||||
let empty_dirs = empty_dirs_by_example
|
||||
.entry(example.to_string())
|
||||
.or_insert_with(Vec::new);
|
||||
for line in contents.lines() {
|
||||
let dir = line.trim();
|
||||
if dir.is_empty() || dir.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
dirs.push(format!("{}/{}", example, dir));
|
||||
empty_dirs.push(dir.to_string());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user