This commit is contained in:
2026-05-27 16:43:54 +08:00
parent e019ef212c
commit ab519320ef
4 changed files with 90 additions and 54 deletions
+28 -51
View File
@@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::cmds::{Command, args_optional, raw_args}; use crate::cmds::{Command, args_optional, raw_args};
use crate::cfg::*; use crate::cfg::*;
use crate::help::{build_help, help_entity_type}; use crate::help::{build_help, build_app_help_msg, help_entity_type, app_help_handler, print_command_help};
pub struct App { pub struct App {
name: String, name: String,
@@ -18,57 +18,21 @@ impl App {
} }
pub fn build_help_msg(&self) -> String { pub fn build_help_msg(&self) -> String {
let author = &self.author; build_app_help_msg(&self.name, &self.author, &self.license, &self.aim, &self.commands)
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: {} <operation> [...]\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> <required_arguments>",
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<String>) { fn app_help_handler(&self, targets: Vec<String>) {
//考虑无参数也就是应该输出app自身的帮助信息
if targets.len() == 0 {
let help_msg = self.build_help_msg(); let help_msg = self.build_help_msg();
println_compat(&help_msg); app_help_handler(&help_msg, &self.commands, targets);
}
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的改名功能 /// 注意逻辑严谨项:依赖不存在的指令,这是有些离谱的,这种去怪神人程序员,没那本事老老实实去用ide的改名功能
/// 没事别写互相依赖这种,换个方式实现 /// 没事别写互相依赖这种,换个方式实现
/// 我写的逻辑可能有问题,涉及到cmd名字的地方建议用全名 /// 我写的逻辑可能有问题,涉及到cmd名字的地方建议用全名
pub fn run(self) { /// 构建命令执行顺序
//根据依赖command的顺序运行各个command handler /// 返回: (执行队列, 未找到的命令列表)
fn build_execution_order(&self) -> (Vec<String>, Vec<String>, HashMap<String, Vec<String>>) {
//从环境变量中获取参数 //从环境变量中获取参数
let mut args = std::env::args().collect::<Vec<String>>(); let mut args = std::env::args().collect::<Vec<String>>();
// 跳过第一个参数(程序名) // 跳过第一个参数(程序名)
@@ -116,7 +80,7 @@ impl App {
if !proced_queue.contains(cmd_name) { //这些是根部之外的 if !proced_queue.contains(cmd_name) { //这些是根部之外的
//搜索这个名为cmd_name的依赖项并确认是否满足 //搜索这个名为cmd_name的依赖项并确认是否满足
for cmd_entity in &self.commands { for cmd_entity in &self.commands {
if cmd_entity.matches(cmd_name) { // [AI修改-需学习] 使用matches方法 if cmd_entity.matches(cmd_name) {
// 确认依赖项是否满足 // 确认依赖项是否满足
if cmd_entity.critical_dep_command_list.iter().all(|dep_cmd_ref| { 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.iter().any(|proc_name| dep_cmd_ref.matches(proc_name))
@@ -131,14 +95,26 @@ impl App {
last_round_queue = proced_queue.clone(); last_round_queue = proced_queue.clone();
loop_count += 1; loop_count += 1;
} }
// 提取命令参数映射
let mut cmd_args_map: HashMap<String, Vec<String>> = HashMap::new();
for (cmd_name, (cmd_args, _)) in &whole_queue {
cmd_args_map.insert(cmd_name.clone(), cmd_args.clone());
}
(proced_queue, notfound_queue, cmd_args_map)
}
/// 执行命令队列
fn execute_commands(&self, proced_queue: &[String], cmd_args_map: &HashMap<String, Vec<String>>, notfound_queue: &[String]) {
//无参数则执行help //无参数则执行help
if proced_queue.is_empty() { if proced_queue.is_empty() {
self.app_help_handler(vec![]); self.app_help_handler(vec![]);
} }
// 执行所有已处理的命令 // 执行所有已处理的命令
for cmd_name in &proced_queue { for cmd_name in proced_queue {
if let Some((cmd_args, _)) = whole_queue.get(cmd_name) { if let Some(cmd_args) = cmd_args_map.get(cmd_name) {
// 查找对应的命令实体并执行handler // 查找对应的命令实体并执行handler
for cmd_entity in &self.commands { for cmd_entity in &self.commands {
if cmd_entity.matches(cmd_name) { if cmd_entity.matches(cmd_name) {
@@ -155,6 +131,12 @@ impl App {
} }
} }
pub fn run(self) {
//根据依赖command的顺序运行各个command handler
let (proced_queue, notfound_queue, cmd_args_map) = self.build_execution_order();
self.execute_commands(&proced_queue, &cmd_args_map, &notfound_queue);
}
pub fn construct(author: String, license: String, aim: String, name: String) -> App { pub fn construct(author: String, license: String, aim: String, name: String) -> App {
App{ App{
name: name, name: name,
@@ -178,12 +160,7 @@ impl App {
println_compat(&help_msg); println_compat(&help_msg);
} }
for cmd in needed_help_args { for cmd in needed_help_args {
for cmd_entity in &app.commands { print_command_help(&app.commands, &cmd);
if cmd_entity.matches(&cmd) {
let help_msg = cmd_entity.build_help_msg();
println_compat(&help_msg);
}
}
} }
}; };
+45 -1
View File
@@ -1,4 +1,4 @@
use crate::cfg::{cmd_flag, display_in_a_line_args}; use crate::cfg::{cmd_flag, display_in_a_line_args, family_statement, println_compat};
use crate::cmds::{args_optional, Command, CommandRef}; use crate::cmds::{args_optional, Command, CommandRef};
impl Command { impl Command {
@@ -63,3 +63,47 @@ pub fn build_help(who_type: help_entity_type, help_info_prov: Box<dyn Fn(Vec<Str
handler: help_info_prov handler: help_info_prov
} }
} }
pub fn build_app_help_msg(name: &str, author: &str, license: &str, aim: &str, commands: &[Command]) -> String {
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: {} <operation> [...]\n", name));
help_msg.push_str("operations:\n");
for cmd in 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> <required_arguments>",
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
}
pub fn print_command_help(commands: &[Command], cmd_name: &str) {
for cmd_entity in commands {
if cmd_entity.matches(cmd_name) {
let help_msg = cmd_entity.build_help_msg();
println_compat(&help_msg);
}
}
}
pub fn app_help_handler(app_help_msg: &str, commands: &[Command], targets: Vec<String>) {
//考虑无参数也就是应该输出app自身的帮助信息
if targets.len() == 0 {
println_compat(app_help_msg);
}
for cmd in targets {
//这里应该根据cmd的名称拿到帮助信息
print_command_help(commands, &cmd);
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
pub mod app;
pub mod cmds; pub mod cmds;
mod cfg; mod cfg;
mod help; mod help;
pub mod app;
+15
View File
@@ -0,0 +1,15 @@
//Copyright (c) [2026] [S.A. @SNOWARE SALflake@aliyun.com]
// [swpk::CmdEngine] is licensed under Mulan PubL v2.
// You can use this software according to the terms and conditions of the Mulan PubL v2.
// You may obtain a copy of Mulan PubL v2 at:
// http://license.coscl.org.cn/MulanPubL-2.0
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PubL v2 for more details
// This file has been split into separate modules:
// - cfg.rs: Configuration constants
// - cmds.rs: Command and CommandRef definitions
// - help.rs: Help system implementation
// - app.rs: App structure and logic