mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-30 18:52:12 +08:00
- Replace boolean/string validation results with structured ValidationOutcome values across config validation, profile switching, DNS validation, and script validation. Callers can now distinguish valid, invalid, skipped, and busy states, with invalid results carrying an error kind and message. - Route core config updates through structured outcomes and checked forced updates. Generation and validation failures now discard runtime drafts and surface validation failures instead of relying on empty success messages. - Preserve existing frontend ergonomics by keeping boolean service wrappers where callers only need success, while direct DNS and script validation consumers receive ValidationOutcome. Failed profile editor saves reload the backend-restored file content and synchronize Monaco models so reopened editors show the restored state.
147 lines
4.8 KiB
Rust
147 lines
4.8 KiB
Rust
use super::CoreManager;
|
|
use crate::{
|
|
config::{Config, ConfigType, runtime::IRuntime},
|
|
constants::timing,
|
|
core::{
|
|
handle,
|
|
validate::{CoreConfigValidator, ValidationOutcome, ValidationSkipReason},
|
|
},
|
|
utils::{dirs, help},
|
|
};
|
|
use anyhow::{Result, anyhow};
|
|
use clash_verge_logging::{Type, logging};
|
|
use smartstring::alias::String;
|
|
use std::{collections::HashSet, path::PathBuf, time::Instant};
|
|
use tauri_plugin_mihomo::Error as MihomoError;
|
|
|
|
impl CoreManager {
|
|
pub async fn use_default_config(&self, error_key: &str, error_msg: &str) -> Result<()> {
|
|
use crate::constants::files::RUNTIME_CONFIG;
|
|
|
|
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
|
|
let clash_config = &Config::clash().await.latest_arc().0;
|
|
|
|
Config::runtime().await.edit_draft(|d| {
|
|
*d = IRuntime {
|
|
config: Some(clash_config.to_owned()),
|
|
exists_keys: HashSet::new(),
|
|
chain_logs: Default::default(),
|
|
}
|
|
});
|
|
|
|
help::save_yaml(&runtime_path, &clash_config, Some("# Clash Verge Runtime")).await?;
|
|
handle::Handle::notice_message(error_key, error_msg);
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn update_config_forced(&self) -> Result<ValidationOutcome> {
|
|
self.update_config_with_force(true).await
|
|
}
|
|
|
|
pub async fn update_config_with_force(&self, force: bool) -> Result<ValidationOutcome> {
|
|
if handle::Handle::global().is_exiting() {
|
|
return Ok(ValidationOutcome::Skipped {
|
|
reason: ValidationSkipReason::Exiting,
|
|
});
|
|
}
|
|
|
|
if !force && !self.should_update_config() {
|
|
logging!(debug, Type::Core, "Skipping config update due to debounce");
|
|
return Ok(ValidationOutcome::Skipped {
|
|
reason: ValidationSkipReason::Debounced,
|
|
});
|
|
}
|
|
|
|
if force {
|
|
self.set_last_update(Instant::now());
|
|
}
|
|
|
|
self.perform_config_update().await
|
|
}
|
|
|
|
pub async fn update_config_checked(&self) -> Result<()> {
|
|
let outcome = self.update_config_forced().await?;
|
|
if outcome.is_valid() {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!("{outcome}"))
|
|
}
|
|
}
|
|
|
|
fn should_update_config(&self) -> bool {
|
|
let now = Instant::now();
|
|
let last = self.get_last_update();
|
|
|
|
if let Some(last_time) = last
|
|
&& now.duration_since(*last_time) < timing::CONFIG_UPDATE_DEBOUNCE
|
|
{
|
|
return false;
|
|
}
|
|
|
|
self.set_last_update(now);
|
|
true
|
|
}
|
|
|
|
async fn perform_config_update(&self) -> Result<ValidationOutcome> {
|
|
if let Err(err) = Config::generate().await {
|
|
let message: String = err.to_string().into();
|
|
Config::runtime().await.discard();
|
|
return Ok(ValidationOutcome::invalid_from_message(message));
|
|
}
|
|
|
|
self.apply_generate_config().await
|
|
}
|
|
|
|
pub async fn apply_generate_config(&self) -> Result<ValidationOutcome> {
|
|
match CoreConfigValidator::global().validate_config_outcome().await {
|
|
Ok(outcome) if outcome.is_valid() => {
|
|
let run_path = Config::generate_file(ConfigType::Run).await?;
|
|
self.apply_config(run_path).await?;
|
|
Ok(ValidationOutcome::Valid)
|
|
}
|
|
Ok(outcome) => {
|
|
Config::runtime().await.discard();
|
|
Ok(outcome)
|
|
}
|
|
Err(e) => {
|
|
Config::runtime().await.discard();
|
|
Err(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn apply_config(&self, path: PathBuf) -> Result<()> {
|
|
let path = dirs::path_to_str(&path)?;
|
|
match self.reload_config(path).await {
|
|
Ok(_) => {
|
|
Config::runtime().await.apply();
|
|
logging!(info, Type::Core, "Configuration applied");
|
|
Ok(())
|
|
}
|
|
Err(err) => {
|
|
logging!(
|
|
warn,
|
|
Type::Core,
|
|
"Failed to apply configuration by mihomo api, restart core to apply it, error msg: {err}"
|
|
);
|
|
match self.restart_core().await {
|
|
Ok(_) => {
|
|
Config::runtime().await.apply();
|
|
logging!(info, Type::Core, "Configuration applied after restart");
|
|
Ok(())
|
|
}
|
|
Err(err) => {
|
|
logging!(error, Type::Core, "Failed to restart core: {}", err);
|
|
Config::runtime().await.discard();
|
|
Err(anyhow!("Failed to apply config: {}", err))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn reload_config(&self, path: &str) -> Result<(), MihomoError> {
|
|
handle::Handle::mihomo().await.reload_config(true, path).await
|
|
}
|
|
}
|