Splited the single file into a set od modules.
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
/target
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
#
|
||||||
|
# already existing elements were commented out
|
||||||
|
|
||||||
|
#/target
|
||||||
Generated
+5
@@ -0,0 +1,5 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 已忽略包含查询文件的默认文件夹
|
||||||
|
/queries/
|
||||||
Generated
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MaterialThemeProjectNewConfig">
|
||||||
|
<option name="metadata">
|
||||||
|
<MTProjectMetadataState>
|
||||||
|
<option name="userId" value="-6ab6e062:19d034dfda5:-7ffd" />
|
||||||
|
</MTProjectMetadataState>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/BambooDragonfly.iml" filepath="$PROJECT_DIR$/.idea/BambooDragonfly.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+7
@@ -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"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "BambooDragonfly"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
+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