diff --git a/Cargo.toml b/Cargo.toml index 741bcd1..006a0f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,5 @@ winres = "0.1.0" winapi = { version = "0.3", features = ["winnt"] } [dependencies] +blake3 = "1.5.1" toml = "0.8" diff --git a/src/config.rs b/src/config.rs index 04d537b..3203f02 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,13 +5,25 @@ use toml::{from_str, Value as TomlValue}; pub const HELP_MESSAGE_EN: &str = r#"call [options] [--] [arguments] Options: - --hide Hide console window (default) + --hide Hide console window + -h Same as --hide + --show Show console window + -s Same as --show + + --verbose Show more information + -v Same as --verbose + --chdir=xxx Change working directory to xxx + -cd=xxx Same as --chdir=xxx + --bin=xxx Specify executable file + --config=xxx Specify configuration file + -cfg=xxx Same as --config=xxx + --help Print this help message(based on system language) - --help-zh Print this help message(but in Chinese) + --help-zh Print this help message(用中文) --help-en Print this help message Defaults: hide console @@ -21,14 +33,25 @@ Defaults: pub const HELP_MESSAGE_ZH: &str = r#"call [选项] [--] [参数] 选项: - --hide 隐藏控制台窗口 (默认) + --hide 隐藏控制台窗口 + -h 同 --hide + --show 显示控制台窗口 + -s 同 --show + + --verbose 显示更多信息 + -v 同 --verbose + --chdir=xxx 切换工作目录到 xxx + -cd=xxx 同 --chdir=xxx + --bin=xxx 指定可执行文件 --config=xxx 指定配置文件 + -cfg=xxx 同 --config=xxx + --help 输出这一堆东西(根据系统语言) --help-zh 输出这一堆东西 - --help-en 输出这一堆东西(但是英文) + --help-en 输出这一堆东西(In English) 默认: 隐藏控制台 切换工作目录到 ./lib @@ -36,6 +59,8 @@ pub const HELP_MESSAGE_ZH: &str = r#"call [选项] [--] [参数] "#; pub fn show_help() { + #[cfg(windows)] + crate::win::attach_console(); println!("version: {}", crate::VERSION); match std::env::var("LANG") { Ok(lang) => { @@ -52,41 +77,80 @@ pub fn show_help() { } } -pub fn get_default_config() -> (bool, Option, String, String, String) { - let hard_default = ( - true, - Some("lib".to_string()), - "./main".to_string(), - "run.conf".to_string(), - "".to_string(), - ); - - hard_default -} - #[derive(Clone)] pub struct RawConfig { pub show_console: Option, + pub verbose: Option, pub chdir: Option, pub bin: Option, pub bin_arg: Option, + pub config: Option, } impl Display for RawConfig { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "RawConfig {{ show_console: {:?}, chdir: {:?}, bin: {:?}, bin_arg: {:?} }}", - self.show_console, self.chdir, self.bin, self.bin_arg - )) + f.write_str("RawConfig {\n")?; + let _: std::fmt::Result = { + f.write_str(&format!(" show_console: {:?}\n", self.show_console))?; + f.write_str(&format!(" verbose: {:?}\n", self.verbose))?; + f.write_str(&format!(" chdir: {:?}\n", self.chdir))?; + f.write_str(&format!(" bin: {:?}\n", self.bin))?; + f.write_str(&format!(" bin_arg: {:?}\n", self.bin_arg))?; + f.write_str(&format!(" config: {:?}\n", self.config))?; + Ok(()) + }; + f.write_str("}") } } +impl Default for RawConfig { + fn default() -> Self { + RawConfig { + show_console: Some(true), + verbose: Some(false), + chdir: Some("lib".to_string()), + bin: Some("./main".to_string()), + bin_arg: Some("".to_string()), + config: Some("run.conf".to_string()), + } + } +} + +/// 用于合并 +/// +/// CLI 选项 +/// CLI 指定的配置文件 +/// 默认路径的配置文件 +/// executable 的 builtin 配置 (附带在二进制文件中) (可修改) +/// 最基本的默认配置 impl RawConfig { + pub fn new( + show_console: Option, + verbose: Option, + chdir: Option, + bin: Option, + bin_arg: Option, + config: Option, + ) -> Self { + RawConfig { + show_console, + verbose, + chdir, + bin, + bin_arg, + config, + } + } + + /// 从命令行参数中获取配置 + /// 包括命令行参数中指定的配置文件 pub fn from_cli() -> Self { let mut show_console = None; + let mut verbose = None; let mut chdir = None; let mut bin = None; let mut bin_arg = None; + let mut config = None; let args: Vec = std::env::args().collect(); let index = args.iter().position(|x| x == "--"); if index.is_some() { @@ -97,22 +161,72 @@ impl RawConfig { break; } else if args[i] == "--hide" { show_console = Some(false); - } else if args[i] == "--show" { - show_console = Some(true); + } else if args[i] == "-h" { + show_console = Some(false); + } else if args[i] == "--verbose" { + verbose = Some(true); + } else if args[i] == "-v" { + verbose = Some(true); + } else if args[i] == "--help" { + show_help(); + std::process::exit(0); + } else if args[i] == "--help-zh" { + #[cfg(windows)] + crate::win::attach_console(); + println!("{}", HELP_MESSAGE_ZH); + std::process::exit(0); + } else if args[i] == "--help-en" { + #[cfg(windows)] + crate::win::attach_console(); + println!("{}", HELP_MESSAGE_EN); + std::process::exit(0); } else if args[i].starts_with("--chdir=") { chdir = Some(args[i][8..].to_string()); } else if args[i].starts_with("--bin=") { bin = Some(args[i][6..].to_string()); + } else if args[i].starts_with("--config=") { + config = Some(args[i][9..].to_string()); } } RawConfig { show_console, + verbose, chdir, bin, bin_arg, + config, } } + /// 从指定的配置文件中更新当前缺少的配置 + pub fn update_from_config(&mut self) { + // 从配置文件中获取配置 + let possible_config: Option = + { Self::from_config(self.config.as_ref().map(|x| PathBuf::from(x))) }; + if possible_config.is_some() { + let config = possible_config.unwrap(); + if self.show_console.is_none() { + self.show_console = config.show_console; + } + if self.chdir.is_none() { + self.chdir = config.chdir; + } + if self.bin.is_none() { + self.bin = config.bin; + } + if self.bin_arg.is_none() { + self.bin_arg = config.bin_arg; + } + if self.config.is_none() { + self.config = config.config; + } + }; + } + + pub fn from_executeable() -> Option { + crate::reader::read_self() + } + pub fn from_config(config_path: Option) -> Option { if config_path.is_none() { let config_path = PathBuf::from("./run.conf"); @@ -128,22 +242,58 @@ impl RawConfig { let config_str = std::fs::read_to_string(config_path).ok()?; let config_value: TomlValue = from_str(&config_str).ok()?; let show_console = config_value.get("show_console").and_then(|x| x.as_bool()); - let chdir = config_value.get("chdir").and_then(|x| x.as_str()).map(|x| x.to_string()); - let bin = config_value.get("bin").and_then(|x| x.as_str()).map(|x| x.to_string()); - let bin_arg = config_value.get("bin_arg").and_then(|x| x.as_str()).map(|x| x.to_string()); + let verbose = config_value.get("verbose").and_then(|x| x.as_bool()); + let chdir = config_value + .get("chdir") + .and_then(|x| x.as_str()) + .map(|x| x.to_string()); + let bin = config_value + .get("bin") + .and_then(|x| x.as_str()) + .map(|x| x.to_string()); + let bin_arg = config_value + .get("bin_arg") + .and_then(|x| x.as_str()) + .map(|x| x.to_string()); + let config = config_value + .get("config") + .and_then(|x| x.as_str()) + .map(|x| x.to_string()); Some(RawConfig { show_console, + verbose, chdir, bin, bin_arg, + config, }) } + + pub fn merge_config(mut self, other: RawConfig) -> Self { + if self.show_console.is_none() { + self.show_console = other.show_console; + } + if self.chdir.is_none() { + self.chdir = other.chdir; + } + if self.bin.is_none() { + self.bin = other.bin; + } + if self.bin_arg.is_none() { + self.bin_arg = other.bin_arg; + } + if self.config.is_none() { + self.config = other.config; + } + self + } } #[derive(Clone)] pub struct Config { pub show_console: bool, + pub verbose: bool, pub chdir: Option, pub bin: String, pub bin_arg: Vec, @@ -154,6 +304,7 @@ impl Display for Config { let mut s = String::new(); s.push_str("Config {\n"); s.push_str(&format!(" show_console: {}\n", self.show_console)); + s.push_str(&format!(" verbose: {}\n", self.verbose)); s.push_str(&format!(" chdir: {:?}\n", self.chdir)); s.push_str(&format!(" bin: {:?}\n", self.bin)); s.push_str(&format!(" bin_arg: {:?}\n", self.bin_arg)); @@ -165,106 +316,24 @@ impl Display for Config { impl Config { pub fn new( show_console: bool, + verbose: bool, chdir: Option, bin: String, bin_arg: Vec, ) -> Self { Config { show_console, + verbose, chdir, bin, bin_arg, } } - pub fn from_config( - config_path: Option, - ) -> Option<(Option, Option, Option, Option)> { - if config_path.is_none() { - // 判断一下 ./run.conf 是否存在 - let config_path = PathBuf::from("./run.conf"); - if config_path.exists() { - return Self::from_config(Some(config_path)); - } - return None; - } - let config_path = config_path.unwrap(); - if !config_path.exists() { - return None; - } - let config_str = std::fs::read_to_string(config_path).unwrap(); - let config_value: TomlValue = from_str(&config_str).unwrap(); - let show_console = config_value.get("show_console").and_then(|x| x.as_bool()); - let chdir = config_value.get("chdir").and_then(|x| x.as_str()).map(|x| x.to_string()); - let bin = config_value.get("bin").and_then(|x| x.as_str()).map(|x| x.to_string()); - let arg = config_value.get("bin_arg").and_then(|x| x.as_str()).map(|x| x.to_string()); - Some((show_console, chdir, bin, arg)) - } + pub fn from_cli() -> Self { + let cli_conf = RawConfig::from_cli(); + let execueable_conf = RawConfig::from_executeable(); - pub fn from_cli() -> Option { - let mut show_console = None; - let mut chdir: Option = None; - let mut bin: Option = None; - let mut config: Option = None; - // -- 表示后面的参数都是可执行文件的参数 - let args: Vec = std::env::args().collect(); - // 先检查有没有 --help - if args.contains(&"--help".to_string()) { - show_help(); - return None; - } - - let index = args.iter().position(|x| x == "--"); - let bin_arg: Option>; - if index.is_some() { - bin_arg = Some(args[index.unwrap() + 1..].to_vec()); - } else { - bin_arg = None; - } - // 先尝试获取指定的控制台参数 - // --hide 表示隐藏控制台 - // --show 表示显示控制台 - // --chdir 表示切换工作目录 - // --chdir=xxx 表示切换工作目录到xxx - // --bin=xxx 表示指定可执行文件 - // --config=xxx 表示指定配置文件 - // --help 输出上面这一堆东西 - for i in 1..args.len() { - if args[i] == "--" { - break; - } else if args[i] == "--hide" { - show_console = Some(false); - } else if args[i] == "--show" { - show_console = Some(true); - } else if args[i].starts_with("--chdir=") { - chdir = Some(args[i][8..].to_string()); - } else if args[i].starts_with("--bin=") { - bin = Some(args[i][6..].to_string()); - } else if args[i].starts_with("--config=") { - config = Some(args[i][9..].to_string()); - } - } - let default_conf: (bool, Option, String, String, String) = get_default_config(); - let conf_from_config = Self::from_config( - config - .or(Some(default_conf.3.clone())) - .map(|x| PathBuf::from(x)), - ); - // 优先顺序: cli > config > default - if let Some(conf) = conf_from_config { - Some(Self::new( - show_console.unwrap_or(conf.0.unwrap_or(default_conf.0)), - chdir.or(conf.1.or(default_conf.1)), - bin.unwrap_or(conf.2.unwrap_or(default_conf.2)), - bin_arg.unwrap_or(vec![conf.3.unwrap_or(default_conf.3)]), - )) - } else { - Some(Self::new( - show_console.unwrap_or(default_conf.0), - chdir.or(default_conf.1), - bin.unwrap_or(default_conf.2), - bin_arg.unwrap_or(vec![default_conf.3]), - )) - } + todo!("from_cli") } } diff --git a/src/main.rs b/src/main.rs index 60ab2b5..0f9fe2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,21 +7,24 @@ mod other; #[cfg(windows)] mod win; -mod reader; mod config; +mod reader; pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); pub const SHOW_CONSOLE: bool = false; fn main() { + #[cfg(windows)] + win::init(); + let config = config::Config::from_cli(); - if config.is_none() { - return; - } - let config = config.unwrap(); // 输出相关信息 - println!("call {}", VERSION); - println!("config: {}", config); + if config.verbose { + #[cfg(windows)] + win::attach_console(); + println!("call {}", VERSION); + println!("config: {}", config); + } // 运行 #[cfg(windows)] win::run(&config); diff --git a/src/reader.rs b/src/reader.rs index 89c2a74..d0df92b 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -2,15 +2,38 @@ //! 1. 读取自身的信息 //! 2. 读取自身的配置文件 //! 3. 读取自身的命令行参数 -//! +//! //! 大概就这样 -//! +//! -pub fn merge_self_conf() { - +use blake3::Hasher; + +pub fn read_self() -> Option { + // 先校验最后部分是否为合法的校验码 + let mut verify = Hasher::new(); + + let raw_data = read_self_raw()?; + let raw_data_len = raw_data.len(); + + let (data, verify_bytes) = raw_data.split_at(raw_data_len - 32); + verify.update(data); + + let verify_data = verify.finalize(); + if verify_data.as_bytes() != verify_bytes { + println!( + "校验码不匹配 {:?} {:?}", + verify_data.as_bytes(), + verify_bytes + ); + return None; + } + + // 然后解析配置文件 + + None } -pub fn read_self() -> Option> { +pub fn read_self_raw() -> Option> { let path = std::env::current_exe().ok()?; std::fs::read(&path).ok() } diff --git a/src/win.rs b/src/win.rs index 46df5a1..1190820 100644 --- a/src/win.rs +++ b/src/win.rs @@ -2,6 +2,8 @@ use crate::config::Config; use std::{os::windows::process::CommandExt, process::Command}; use winapi::um::{processthreadsapi, wincon, winuser}; +pub static mut FROM_CONSOLE: bool = false; + fn is_launched_from_console() -> bool { unsafe { let console_window = wincon::GetConsoleWindow(); @@ -14,7 +16,7 @@ fn is_launched_from_console() -> bool { } } -fn attach_console() { +pub fn attach_console() { unsafe { let _out = wincon::AttachConsole(wincon::ATTACH_PARENT_PROCESS); if _out == 0 { @@ -26,17 +28,25 @@ fn attach_console() { } } +pub fn init() { + unsafe { + FROM_CONSOLE = is_launched_from_console(); + } +} + + pub fn run(config: &Config) { attach_console(); - let started_from_console = is_launched_from_console(); - println!("call {}", crate::VERSION); - println!("config: {}", config); + if config.verbose { + println!("call {}", crate::VERSION); + println!("config: {}", config); + } // 先切换工作目录 if let Some(chdir) = config.chdir.as_ref() { std::env::set_current_dir(chdir).unwrap(); } // 如果从终端启动, 且没指定显示终端, 则隐藏 stdout - if started_from_console { + if unsafe { FROM_CONSOLE } { let child; if config.show_console { println!("show_window with stdout");