feat(init): 支持补齐内置模板空目录
新增 init 子命令,根据项目中的 world_*_packs.json 解析实际包目录并创建标准空目录。 改用 .empty-dirs 维护内置模板空目录清单,删除会污染用户项目和网易 Bedrock 加载流程的 .gitkeep 占位文件。
This commit is contained in:
67
src/commands/init.rs
Normal file
67
src/commands/init.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use std::fs;
|
||||
|
||||
use crate::commands::InitArgs;
|
||||
use crate::entity;
|
||||
use crate::error::{CliError, Result};
|
||||
use crate::utils::file;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/embedded_examples.rs"));
|
||||
|
||||
pub fn execute(args: &InitArgs) {
|
||||
if let Err(e) = run_init(args) {
|
||||
eprintln!("❌ 项目初始化失败: {}", e);
|
||||
return;
|
||||
}
|
||||
println!("🍀 项目初始化完成");
|
||||
}
|
||||
|
||||
fn run_init(args: &InitArgs) -> Result<()> {
|
||||
let project_dir = file::find_project_dir(&args.path)?;
|
||||
let target = args.target.as_deref().unwrap_or("default");
|
||||
let empty_dirs = lookup_empty_dirs(target)?;
|
||||
let release_info = entity::get_current_release_info(&project_dir)?;
|
||||
|
||||
let behavior_pack = format!("behavior_pack_{}", release_info.behavior_identifier);
|
||||
let resource_pack = format!("resource_pack_{}", release_info.resource_identifier);
|
||||
|
||||
let mut created = 0usize;
|
||||
let mut skipped = 0usize;
|
||||
|
||||
for rel in empty_dirs {
|
||||
let rewritten = rewrite_pack_path(rel, &behavior_pack, &resource_pack);
|
||||
let abs = project_dir.join(&rewritten);
|
||||
if abs.is_dir() {
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
fs::create_dir_all(&abs).map_err(|e| file::io_error("创建目录", &abs, e))?;
|
||||
println!("📁 创建目录: {}", rewritten);
|
||||
created += 1;
|
||||
}
|
||||
|
||||
println!("✅ 新建 {} 个目录, 跳过 {} 个已存在目录", created, skipped);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lookup_empty_dirs(target: &str) -> Result<&'static [&'static str]> {
|
||||
EMBEDDED_EXAMPLE_EMPTY_DIRS
|
||||
.iter()
|
||||
.find(|e| e.example == target)
|
||||
.map(|e| e.dirs)
|
||||
.ok_or_else(|| {
|
||||
CliError::NotFound(format!(
|
||||
"example target '{}' was not found in the built-in examples",
|
||||
target
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn rewrite_pack_path(rel: &str, behavior: &str, resource: &str) -> String {
|
||||
if let Some(rest) = rel.strip_prefix("behavior_pack/") {
|
||||
format!("{}/{}", behavior, rest)
|
||||
} else if let Some(rest) = rel.strip_prefix("resource_pack/") {
|
||||
format!("{}/{}", resource, rest)
|
||||
} else {
|
||||
rel.to_string()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use clap::{Args, Parser, Subcommand, arg};
|
||||
|
||||
pub mod components;
|
||||
pub mod create;
|
||||
pub mod init;
|
||||
pub mod release;
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -24,6 +25,8 @@ pub enum Commands {
|
||||
Release(ReleaseArgs),
|
||||
/// Create a new mod project
|
||||
Create(CreateArgs),
|
||||
/// Initialize standard empty directories for an existing project
|
||||
Init(InitArgs),
|
||||
/// Create a new component
|
||||
Components(ComponentsArgs),
|
||||
}
|
||||
@@ -52,6 +55,16 @@ pub struct CreateArgs {
|
||||
pub target: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct InitArgs {
|
||||
/// The path of the project (default: current directory)
|
||||
#[arg(short, long)]
|
||||
pub path: Option<String>,
|
||||
/// Example target whose layout to apply
|
||||
#[arg(short, long)]
|
||||
pub target: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct ComponentsArgs {
|
||||
/// The path of the project
|
||||
|
||||
@@ -12,6 +12,7 @@ fn main() {
|
||||
match &cli.command {
|
||||
Commands::Release(args) => commands::release::execute(args),
|
||||
Commands::Create(args) => commands::create::execute(args),
|
||||
Commands::Init(args) => commands::init::execute(args),
|
||||
Commands::Components(args) => commands::components::execute(args),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user