diff --git a/CHANGELOG.md b/CHANGELOG.md index 23cf8ae..8d60e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,17 +5,32 @@ All notable changes to this project will be documented in this file. See [conven --- ## [unreleased] +### Features + +- support standard input to base64 encoding - ([5758091](https://github.com/will-we/rust-cli/commit/5758091c25de2e33e928764c16eb19e3cdaf6c04)) - will-we +- support standard input to base64 decoding - ([98ff633](https://github.com/will-we/rust-cli/commit/98ff633718a5986603c1d0f3aac8fa91f1b4c7a2)) - will-we + +### Other + +- add base64 crate - ([61800ec](https://github.com/will-we/rust-cli/commit/61800ecc5c3ed215c1dceafd22ef372f7274261a)) - will-we + +--- +## [0.0.4](https://github.com/will-we/rust-cli/compare/v0.0.3..v0.0.4) - 2024-09-21 + ### Bug Fixes -- remove unused log and skip cargo deny check - ([48fd8aa](https://github.com/will-we/rust-cli/commit/48fd8aadc52114f50f5a9bcd546340ba51738bc9)) - will-we +- remove unused log and skip cargo deny check - ([a92dce3](https://github.com/will-we/rust-cli/commit/a92dce36c127c86d10ad60b2350e9b7c7cbc8b82)) - will-we +- fix:repository url error - ([2f7ed74](https://github.com/will-we/rust-cli/commit/2f7ed741b3590939d3682f0371d6c0d31bd5bd33)) - will-we +- fix:support any csv file to convert to json - ([28bda57](https://github.com/will-we/rust-cli/commit/28bda5778448115b9ad5eccecf276b59a76546a6)) - will-we +- fix:updated version of workflow - ([4265f5b](https://github.com/will-we/rust-cli/commit/4265f5bb5ad790ba2b189ce5cddd8532c6bd89f2)) - will-we ### Documentation -- doc:add CHANGELOG - ([99972cd](https://github.com/will-we/rust-cli/commit/99972cde6f2852a8719562f21eb2677a2189ad10)) - will-we +- doc:add CHANGELOG - ([305a417](https://github.com/will-we/rust-cli/commit/305a4179ba36e00984b05f27ee416edc953093e8)) - will-we ### Features -- support for random passwords - ([2b0e621](https://github.com/will-we/rust-cli/commit/2b0e62178f3bec4f72938c686c68058389d4ecb4)) - will-we +- support for random passwords - ([42500e9](https://github.com/will-we/rust-cli/commit/42500e97612186314a17e4e19f6b650d57db22f3)) - will-we ### Other diff --git a/Cargo.lock b/Cargo.lock index f8d5539..488b947 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,12 @@ version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "byteorder" version = "1.5.0" @@ -239,6 +245,7 @@ name = "rust-cli" version = "0.1.0" dependencies = [ "anyhow", + "base64", "clap", "csv", "rand", diff --git a/Cargo.toml b/Cargo.toml index 8a2d32d..636721b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ csv = "1.3.0" serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" rand = "0.8.5" +base64 = "0.22.1" diff --git a/README.md b/README.md index 4c5ac7e..07f306d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Usage -``` +```bash rust-cli [options] ``` @@ -13,7 +13,9 @@ rust-cli [options] - `csv`: CSV file processing tool. CSV file can be converted to JSON or YAML format. - `json`: JSON file processing tool. JSON file can be converted to CSV or YAML format. - `random`: Generate random JSON data. +- `base64`: Base64 encoding and decoding tool. + ## Options - `-h, --help`: Show help. -- `-v, --version`: Show version. \ No newline at end of file +- `-v, --version`: Show version. diff --git a/src/cli/base64.rs b/src/cli/base64.rs new file mode 100644 index 0000000..bd24196 --- /dev/null +++ b/src/cli/base64.rs @@ -0,0 +1,66 @@ +use clap::Parser; +use std::fmt; +use std::str::FromStr; + +use super::validate_path; + +#[derive(Debug, Parser)] +pub enum Base64SubCommand { + #[command(name = "encode", about = "将文件或字符串编码为base64")] + Encode(Base64EncodeOpts), + #[command(name = "decode", about = "将base64编码的字符串或文件解码")] + Decode(Base64DecodeOpts), +} + +#[derive(Debug, Parser)] +pub struct Base64EncodeOpts { + #[arg(short, long, value_parser = validate_path, default_value = "-")] + pub input: String, + #[arg(long, value_parser = parse_base64_format, default_value = "standard")] + pub format: Base64Format, +} + +#[derive(Debug, Parser)] +pub struct Base64DecodeOpts { + #[arg(short, long, value_parser = validate_path, default_value = "-")] + pub input: String, + #[arg(long, value_parser = parse_base64_format, default_value = "standard")] + pub format: Base64Format, +} + +#[derive(Debug, Clone, Copy)] +pub enum Base64Format { + Standard, + UrlSafe, +} + +fn parse_base64_format(format: &str) -> Result { + format.parse() +} + +impl FromStr for Base64Format { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "standard" => Ok(Base64Format::Standard), + "urlsafe" => Ok(Base64Format::UrlSafe), + _ => Err(anyhow::anyhow!("Invalid format")), + } + } +} + +impl From for &'static str { + fn from(value: Base64Format) -> Self { + match value { + Base64Format::Standard => "standard", + Base64Format::UrlSafe => "urlsafe", + } + } +} + +impl fmt::Display for Base64Format { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", Into::<&str>::into(*self)) + } +} diff --git a/src/cli/csv.rs b/src/cli/csv.rs new file mode 100644 index 0000000..65e333a --- /dev/null +++ b/src/cli/csv.rs @@ -0,0 +1,22 @@ +use clap::Parser; + +use super::validate_path; + +#[derive(Parser, Debug)] +pub struct CsvOpts { + /// 文件输入的路径,必填 + #[arg(short, long, required = true, value_parser = validate_path)] + pub input: String, + + /// 文件输入的路径,默认 output.json + #[arg(short, long, default_value = "output.json")] + pub output: String, + + /// 文件分隔符,默认"," + #[arg(short, long, default_value = ",")] + pub delimiter: String, + + /// 是否有标题行,默认true + #[arg(long, default_value_t = true)] + pub header: bool, +} diff --git a/src/cli/genpass.rs b/src/cli/genpass.rs new file mode 100644 index 0000000..d5f5f37 --- /dev/null +++ b/src/cli/genpass.rs @@ -0,0 +1,19 @@ +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct GenPassOpts { + #[arg(short, long, default_value_t = 16)] + pub length: u8, + + #[arg(long, default_value_t = true)] + pub uppercase: bool, + + #[arg(long, default_value_t = true)] + pub lowercase: bool, + + #[arg(long, default_value_t = true)] + pub number: bool, + + #[arg(long, default_value_t = true)] + pub symbol: bool, +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..1f48f88 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,27 @@ +use std::path; + +pub mod base64; +pub mod csv; +pub mod genpass; +pub mod opts; + +/// 自定义参数校验器: 校验文件路径是否存在 +pub fn validate_path(path: &str) -> Result { + if path == "-" || path::Path::new(path).exists() { + Ok(path.to_string()) + } else { + Err(format!("{} 文件不存在", path)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_validate_path() { + assert_eq!(validate_path("Cargo.toml").unwrap(), "Cargo.toml"); + assert_eq!(validate_path("-").unwrap(), "-"); + assert!(validate_path("not_exist.txt").is_err()); + } +} diff --git a/src/cli/opts.rs b/src/cli/opts.rs new file mode 100644 index 0000000..5e6f60a --- /dev/null +++ b/src/cli/opts.rs @@ -0,0 +1,21 @@ +use crate::base64::Base64SubCommand; +use crate::csv::CsvOpts; +use crate::genpass::GenPassOpts; +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(name = "rust-cli", version, about="rust命令行工具", long_about = None)] +pub struct Opts { + #[command(subcommand)] + pub cmd: SubCommand, +} + +#[derive(Parser, Debug)] +pub enum SubCommand { + #[command(name = "csv", about = "读取csv文件并默认输出json文件")] + Csv(CsvOpts), + #[command(name = "gen-pass", about = "生成随机密码")] + GenPass(GenPassOpts), + #[command(subcommand, about = "base64编解码")] + Base64(Base64SubCommand), +} diff --git a/src/lib.rs b/src/lib.rs index 171fc87..6fc53fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ -mod opts; +mod cli; -pub use opts::Opts; +pub use cli::{base64, csv, genpass, opts}; diff --git a/src/main.rs b/src/main.rs index d5dee91..5d17b13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ -mod opts; - -use crate::opts::{Opts, SubCommand}; +use base64::engine::general_purpose::URL_SAFE; +use base64::prelude::BASE64_STANDARD; +use base64::Engine; use clap::Parser; use csv::Reader; use rand::Rng; +use rust_cli::base64::{Base64Format, Base64SubCommand}; +use rust_cli::opts::{Opts, SubCommand}; use serde_json::Value; use std::fs; +use std::io::stdin; /// rust-li csv -i input.csv -o output.json -d "," fn main() -> anyhow::Result<()> { @@ -37,6 +40,46 @@ fn main() -> anyhow::Result<()> { println!("Password: {}", pass); Ok(()) } + SubCommand::Base64(base64_sub_command) => match base64_sub_command { + Base64SubCommand::Encode(opts) => { + println!("Encoding: {:?}", opts); + let mut std_in_str = String::new(); + stdin().read_line(&mut std_in_str)?; + // 优化去掉/r/n + let input_str = std_in_str.trim_end(); + //打印输入的字符串 + print!("Input: {:?} ", input_str); + //编码 + let mut buf = String::new(); + match opts.format { + Base64Format::Standard => { + BASE64_STANDARD.encode_string(input_str, &mut buf); + } + Base64Format::UrlSafe => { + URL_SAFE.encode_string(input_str, &mut buf); + } + } + println!("Encoded: {}", buf); + Ok(()) + } + Base64SubCommand::Decode(opts) => { + println!("Decoding: {:?}", opts); + let mut std_in_str = String::new(); + stdin().read_line(&mut std_in_str)?; + // 优化去掉/r/n + let input_str = std_in_str.trim_end(); + //打印输入的字符串 + print!("Input: {:?} ", input_str); + //编码 + let decoded = match opts.format { + Base64Format::Standard => BASE64_STANDARD.decode(input_str)?, + Base64Format::UrlSafe => URL_SAFE.decode(input_str)?, + }; + let decoded_str = String::from_utf8(decoded)?; + println!("Decoded: {}", decoded_str); + Ok(()) + } + }, } } diff --git a/src/opts.rs b/src/opts.rs deleted file mode 100644 index 9806dbb..0000000 --- a/src/opts.rs +++ /dev/null @@ -1,63 +0,0 @@ -use clap::Parser; -use std::path; - -#[derive(Parser, Debug)] -#[command(name = "rust-cli", version, about="rust命令行工具", long_about = None)] -pub struct Opts { - #[command(subcommand)] - pub(crate) cmd: SubCommand, -} - -#[derive(Parser, Debug)] -pub enum SubCommand { - #[command(name = "csv", about = "读取csv文件并默认输出json文件")] - Csv(CsvOpts), - #[command(name = "gen-pass", about = "生成随机密码")] - GenPass(GenPassOpts), -} - -#[derive(Debug, Parser)] -pub struct GenPassOpts { - #[arg(short, long, default_value_t = 16)] - pub length: u8, - - #[arg(long, default_value_t = true)] - pub uppercase: bool, - - #[arg(long, default_value_t = true)] - pub lowercase: bool, - - #[arg(long, default_value_t = true)] - pub number: bool, - - #[arg(long, default_value_t = true)] - pub symbol: bool, -} - -#[derive(Parser, Debug)] -pub struct CsvOpts { - /// 文件输入的路径,必填 - #[arg(short, long, required = true, value_parser = validate_path)] - pub input: String, - - /// 文件输入的路径,默认 output.json - #[arg(short, long, default_value = "output.json")] - pub output: String, - - /// 文件分隔符,默认"," - #[arg(short, long, default_value = ",")] - pub delimiter: String, - - /// 是否有标题行,默认true - #[arg(long, default_value_t = true)] - pub header: bool, -} - -/// 自定义参数校验器: 校验文件路径是否存在 -fn validate_path(path: &str) -> Result { - if path::Path::new(path).exists() { - Ok(path.to_string()) - } else { - Err(format!("{} 文件不存在", path)) - } -}