Splited the single file into a set od modules.

This commit is contained in:
2026-05-27 15:45:10 +08:00
commit e019ef212c
14 changed files with 424 additions and 0 deletions
+195
View File
@@ -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<Command>,
}
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: {} <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>) {
//考虑无参数也就是应该输出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::<Vec<String>>();
// 跳过第一个参数(程序名)
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<String, (Vec<String>, usize)> = HashMap::new();
let mut notfound_queue: Vec<String> = Vec::new();
let mut proced_queue: Vec<String> = 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<String>| {
// 安全地将裸指针转回引用
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函数,注意:命令依赖数要自动生成
}
+8
View File
@@ -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);
}
+91
View File
@@ -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<String>,
}
impl raw_args {
pub fn from(storage: Vec<String>) -> raw_args {
raw_args{
content: storage,
}
}
pub fn into_unit(self) -> Vec<Vec<String>> {
let mut stdout :Vec<Vec<String>> = Vec::new();
let mut unit_source :Vec<String> = 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<CommandRef>,
pub args_min_length: usize,
pub args_list: args_list_t,
pub handler: Box<dyn Fn(Vec<String>)>,
}
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
}
}
View File
+65
View File
@@ -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 => " <required_arguments>",
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<dyn Fn(Vec<String>)>) -> 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
}
}
+4
View File
@@ -0,0 +1,4 @@
pub mod app;
pub mod cmds;
mod cfg;
mod help;