From e019ef212cdad29e1fcc422e0a3e51b508bff80d Mon Sep 17 00:00:00 2001 From: Snowflake Date: Wed, 27 May 2026 15:45:10 +0800 Subject: [PATCH] Splited the single file into a set od modules. --- .gitignore | 8 ++ .idea/.gitignore | 5 + .idea/BambooDragonfly.iml | 11 +++ .idea/misc.xml | 10 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ Cargo.lock | 7 ++ Cargo.toml | 6 ++ src/app.rs | 195 ++++++++++++++++++++++++++++++++++++++ src/cfg.rs | 8 ++ src/cmds.rs | 91 ++++++++++++++++++ src/dep.rs | 0 src/help.rs | 65 +++++++++++++ src/lib.rs | 4 + 14 files changed, 424 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/BambooDragonfly.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/app.rs create mode 100644 src/cfg.rs create mode 100644 src/cmds.rs create mode 100644 src/dep.rs create mode 100644 src/help.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5ff07f --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/target + + +# Added by cargo +# +# already existing elements were commented out + +#/target diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..4101633 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 已忽略包含查询文件的默认文件夹 +/queries/ diff --git a/.idea/BambooDragonfly.iml b/.idea/BambooDragonfly.iml new file mode 100644 index 0000000..cf84ae4 --- /dev/null +++ b/.idea/BambooDragonfly.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0be9df2 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..fa17255 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8753d68 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "BambooDragonfly" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..da6b686 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "BambooDragonfly" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..dd21da9 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,195 @@ +use std::collections::HashMap; +use crate::cmds::{Command, args_optional, raw_args}; +use crate::cfg::*; +use crate::help::{build_help, help_entity_type}; + +pub struct App { + name: String, + author: String, + license: String, + aim: String, + note: String, + pub commands: Vec, +} +impl App { + // [AI修改-需学习] 添加辅助方法,通过名称查找命令,减少重复代码 + fn find_command_by_name(&self, name: &str) -> Option<&Command> { + self.commands.iter().find(|cmd| cmd.matches(name)) + } + + pub fn build_help_msg(&self) -> String { + let author = &self.author; + let license = &self.license; + let aim = &self.aim; + let name = &self.name; + + let mut help_msg = String::new(); + help_msg.push_str(&format!("{}\n", family_statement)); + help_msg.push_str(&format!("This App is developed by {} and distributed under {}.\n", author, license)); + help_msg.push_str(&format!("aim: {}\n", aim)); + help_msg.push_str(&format!("usage: {} [...]\n", name)); + help_msg.push_str("operations:\n"); + + for cmd in &self.commands { + let cmd_short_name = cmd.short_name(); + let cmd_long_name = cmd.long_name(); + let args_info = match &cmd.args_optional { + args_optional::Optional => " [args] [optional_arguments]", + args_optional::Required => " ", + args_optional::NoArgs => "", + }; + help_msg.push_str(&format!(" {} {{{} {}}}{} {}\n", + name, cmd_short_name, cmd_long_name, args_info, cmd.description)); + } + + help_msg.push_str("\nuse 'Command {-h --help}' with an operation for available options.\n"); + help_msg + } + + fn app_help_handler(&self, targets: Vec) { + //考虑无参数也就是应该输出app自身的帮助信息 + if targets.len() == 0 { + let help_msg = self.build_help_msg(); + println_compat(&help_msg); + } + for cmd in targets { + //这里应该根据cmd的名称拿到帮助信息 + for cmd_entity in &self.commands { + if cmd_entity.matches(&cmd) { // [AI修改-需学习] 使用matches方法统一匹配 + let help_msg = cmd_entity.build_help_msg(); + println_compat(&help_msg); + } + } + } + } + + + /// 注意逻辑严谨项:依赖不存在的指令,这是有些离谱的,这种去怪神人程序员,没那本事老老实实去用ide的改名功能 + /// 没事别写互相依赖这种,换个方式实现 + /// 我写的逻辑可能有问题,涉及到cmd名字的地方建议用全名 + pub fn run(self) { + //根据依赖command的顺序运行各个command handler + //从环境变量中获取参数 + let mut args = std::env::args().collect::>(); + // 跳过第一个参数(程序名) + if !args.is_empty() { + args.remove(0); + } + let raw_args = raw_args::from(args); + //准备下一步处理:获取是哪个handler及其参数 + let targets = raw_args.into_unit(); + //尝试构建树状依赖关系并解决 + //先同时处理命令不存在和方便后续处理的哈希表 + let mut whole_queue: HashMap, usize)> = HashMap::new(); + let mut notfound_queue: Vec = Vec::new(); + let mut proced_queue: Vec = Vec::new(); + for target in targets { + //检查有没有这个参(-a--a/a //a这种在不在app的参数表里) + if !self.commands.iter().any(|cmd| cmd.matches(&target[0])) { // [AI修改-需学习] 使用matches方法 + notfound_queue.push(target[0].clone()); + continue // [AI修复] 移除无效的remove操作,因为target[0]尚未插入whole_queue + } + whole_queue.insert( + target[0].clone(), (target[1..].to_vec(), 0) + ); + } + //生根,找到并处理零依赖项 + for (cmd_name, (_cmd_args, _)) in &whole_queue { + for cmd_entity in &self.commands { + if cmd_entity.matches(cmd_name) && cmd_entity.critical_dep_command_list.len() == 0 { // [AI修改-需学习] 使用matches方法 + proced_queue.push(cmd_name.clone()); + } + } + } + + //找到依赖项都在已经处理列表中的项,重复此过程,知道全部处理完毕,或者剩下的序列2轮不变 + let mut last_round_queue = Vec::new(); + let mut loop_count = 0;//防止死循环 + loop { + if last_round_queue == proced_queue { + break + } + if loop_count > loop_rounds_limit - 1 { + panic!("Too many loops when handling commands. Check if there's a recursive dep.") + } + for (cmd_name, (_cmd_args, _)) in &whole_queue { + if !proced_queue.contains(cmd_name) { //这些是根部之外的 + //搜索这个名为cmd_name的依赖项并确认是否满足 + for cmd_entity in &self.commands { + if cmd_entity.matches(cmd_name) { // [AI修改-需学习] 使用matches方法 + // 确认依赖项是否满足 + if cmd_entity.critical_dep_command_list.iter().all(|dep_cmd_ref| { + proced_queue.iter().any(|proc_name| dep_cmd_ref.matches(proc_name)) + }) { + // 添加到处理列表 + proced_queue.push(cmd_name.clone()); + } + } + } + } + } + last_round_queue = proced_queue.clone(); + loop_count += 1; + } + //无参数则执行help + if proced_queue.is_empty() { + self.app_help_handler(vec![]); + } + + // 执行所有已处理的命令 + for cmd_name in &proced_queue { + if let Some((cmd_args, _)) = whole_queue.get(cmd_name) { + // 查找对应的命令实体并执行handler + for cmd_entity in &self.commands { + if cmd_entity.matches(cmd_name) { + (cmd_entity.handler)(cmd_args.clone()); + break; + } + } + } + } + + // 报告未找到的命令 + if !notfound_queue.is_empty() { + eprintln!("Error: Unknown commands: {}", notfound_queue.join(", ")); + } + } + + pub fn construct(author: String, license: String, aim: String, name: String) -> App { + App{ + name: name, + author: author, + license: license, + aim: aim, + note: "".to_string(), + commands: Vec::new(), + } + } + + // 添加一个方法来初始化帮助命令 + pub fn init_help(mut self) -> Self { + let app_ref = &self as *const App; // 使用裸指针避免借用检查问题 + + let app_help_handler_wrapper = move |needed_help_args: Vec| { + // 安全地将裸指针转回引用 + let app = unsafe { &*app_ref }; + if needed_help_args.is_empty() { + let help_msg = app.build_help_msg(); + println_compat(&help_msg); + } + for cmd in needed_help_args { + for cmd_entity in &app.commands { + if cmd_entity.matches(&cmd) { + let help_msg = cmd_entity.build_help_msg(); + println_compat(&help_msg); + } + } + } + }; + + let help_command = build_help(help_entity_type::App, Box::new(app_help_handler_wrapper)); + self.commands.insert(0, help_command); // 将帮助命令插入到第一个位置 + self + } + //TODO register_command函数,注意:命令依赖数要自动生成 +} \ No newline at end of file diff --git a/src/cfg.rs b/src/cfg.rs new file mode 100644 index 0000000..d400795 --- /dev/null +++ b/src/cfg.rs @@ -0,0 +1,8 @@ +pub static cmd_flag: &'static str = "-"; +pub static family_statement: &'static str = "<<*>> [-^v-] S.A. SNOWARE Information System Infrastructure Component"; +pub static display_in_a_line_args:usize = 2; +pub static loop_rounds_limit:usize = 200; + +pub fn println_compat(msg: &str) { + println!("{}", msg); +} \ No newline at end of file diff --git a/src/cmds.rs b/src/cmds.rs new file mode 100644 index 0000000..8439d8e --- /dev/null +++ b/src/cmds.rs @@ -0,0 +1,91 @@ +use crate::cfg::cmd_flag; + +pub enum args_optional { + Optional, + Required, + NoArgs +} +pub type args_list_t = Vec<(String, String)>; + +pub struct raw_args { + content: Vec, +} + +impl raw_args { + pub fn from(storage: Vec) -> raw_args { + raw_args{ + content: storage, + } + } + + pub fn into_unit(self) -> Vec> { + let mut stdout :Vec> = Vec::new(); + let mut unit_source :Vec = Vec::new(); + let count = self.content.len(); + let mut proc_ed = 0; + for item in self.content.iter() { + proc_ed = proc_ed + 1; + // 如果当前项以 - 开头且 unit_source 不为空,说明是一个新命令的开始 + if item.starts_with(cmd_flag) && !unit_source.is_empty() { + stdout.push(unit_source.clone()); + unit_source = Vec::new(); + } + unit_source.push(item.clone()); + // 如果是最后一个元素,将当前的 unit_source 加入结果 + if proc_ed == count { + stdout.push(unit_source.clone()); + unit_source = Vec::new(); + } + } + stdout + } +} + +pub struct Command { + pub cmd_ref: CommandRef, + pub args_optional: args_optional, + pub description: String, + pub critical_dep_command_list: Vec, + pub args_min_length: usize, + pub args_list: args_list_t, + pub handler: Box)>, +} + +impl Command { + pub fn long_name(&self) -> &str { + &self.cmd_ref.long_name + } + + pub fn short_name(&self) -> &str { + &self.cmd_ref.short_name + } + + pub fn id(&self) -> &str { + &self.cmd_ref.id + } + + pub fn matches(&self, name: &str) -> bool { + self.cmd_ref.matches(name) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CommandRef { + pub id: String, + pub long_name: String, + pub short_name: String, +} + +impl CommandRef { + pub fn new(id: &str, long_name: &str, short_name: &str) -> Self { + CommandRef { + id: id.to_string(), + long_name: long_name.to_string(), + short_name: short_name.to_string(), + } + } + + pub fn matches(&self, name: &str) -> bool { + self.long_name == name || self.short_name == name + } +} diff --git a/src/dep.rs b/src/dep.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/help.rs b/src/help.rs new file mode 100644 index 0000000..57d7813 --- /dev/null +++ b/src/help.rs @@ -0,0 +1,65 @@ +use crate::cfg::{cmd_flag, display_in_a_line_args}; +use crate::cmds::{args_optional, Command, CommandRef}; + +impl Command { + pub fn build_help_msg(&self) -> String { + let cmd_short_name = self.short_name(); + let cmd_long_name = self.long_name(); + let args_example = match &self.args_optional { + args_optional::Optional => " [optional_arguments]", + args_optional::Required => " ", + args_optional::NoArgs => "", + }; + let fixed_head = format!("type {cmd_flag}h or {cmd_flag}help for the app's information.\n"); + let mutable_head_secondary = format!(r#"Command {{{cmd_short_name} {cmd_long_name}}}{args_example}"#); + let mut args_in_a_line: Vec<(String, String)> = Vec::new(); + let mut detailed_args_doc = String::new(); + for arg in &self.args_list { + if args_in_a_line.len() + 1 < display_in_a_line_args { + args_in_a_line.push(arg.clone()); + } else { + for (arg_name, arg_description) in args_in_a_line.iter() { + detailed_args_doc.push_str(&format!("Arg [{}]: {}", arg_name, arg_description)); + } + detailed_args_doc.push_str("\n"); + args_in_a_line = Vec::new(); + } + } + fixed_head + &mutable_head_secondary + "\n" + &detailed_args_doc + } +} + +pub enum help_entity_type{ + Command, + App, +} + +impl std::fmt::Debug for help_entity_type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + help_entity_type::Command => write!(f, "Command"), + help_entity_type::App => write!(f, "App"), + } + } +} + + + +pub fn build_help(who_type: help_entity_type, help_info_prov: Box)>) -> Command { + let help_info_type = match who_type { + help_entity_type::Command => "Command", + help_entity_type::App => "App", + }; + let id = format!("help_{:?}", who_type); + let long_name = format!("{} help", &help_info_type); + + Command{ + cmd_ref: CommandRef::new(&id, &long_name, "h"), // [AI修改-需学习] 使用CommandRef构造函数 + args_optional: args_optional::NoArgs, + description: format!("Some useful information about this {}.", help_info_type).to_string(), + critical_dep_command_list: Vec::new(), + args_min_length: 0, + args_list: Vec::new(), + handler: help_info_prov + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..660cc2f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub mod app; +pub mod cmds; +mod cfg; +mod help;