use serde::Serialize; use std::sync::{OnceLock, RwLock}; #[derive(Debug, Clone, Serialize)] pub struct InitErrorPayload { pub path: String, pub error: String, /// 错误类别。`Some("db_version_too_new")` 表示数据库版本过新(应用过旧), /// 前端据此展示「升级应用」恢复界面而非直接退出。 #[serde(skip_serializing_if = "Option::is_none")] pub kind: Option, /// 磁盘上数据库的 user_version(数据库版本过新时填充) #[serde(skip_serializing_if = "Option::is_none")] pub db_version: Option, /// 当前应用支持的 SCHEMA_VERSION(数据库版本过新时填充)。 /// 当升级到最新版后 db_version 仍 > supported_version,说明可能由第三方客户端创建。 #[serde(skip_serializing_if = "Option::is_none")] pub supported_version: Option, } static INIT_ERROR: OnceLock>> = OnceLock::new(); fn cell() -> &'static RwLock> { INIT_ERROR.get_or_init(|| RwLock::new(None)) } pub fn set_init_error(payload: InitErrorPayload) { #[allow(clippy::unwrap_used)] if let Ok(mut guard) = cell().write() { *guard = Some(payload); } } pub fn get_init_error() -> Option { cell().read().ok()?.clone() } // ============================================================ // 迁移结果状态 // ============================================================ static MIGRATION_SUCCESS: OnceLock> = OnceLock::new(); fn migration_cell() -> &'static RwLock { MIGRATION_SUCCESS.get_or_init(|| RwLock::new(false)) } pub fn set_migration_success() { if let Ok(mut guard) = migration_cell().write() { *guard = true; } } /// 获取并消费迁移成功状态(只返回一次 true,之后返回 false) pub fn take_migration_success() -> bool { if let Ok(mut guard) = migration_cell().write() { let val = *guard; *guard = false; val } else { false } } // ============================================================ // Skills SSOT 迁移结果状态 // ============================================================ #[derive(Debug, Clone, Serialize)] pub struct SkillsMigrationPayload { pub count: usize, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } static SKILLS_MIGRATION_RESULT: OnceLock>> = OnceLock::new(); fn skills_migration_cell() -> &'static RwLock> { SKILLS_MIGRATION_RESULT.get_or_init(|| RwLock::new(None)) } pub fn set_skills_migration_result(count: usize) { if let Ok(mut guard) = skills_migration_cell().write() { *guard = Some(SkillsMigrationPayload { count, error: None }); } } pub fn set_skills_migration_error(error: String) { if let Ok(mut guard) = skills_migration_cell().write() { *guard = Some(SkillsMigrationPayload { count: 0, error: Some(error), }); } } /// 获取并消费 Skills 迁移结果(只返回一次 Some,之后返回 None) pub fn take_skills_migration_result() -> Option { if let Ok(mut guard) = skills_migration_cell().write() { guard.take() } else { None } } #[cfg(test)] mod tests { use super::*; #[test] fn init_error_roundtrip() { let payload = InitErrorPayload { path: "/tmp/config.json".into(), error: "broken json".into(), kind: None, db_version: None, supported_version: None, }; set_init_error(payload.clone()); let got = get_init_error().expect("should get payload back"); assert_eq!(got.path, payload.path); assert_eq!(got.error, payload.error); } }