feat: CLI 工具 (clap + anyhow)
This commit is contained in:
+102
-2
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user