feat: vCard 扩展 + 格式扩展 + 解码透视矫正 — v0.3.0
Phase 1: 格式扩展 - png.rs → image.rs,OutputFormat 枚举 (PNG/BMP/JPEG/WebP) - CLI -f/--format,Web fmt 参数扩展,image crate +bmp feature Phase 2: 解码增强 - 新增 decoder/perspective.rs — 旋转矫正(MVP) - auto_correct: finder 检测→计算旋转角→仿射变换→再解码 - decode_image 自动重试矫正流水线 Phase 3: vCard 扩展 - 新增 5 字段:TITLE/URL/BDAY/NOTE/PHOTO - Rust text_builder + TS qrText + VCardMode UI 同步 - CLI 新增 --title --vcard-url --birthday --note --photo - 中/英 i18n 翻译 测试: 81 Rust + 19 前端全部通过
This commit is contained in:
@@ -8,15 +8,42 @@ pub fn build_wifi_text(ssid: &str, password: &str, encryption: &str, hidden: boo
|
||||
format!("WIFI:T:{encryption};S:{ssid};P:{password};{h};")
|
||||
}
|
||||
|
||||
/// 构造 vCard 字符串
|
||||
/// 构造 vCard 3.0 字符串(含扩展字段)
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_vcard_text(
|
||||
name: &str,
|
||||
phone: &str,
|
||||
email: &str,
|
||||
company: &str,
|
||||
address: &str,
|
||||
title: &str,
|
||||
url: &str,
|
||||
birthday: &str,
|
||||
note: &str,
|
||||
photo: &str,
|
||||
) -> String {
|
||||
format!("BEGIN:VCARD\nVERSION:3.0\nFN:{name}\nTEL:{phone}\nEMAIL:{email}\nORG:{company}\nADR:{address}\nEND:VCARD")
|
||||
let mut s =
|
||||
format!("BEGIN:VCARD\nVERSION:3.0\nFN:{name}\nTEL:{phone}\nEMAIL:{email}\nORG:{company}");
|
||||
if !title.is_empty() {
|
||||
s.push_str(&format!("\nTITLE:{title}"));
|
||||
}
|
||||
if !address.is_empty() {
|
||||
s.push_str(&format!("\nADR:{address}"));
|
||||
}
|
||||
if !url.is_empty() {
|
||||
s.push_str(&format!("\nURL:{url}"));
|
||||
}
|
||||
if !birthday.is_empty() {
|
||||
s.push_str(&format!("\nBDAY:{birthday}"));
|
||||
}
|
||||
if !note.is_empty() {
|
||||
s.push_str(&format!("\nNOTE:{note}"));
|
||||
}
|
||||
if !photo.is_empty() {
|
||||
s.push_str(&format!("\nPHOTO:{photo}"));
|
||||
}
|
||||
s.push_str("\nEND:VCARD");
|
||||
s
|
||||
}
|
||||
|
||||
/// 构造 mailto 链接
|
||||
@@ -71,10 +98,43 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_build_vcard_text() {
|
||||
let text = build_vcard_text("张三", "13800138000", "a@b.com", "公司", "北京");
|
||||
let text = build_vcard_text(
|
||||
"张三",
|
||||
"13800138000",
|
||||
"a@b.com",
|
||||
"公司",
|
||||
"北京",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
);
|
||||
assert!(text.contains("BEGIN:VCARD"));
|
||||
assert!(text.contains("FN:张三"));
|
||||
assert!(text.contains("END:VCARD"));
|
||||
assert!(!text.contains("TITLE:")); // 空字段不输出
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_vcard_full() {
|
||||
let text = build_vcard_text(
|
||||
"张三",
|
||||
"13800138000",
|
||||
"a@b.com",
|
||||
"公司",
|
||||
"北京",
|
||||
"工程师",
|
||||
"https://z.com",
|
||||
"1990-01-01",
|
||||
"备注",
|
||||
"https://z.com/p.jpg",
|
||||
);
|
||||
assert!(text.contains("TITLE:工程师"));
|
||||
assert!(text.contains("URL:https://z.com"));
|
||||
assert!(text.contains("BDAY:1990-01-01"));
|
||||
assert!(text.contains("NOTE:备注"));
|
||||
assert!(text.contains("PHOTO:https://z.com/p.jpg"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user