feat: CLI 工具 (clap + anyhow)

This commit is contained in:
2026-06-16 23:52:57 +08:00
parent 41ef43f038
commit 9f2856a81f
+102 -2
View File
@@ -1,3 +1,103 @@
fn main() { use clap::Parser;
println!("QRGen - 开发中"); use qr_core::qr::{QrCode, QrConfig, VersionMode};
use qr_core::version::EcLevel;
use std::path::Path;
#[derive(Parser)]
#[command(name = "qrgen", about = "QR 码生成器 — 从零手搓的 ISO/IEC 18004 实现")]
struct Args {
/// 要编码的内容
content: String,
/// 输出文件 (.png 或 .svg),不指定则输出终端 ASCII
#[arg(short = 'o', long)]
output: Option<String>,
/// 纠错级别 [L/M/Q/H] [default: M]
#[arg(short = 'l', long, default_value = "M")]
level: String,
/// 手动指定版本 (1-40),不指定则自动选择
#[arg(short = 'v', long)]
version: Option<u8>,
/// 模块像素大小(仅 PNG[default: 4]
#[arg(short = 's', long, default_value = "4")]
size: u8,
/// 白边模块数 [default: 4]
#[arg(short = 'm', long, default_value = "4")]
margin: u8,
/// 反色(黑底白码)
#[arg(long)]
invert: bool,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let level = match args.level.to_uppercase().as_str() {
"L" => EcLevel::L,
"M" => EcLevel::M,
"Q" => EcLevel::Q,
"H" => EcLevel::H,
_ => anyhow::bail!("无效纠错级别: {}。支持 L/M/Q/H", args.level),
};
let version = match args.version {
Some(v) => {
if !(1..=40).contains(&v) {
anyhow::bail!("无效版本号: {}。支持 1-40", v);
}
VersionMode::Fixed(v)
}
None => VersionMode::Auto,
};
let config = QrConfig {
level,
version,
margin: args.margin,
};
let qr = QrCode::encode(&args.content, config)
.map_err(|e| anyhow::anyhow!("编码失败: {}", e))?;
match args.output {
Some(path) => {
let ext = Path::new(&path)
.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
.to_lowercase();
match ext.as_str() {
"png" => {
let bytes = qr.to_png_bytes(args.size);
std::fs::write(&path, bytes)?;
println!("已生成: {} (版本 {}, {}×{} 模块, {} 级纠错)",
path, qr.version.0, qr.size(), qr.size(),
match qr.level {
EcLevel::L => "L",
EcLevel::M => "M",
EcLevel::Q => "Q",
EcLevel::H => "H",
});
}
"svg" => {
let svg = qr.to_svg();
std::fs::write(&path, svg)?;
println!("已生成: {} (版本 {}, SVG 格式)", path, qr.version.0);
}
_ => anyhow::bail!("不支持的文件格式: .{}。支持 .png / .svg", ext),
}
}
None => {
// 终端 ASCII 输出
println!("{}", qr.to_ascii(args.invert));
}
}
Ok(())
} }