Splited the single file into a set od modules.
This commit is contained in:
+195
@@ -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函数,注意:命令依赖数要自动生成
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
+65
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod app;
|
||||
pub mod cmds;
|
||||
mod cfg;
|
||||
mod help;
|
||||
Reference in New Issue
Block a user