mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
refactor(Draft): Replace latest() with latest_ref() and data() with data_mut() in multiple files for improved mutability handling and consistency across the codebase (#3987)
* feat: add benchmarking for draft operations and new draft management structure * Refactor Config Access: Replace `latest()` with `latest_ref()` and `data()` with `data_mut()` in multiple files for improved mutability handling and consistency across the codebase. * refactor: remove DraftNew implementation and related benchmarks for cleaner codebase
This commit is contained in:
@@ -52,20 +52,24 @@ impl Config {
|
||||
/// 初始化订阅
|
||||
pub async fn init_config() -> Result<()> {
|
||||
if Self::profiles()
|
||||
.data()
|
||||
.latest_ref()
|
||||
.get_item(&"Merge".to_string())
|
||||
.is_err()
|
||||
{
|
||||
let merge_item = PrfItem::from_merge(Some("Merge".to_string()))?;
|
||||
Self::profiles().data().append_item(merge_item.clone())?;
|
||||
Self::profiles()
|
||||
.data_mut()
|
||||
.append_item(merge_item.clone())?;
|
||||
}
|
||||
if Self::profiles()
|
||||
.data()
|
||||
.latest_ref()
|
||||
.get_item(&"Script".to_string())
|
||||
.is_err()
|
||||
{
|
||||
let script_item = PrfItem::from_script(Some("Script".to_string()))?;
|
||||
Self::profiles().data().append_item(script_item.clone())?;
|
||||
Self::profiles()
|
||||
.data_mut()
|
||||
.append_item(script_item.clone())?;
|
||||
}
|
||||
// 生成运行时配置
|
||||
if let Err(err) = Self::generate().await {
|
||||
@@ -135,7 +139,7 @@ impl Config {
|
||||
};
|
||||
|
||||
let runtime = Config::runtime();
|
||||
let runtime = runtime.latest();
|
||||
let runtime = runtime.latest_ref();
|
||||
let config = runtime
|
||||
.config
|
||||
.as_ref()
|
||||
@@ -149,7 +153,7 @@ impl Config {
|
||||
pub async fn generate() -> Result<()> {
|
||||
let (config, exists_keys, logs) = enhance::enhance().await;
|
||||
|
||||
*Config::runtime().draft() = Box::new(IRuntime {
|
||||
*Config::runtime().draft_mut() = Box::new(IRuntime {
|
||||
config: Some(config),
|
||||
exists_keys,
|
||||
chain_logs: logs,
|
||||
|
||||
@@ -1,135 +1,147 @@
|
||||
use super::{IClashTemp, IProfiles, IRuntime, IVerge};
|
||||
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::{
|
||||
MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard,
|
||||
RwLockUpgradableReadGuard, RwLockWriteGuard,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Draft<T: Clone + ToOwned> {
|
||||
inner: Arc<Mutex<(T, Option<T>)>>,
|
||||
inner: Arc<RwLock<(T, Option<T>)>>,
|
||||
}
|
||||
|
||||
macro_rules! draft_define {
|
||||
($id: ident) => {
|
||||
impl From<$id> for Draft<$id> {
|
||||
fn from(data: $id) -> Self {
|
||||
Draft {
|
||||
inner: Arc::new(Mutex::new((data, None))),
|
||||
}
|
||||
}
|
||||
impl<T: Clone + ToOwned> From<T> for Draft<T> {
|
||||
fn from(data: T) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new((data, None))),
|
||||
}
|
||||
|
||||
impl Draft<Box<$id>> {
|
||||
#[allow(unused)]
|
||||
pub fn data(&self) -> MappedMutexGuard<Box<$id>> {
|
||||
MutexGuard::map(self.inner.lock(), |guard| &mut guard.0)
|
||||
}
|
||||
|
||||
pub fn latest(&self) -> MappedMutexGuard<Box<$id>> {
|
||||
MutexGuard::map(self.inner.lock(), |inner| {
|
||||
if inner.1.is_none() {
|
||||
&mut inner.0
|
||||
} else {
|
||||
inner.1.as_mut().unwrap()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn draft(&self) -> MappedMutexGuard<Box<$id>> {
|
||||
MutexGuard::map(self.inner.lock(), |inner| {
|
||||
if inner.1.is_none() {
|
||||
inner.1 = Some(inner.0.clone());
|
||||
}
|
||||
|
||||
inner.1.as_mut().unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn apply(&self) -> Option<Box<$id>> {
|
||||
let mut inner = self.inner.lock();
|
||||
|
||||
match inner.1.take() {
|
||||
Some(draft) => {
|
||||
let old_value = inner.0.to_owned();
|
||||
inner.0 = draft.to_owned();
|
||||
Some(old_value)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn discard(&self) -> Option<Box<$id>> {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.1.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<$id>> for Draft<Box<$id>> {
|
||||
fn from(data: Box<$id>) -> Self {
|
||||
Draft {
|
||||
inner: Arc::new(Mutex::new((data, None))),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// draft_define!(IClash);
|
||||
draft_define!(IClashTemp);
|
||||
draft_define!(IProfiles);
|
||||
draft_define!(IRuntime);
|
||||
draft_define!(IVerge);
|
||||
/// 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());
|
||||
}
|
||||
// 已存在草稿,升级为写锁映射
|
||||
RwLockWriteGuard::map(RwLockUpgradableReadGuard::upgrade(guard), |inner| {
|
||||
inner.1.as_mut().unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
/// 零拷贝只读视图:返回草稿(若存在)或正式值
|
||||
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);
|
||||
|
||||
assert_eq!(draft.data().enable_auto_launch, Some(true));
|
||||
assert_eq!(draft.data().enable_tun_mode, Some(false));
|
||||
|
||||
assert_eq!(draft.draft().enable_auto_launch, Some(true));
|
||||
assert_eq!(draft.draft().enable_tun_mode, Some(false));
|
||||
|
||||
// 2. 读取正式数据(data_mut)
|
||||
{
|
||||
let mut d = draft.draft();
|
||||
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().enable_auto_launch, Some(true));
|
||||
assert_eq!(draft.data().enable_tun_mode, Some(false));
|
||||
|
||||
assert_eq!(draft.draft().enable_auto_launch, Some(false));
|
||||
assert_eq!(draft.draft().enable_tun_mode, Some(true));
|
||||
|
||||
assert_eq!(draft.latest().enable_auto_launch, Some(false));
|
||||
assert_eq!(draft.latest().enable_tun_mode, Some(true));
|
||||
|
||||
assert!(draft.apply().is_some());
|
||||
assert!(draft.apply().is_none());
|
||||
|
||||
assert_eq!(draft.data().enable_auto_launch, Some(false));
|
||||
assert_eq!(draft.data().enable_tun_mode, Some(true));
|
||||
|
||||
assert_eq!(draft.draft().enable_auto_launch, Some(false));
|
||||
assert_eq!(draft.draft().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 mut d = draft.draft();
|
||||
d.enable_auto_launch = Some(true);
|
||||
let latest = draft.latest_ref();
|
||||
assert_eq!(latest.enable_auto_launch, Some(false));
|
||||
assert_eq!(latest.enable_tun_mode, Some(true));
|
||||
}
|
||||
|
||||
assert_eq!(draft.data().enable_auto_launch, Some(false));
|
||||
assert_eq!(draft.draft().enable_auto_launch, Some(true));
|
||||
// 5. 提交草稿
|
||||
assert!(draft.apply().is_some()); // 第一次提交应有返回
|
||||
assert!(draft.apply().is_none()); // 第二次提交返回 None
|
||||
|
||||
assert!(draft.discard().is_some());
|
||||
// 正式数据已更新
|
||||
{
|
||||
let data = draft.data_mut();
|
||||
assert_eq!(data.enable_auto_launch, Some(false));
|
||||
assert_eq!(data.enable_tun_mode, Some(true));
|
||||
}
|
||||
|
||||
assert_eq!(draft.data().enable_auto_launch, Some(false));
|
||||
assert!(draft.discard().is_none());
|
||||
// 6. 新建并修改下一轮草稿
|
||||
{
|
||||
let mut d = draft.draft_mut();
|
||||
d.enable_auto_launch = Some(true);
|
||||
}
|
||||
assert_eq!(draft.draft_mut().enable_auto_launch, Some(true));
|
||||
|
||||
assert_eq!(draft.draft().enable_auto_launch, Some(false));
|
||||
// 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));
|
||||
}
|
||||
|
||||
@@ -186,29 +186,37 @@ impl PrfItem {
|
||||
|
||||
if merge.is_none() {
|
||||
let merge_item = PrfItem::from_merge(None)?;
|
||||
Config::profiles().data().append_item(merge_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(merge_item.clone())?;
|
||||
merge = merge_item.uid;
|
||||
}
|
||||
if script.is_none() {
|
||||
let script_item = PrfItem::from_script(None)?;
|
||||
Config::profiles().data().append_item(script_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(script_item.clone())?;
|
||||
script = script_item.uid;
|
||||
}
|
||||
if rules.is_none() {
|
||||
let rules_item = PrfItem::from_rules()?;
|
||||
Config::profiles().data().append_item(rules_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(rules_item.clone())?;
|
||||
rules = rules_item.uid;
|
||||
}
|
||||
if proxies.is_none() {
|
||||
let proxies_item = PrfItem::from_proxies()?;
|
||||
Config::profiles()
|
||||
.data()
|
||||
.data_mut()
|
||||
.append_item(proxies_item.clone())?;
|
||||
proxies = proxies_item.uid;
|
||||
}
|
||||
if groups.is_none() {
|
||||
let groups_item = PrfItem::from_groups()?;
|
||||
Config::profiles().data().append_item(groups_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(groups_item.clone())?;
|
||||
groups = groups_item.uid;
|
||||
}
|
||||
Ok(PrfItem {
|
||||
@@ -366,29 +374,37 @@ impl PrfItem {
|
||||
|
||||
if merge.is_none() {
|
||||
let merge_item = PrfItem::from_merge(None)?;
|
||||
Config::profiles().data().append_item(merge_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(merge_item.clone())?;
|
||||
merge = merge_item.uid;
|
||||
}
|
||||
if script.is_none() {
|
||||
let script_item = PrfItem::from_script(None)?;
|
||||
Config::profiles().data().append_item(script_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(script_item.clone())?;
|
||||
script = script_item.uid;
|
||||
}
|
||||
if rules.is_none() {
|
||||
let rules_item = PrfItem::from_rules()?;
|
||||
Config::profiles().data().append_item(rules_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(rules_item.clone())?;
|
||||
rules = rules_item.uid;
|
||||
}
|
||||
if proxies.is_none() {
|
||||
let proxies_item = PrfItem::from_proxies()?;
|
||||
Config::profiles()
|
||||
.data()
|
||||
.data_mut()
|
||||
.append_item(proxies_item.clone())?;
|
||||
proxies = proxies_item.uid;
|
||||
}
|
||||
if groups.is_none() {
|
||||
let groups_item = PrfItem::from_groups()?;
|
||||
Config::profiles().data().append_item(groups_item.clone())?;
|
||||
Config::profiles()
|
||||
.data_mut()
|
||||
.append_item(groups_item.clone())?;
|
||||
groups = groups_item.uid;
|
||||
}
|
||||
|
||||
|
||||
@@ -300,7 +300,7 @@ impl IVerge {
|
||||
use crate::config::Config;
|
||||
|
||||
let config_draft = Config::verge();
|
||||
*config_draft.draft() = Box::new(updated_config.clone());
|
||||
*config_draft.draft_mut() = Box::new(updated_config.clone());
|
||||
config_draft.apply();
|
||||
|
||||
logging!(
|
||||
|
||||
Reference in New Issue
Block a user