* refactor: improve code quality with clippy fixes and standardized logging
- Replace dangerous unwrap()/expect() calls with proper error handling
- Standardize logging from log:: to logging\! macro with Type:: classifications
- Fix app handle panics with graceful fallback patterns
- Improve error resilience across 35+ modules without breaking functionality
- Reduce clippy warnings from 300+ to 0 in main library code
* chore: update Cargo.toml configuration
* refactor: resolve all clippy warnings
- Fix Arc clone warnings using explicit Arc::clone syntax across 9 files
- Add #[allow(clippy::expect_used)] to test functions for appropriate expect usage
- Remove no-effect statements from debug code cleanup
- Apply clippy auto-fixes for dbg\! macro removals and path statements
- Achieve zero clippy warnings on all targets with -D warnings flag
* chore: update Cargo.toml clippy configuration
* refactor: simplify macOS job configuration and improve caching
* refactor: remove unnecessary async/await from service and proxy functions
* refactor: streamline pnpm installation in CI configuration
* refactor: simplify error handling and remove unnecessary else statements
* refactor: replace async/await with synchronous locks for core management
* refactor: add workflow_dispatch trigger to clippy job
* refactor: convert async functions to synchronous for service management
* refactor: convert async functions to synchronous for UWP tool invocation
* fix: change wrong logging
* refactor: convert proxy restoration functions to async
* Revert "refactor: convert proxy restoration functions to async"
This reverts commit b82f5d250b.
* refactor: update proxy restoration functions to return Result types
* fix: handle errors during proxy restoration and update async function signatures
* fix: handle errors during proxy restoration and update async function signatures
* refactor: update restore_pac_proxy and restore_sys_proxy functions to async
* fix: convert restore_pac_proxy and restore_sys_proxy functions to async
* fix: await restore_sys_proxy calls in proxy restoration logic
* fix: suppress clippy warnings for unused async functions in proxy restoration
* fix: suppress clippy warnings for unused async functions in proxy restoration
155 lines
5.2 KiB
Rust
155 lines
5.2 KiB
Rust
use std::sync::Arc;
|
||
|
||
use parking_lot::{
|
||
MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard,
|
||
RwLockUpgradableReadGuard, RwLockWriteGuard,
|
||
};
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct Draft<T: Clone + ToOwned> {
|
||
inner: Arc<RwLock<(T, Option<T>)>>,
|
||
}
|
||
|
||
impl<T: Clone + ToOwned> From<T> for Draft<T> {
|
||
fn from(data: T) -> Self {
|
||
Self {
|
||
inner: Arc::new(RwLock::new((data, None))),
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Implements draft management for `Box<T>`, allowing for safe concurrent editing and committing of draft data.
|
||
/// # Type Parameters
|
||
/// - `T`: The underlying data type, which must implement `Clone` and `ToOwned`.
|
||
///
|
||
/// # Methods
|
||
/// - `data_mut`: Returns a mutable reference to the committed data.
|
||
/// - `data_ref`: Returns an immutable reference to the committed data.
|
||
/// - `draft_mut`: Creates or retrieves a mutable reference to the draft data, cloning the committed data if no draft exists.
|
||
/// - `latest_ref`: Returns an immutable reference to the draft data if it exists, otherwise to the committed data.
|
||
/// - `apply`: Commits the draft data, replacing the committed data and returning the old committed value if a draft existed.
|
||
/// - `discard`: Discards the draft data and returns it if it existed.
|
||
impl<T: Clone + ToOwned> Draft<Box<T>> {
|
||
/// 可写正式数据
|
||
pub fn data_mut(&self) -> MappedRwLockWriteGuard<'_, Box<T>> {
|
||
RwLockWriteGuard::map(self.inner.write(), |inner| &mut inner.0)
|
||
}
|
||
|
||
/// 返回正式数据的只读视图(不包含草稿)
|
||
pub fn data_ref(&self) -> MappedRwLockReadGuard<'_, Box<T>> {
|
||
RwLockReadGuard::map(self.inner.read(), |inner| &inner.0)
|
||
}
|
||
|
||
/// 创建或获取草稿并返回可写引用
|
||
pub fn draft_mut(&self) -> MappedRwLockWriteGuard<'_, Box<T>> {
|
||
let guard = self.inner.upgradable_read();
|
||
if guard.1.is_none() {
|
||
let mut guard = RwLockUpgradableReadGuard::upgrade(guard);
|
||
guard.1 = Some(guard.0.clone());
|
||
return RwLockWriteGuard::map(guard, |inner| {
|
||
inner.1.as_mut().unwrap_or_else(|| {
|
||
unreachable!("Draft was just created above, this should never fail")
|
||
})
|
||
});
|
||
}
|
||
// 已存在草稿,升级为写锁映射
|
||
RwLockWriteGuard::map(RwLockUpgradableReadGuard::upgrade(guard), |inner| {
|
||
inner
|
||
.1
|
||
.as_mut()
|
||
.unwrap_or_else(|| unreachable!("Draft should exist when guard.1.is_some()"))
|
||
})
|
||
}
|
||
|
||
/// 零拷贝只读视图:返回草稿(若存在)或正式值
|
||
pub fn latest_ref(&self) -> MappedRwLockReadGuard<'_, Box<T>> {
|
||
RwLockReadGuard::map(self.inner.read(), |inner| {
|
||
inner.1.as_ref().unwrap_or(&inner.0)
|
||
})
|
||
}
|
||
|
||
/// 提交草稿,返回旧正式数据
|
||
pub fn apply(&self) -> Option<Box<T>> {
|
||
let mut inner = self.inner.write();
|
||
inner
|
||
.1
|
||
.take()
|
||
.map(|draft| std::mem::replace(&mut inner.0, draft))
|
||
}
|
||
|
||
/// 丢弃草稿,返回被丢弃的草稿
|
||
pub fn discard(&self) -> Option<Box<T>> {
|
||
self.inner.write().1.take()
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_draft_box() {
|
||
use super::IVerge;
|
||
|
||
// 1. 创建 Draft<Box<IVerge>>
|
||
let verge = Box::new(IVerge {
|
||
enable_auto_launch: Some(true),
|
||
enable_tun_mode: Some(false),
|
||
..IVerge::default()
|
||
});
|
||
let draft = Draft::from(verge);
|
||
|
||
// 2. 读取正式数据(data_mut)
|
||
{
|
||
let data = draft.data_mut();
|
||
assert_eq!(data.enable_auto_launch, Some(true));
|
||
assert_eq!(data.enable_tun_mode, Some(false));
|
||
}
|
||
|
||
// 3. 初次获取草稿(draft_mut 会自动 clone 一份)
|
||
{
|
||
let draft_view = draft.draft_mut();
|
||
assert_eq!(draft_view.enable_auto_launch, Some(true));
|
||
assert_eq!(draft_view.enable_tun_mode, Some(false));
|
||
}
|
||
|
||
// 4. 修改草稿
|
||
{
|
||
let mut d = draft.draft_mut();
|
||
d.enable_auto_launch = Some(false);
|
||
d.enable_tun_mode = Some(true);
|
||
}
|
||
|
||
// 正式数据未变
|
||
assert_eq!(draft.data_mut().enable_auto_launch, Some(true));
|
||
assert_eq!(draft.data_mut().enable_tun_mode, Some(false));
|
||
|
||
// 草稿已变
|
||
{
|
||
let latest = draft.latest_ref();
|
||
assert_eq!(latest.enable_auto_launch, Some(false));
|
||
assert_eq!(latest.enable_tun_mode, Some(true));
|
||
}
|
||
|
||
// 5. 提交草稿
|
||
assert!(draft.apply().is_some()); // 第一次提交应有返回
|
||
assert!(draft.apply().is_none()); // 第二次提交返回 None
|
||
|
||
// 正式数据已更新
|
||
{
|
||
let data = draft.data_mut();
|
||
assert_eq!(data.enable_auto_launch, Some(false));
|
||
assert_eq!(data.enable_tun_mode, Some(true));
|
||
}
|
||
|
||
// 6. 新建并修改下一轮草稿
|
||
{
|
||
let mut d = draft.draft_mut();
|
||
d.enable_auto_launch = Some(true);
|
||
}
|
||
assert_eq!(draft.draft_mut().enable_auto_launch, Some(true));
|
||
|
||
// 7. 丢弃草稿
|
||
assert!(draft.discard().is_some()); // 第一次丢弃返回 Some
|
||
assert!(draft.discard().is_none()); // 再次丢弃返回 None
|
||
|
||
// 8. 草稿已被丢弃,新的 draft_mut() 会重新 clone
|
||
assert_eq!(draft.draft_mut().enable_auto_launch, Some(false));
|
||
}
|