mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-08 02:52:07 +08:00
refactor: api cleanup (#7812)
* refactor(config): unify YAML persistence * refactor(config): remove redundant config APIs * refactor(config): remove unused config APIs * refactor(config): narrow profile APIs * fix(config): preserve profiles on invalid reorder * refactor(startup): narrow resolve helpers * refactor(config): unify mixed port defaults * refactor(network): simplify response APIs * refactor(config): make profile builders infallible * refactor(config): make profile activation infallible * refactor(api): simplify internal runtime APIs * refactor: consolidate internal config and window APIs
This commit is contained in:
+53
-20
@@ -1,15 +1,17 @@
|
||||
use crate::{config::with_encryption, enhance::seq::SeqMap};
|
||||
use crate::config::with_encryption;
|
||||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use nanoid::nanoid;
|
||||
use scopeguard::{ScopeGuard, guard};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_yaml_ng::{Mapping, Value};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
use tokio::io::AsyncWriteExt as _;
|
||||
|
||||
pub async fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
|
||||
pub async fn read_yaml<T: DeserializeOwned>(path: &Path) -> Result<T> {
|
||||
if !tokio::fs::try_exists(path).await.unwrap_or(false) {
|
||||
bail!("file not found \"{}\"", path.display());
|
||||
}
|
||||
@@ -19,7 +21,7 @@ pub async fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
|
||||
Ok(with_encryption(|| async { serde_yaml_ng::from_str::<T>(&yaml_str) }).await?)
|
||||
}
|
||||
|
||||
pub async fn read_mapping(path: &PathBuf) -> Result<Mapping> {
|
||||
pub async fn read_mapping(path: &Path) -> Result<Mapping> {
|
||||
if !tokio::fs::try_exists(path).await.unwrap_or(false) {
|
||||
bail!("file not found \"{}\"", path.display());
|
||||
}
|
||||
@@ -49,11 +51,7 @@ pub async fn read_mapping(path: &PathBuf) -> Result<Mapping> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_seq_map(path: &PathBuf) -> Result<SeqMap> {
|
||||
read_yaml(path).await
|
||||
}
|
||||
|
||||
pub async fn save_yaml<T: Serialize + Sync>(path: &PathBuf, data: &T, prefix: Option<&str>) -> Result<()> {
|
||||
pub async fn save_yaml<T: Serialize + Sync>(path: &Path, data: &T, prefix: Option<&str>) -> Result<()> {
|
||||
let data_str = with_encryption(|| async { serde_yaml_ng::to_string(data) }).await?;
|
||||
|
||||
let yaml_str = match prefix {
|
||||
@@ -61,20 +59,55 @@ pub async fn save_yaml<T: Serialize + Sync>(path: &PathBuf, data: &T, prefix: Op
|
||||
None => data_str,
|
||||
};
|
||||
|
||||
tokio::fs::write(path, yaml_str.as_bytes())
|
||||
.await
|
||||
.with_context(|| format!("failed to save file \"{}\"", path.display()))?;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
Ok(())
|
||||
}
|
||||
let (temporary, file) = loop {
|
||||
let temporary = path.with_extension(format!("tmp-{}-{}", std::process::id(), nanoid!()));
|
||||
match std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(&temporary)
|
||||
{
|
||||
Ok(file) => break (temporary, file),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {}
|
||||
Err(error) => {
|
||||
return Err(error).with_context(|| {
|
||||
format!(
|
||||
"failed to create temporary file \"{}\" for \"{}\"",
|
||||
temporary.display(),
|
||||
path.display()
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub async fn save_yaml_atomic<T: Serialize + Sync>(path: &Path, data: &T, prefix: Option<&str>) -> Result<()> {
|
||||
let temporary = path.with_extension("delete.tmp");
|
||||
save_yaml(&temporary, data, prefix).await?;
|
||||
if let Err(error) = super::server::replace_file_atomic(&temporary, path) {
|
||||
let _ = tokio::fs::remove_file(&temporary).await;
|
||||
return Err(error).with_context(|| format!("failed to replace file \"{}\"", path.display()));
|
||||
let temporary = guard(temporary, |path| {
|
||||
let _ = std::fs::remove_file(path);
|
||||
});
|
||||
let mut file = tokio::fs::File::from_std(file);
|
||||
file.write_all(yaml_str.as_bytes())
|
||||
.await
|
||||
.with_context(|| format!("failed to write temporary file for \"{}\"", path.display()))?;
|
||||
file.flush()
|
||||
.await
|
||||
.with_context(|| format!("failed to flush temporary file for \"{}\"", path.display()))?;
|
||||
drop(file);
|
||||
|
||||
match tokio::fs::symlink_metadata(path).await {
|
||||
Ok(metadata) if metadata.file_type().is_file() => {
|
||||
tokio::fs::set_permissions(temporary.as_path(), metadata.permissions())
|
||||
.await
|
||||
.with_context(|| format!("failed to preserve permissions for \"{}\"", path.display()))?;
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(error) => {
|
||||
return Err(error).with_context(|| format!("failed to inspect file \"{}\"", path.display()));
|
||||
}
|
||||
}
|
||||
|
||||
std::fs::rename(temporary.as_path(), path)
|
||||
.with_context(|| format!("failed to replace file \"{}\"", path.display()))?;
|
||||
let _ = ScopeGuard::into_inner(temporary);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user