109 lines
3.1 KiB
Rust
109 lines
3.1 KiB
Rust
use clap::Parser;
|
||
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(())
|
||
}
|