133 lines
3.4 KiB
Rust
133 lines
3.4 KiB
Rust
use qr_core::qr::{QrCode, QrConfig, VersionMode};
|
||
use qr_core::version::EcLevel;
|
||
use serde::{Deserialize, Serialize};
|
||
use std::sync::Mutex;
|
||
|
||
/// QR 编码响应
|
||
#[derive(Debug, Serialize, Clone)]
|
||
struct QrResponse {
|
||
svg: String,
|
||
version: u8,
|
||
size: u8,
|
||
mask: u8,
|
||
}
|
||
|
||
/// 历史记录条目
|
||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||
struct HistoryEntry {
|
||
id: String,
|
||
mode: String,
|
||
content: String,
|
||
timestamp: u64,
|
||
}
|
||
|
||
/// 应用状态
|
||
struct AppState {
|
||
history: Mutex<Vec<HistoryEntry>>,
|
||
}
|
||
|
||
/// 编码 QR 码,返回 SVG + 元信息
|
||
#[tauri::command]
|
||
fn encode_qr(text: String, level: String, margin: u8) -> Result<QrResponse, String> {
|
||
if margin > 100 {
|
||
return Err("边距过大(最大 100)".into());
|
||
}
|
||
|
||
let ec_level = match level.to_uppercase().as_str() {
|
||
"L" => EcLevel::L,
|
||
"M" => EcLevel::M,
|
||
"Q" => EcLevel::Q,
|
||
"H" => EcLevel::H,
|
||
_ => return Err(format!("无效纠错级别: {}", level)),
|
||
};
|
||
|
||
let config = QrConfig {
|
||
level: ec_level,
|
||
version: VersionMode::Auto,
|
||
margin,
|
||
};
|
||
|
||
let qr = QrCode::encode(&text, config).map_err(|e| format!("编码失败: {}", e))?;
|
||
|
||
let svg = qr.to_svg();
|
||
|
||
Ok(QrResponse {
|
||
svg,
|
||
version: qr.version.0,
|
||
size: qr.size(),
|
||
mask: qr.mask,
|
||
})
|
||
}
|
||
|
||
/// 导出 PNG bytes
|
||
#[tauri::command]
|
||
fn export_png(text: String, level: String, margin: u8, module_size: u8) -> Result<Vec<u8>, String> {
|
||
if margin > 100 {
|
||
return Err("边距过大(最大 100)".into());
|
||
}
|
||
|
||
let ec_level = match level.to_uppercase().as_str() {
|
||
"L" => EcLevel::L,
|
||
"M" => EcLevel::M,
|
||
"Q" => EcLevel::Q,
|
||
"H" => EcLevel::H,
|
||
_ => return Err(format!("无效纠错级别: {}", level)),
|
||
};
|
||
|
||
let config = QrConfig {
|
||
level: ec_level,
|
||
version: VersionMode::Auto,
|
||
margin,
|
||
};
|
||
|
||
let qr = QrCode::encode(&text, config).map_err(|e| format!("编码失败: {}", e))?;
|
||
|
||
qr.to_png_bytes(module_size).map_err(|e| format!("PNG 导出失败: {}", e))
|
||
}
|
||
|
||
/// 保存历史记录条目
|
||
#[tauri::command]
|
||
fn save_history(state: tauri::State<AppState>, entry: HistoryEntry) -> Result<(), String> {
|
||
let mut history = state.history.lock().map_err(|e| e.to_string())?;
|
||
history.push(entry);
|
||
if history.len() > 50 {
|
||
history.remove(0);
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// 加载全部历史记录
|
||
#[tauri::command]
|
||
fn load_history(state: tauri::State<AppState>) -> Result<Vec<HistoryEntry>, String> {
|
||
let history = state.history.lock().map_err(|e| e.to_string())?;
|
||
Ok(history.clone())
|
||
}
|
||
|
||
/// 清空历史记录
|
||
#[tauri::command]
|
||
fn clear_history(state: tauri::State<AppState>) -> Result<(), String> {
|
||
let mut history = state.history.lock().map_err(|e| e.to_string())?;
|
||
history.clear();
|
||
Ok(())
|
||
}
|
||
|
||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||
pub fn run() {
|
||
tauri::Builder::default()
|
||
.plugin(tauri_plugin_store::Builder::new().build())
|
||
.plugin(tauri_plugin_dialog::init())
|
||
.plugin(tauri_plugin_clipboard_manager::init())
|
||
.manage(AppState {
|
||
history: Mutex::new(Vec::new()),
|
||
})
|
||
.invoke_handler(tauri::generate_handler![
|
||
encode_qr,
|
||
export_png,
|
||
save_history,
|
||
load_history,
|
||
clear_history,
|
||
])
|
||
.run(tauri::generate_context!())
|
||
.expect("启动 QRGen GUI 失败");
|
||
}
|