mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-09 11:52:08 +08:00
* 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
93 lines
3.0 KiB
Rust
93 lines
3.0 KiB
Rust
//! Resolves the configured [`MixedPort::desired`] separately from the Core's live
|
|
//! [`MixedPort::effective`] value.
|
|
|
|
use std::sync::atomic::{AtomicU16, Ordering};
|
|
|
|
use anyhow::Result;
|
|
|
|
use super::Config;
|
|
use crate::core::handle::Handle;
|
|
|
|
/// A startup fallback for this session only; persistence would make the port climb across launches.
|
|
static SESSION_FALLBACK: AtomicU16 = AtomicU16::new(0);
|
|
|
|
pub struct MixedPort;
|
|
|
|
impl MixedPort {
|
|
pub(crate) fn session_fallback() -> Option<u16> {
|
|
match SESSION_FALLBACK.load(Ordering::Acquire) {
|
|
0 => None,
|
|
port => Some(port),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn set_session_fallback(port: u16) {
|
|
SESSION_FALLBACK.store(port, Ordering::Release);
|
|
}
|
|
|
|
pub(crate) fn clear_session_fallback() {
|
|
SESSION_FALLBACK.store(0, Ordering::Release);
|
|
}
|
|
|
|
/// Returns the configured port. Paths staging it must close PAC until the Core catches up.
|
|
pub async fn desired() -> u16 {
|
|
let selected = Config::verge().await.latest_arc().verge_mixed_port;
|
|
// `get_mixed_port` already falls back to the default when the Merge Config is silent.
|
|
let merged = Config::clash().await.latest_arc().get_mixed_port();
|
|
resolve_desired(Self::session_fallback(), selected, merged)
|
|
}
|
|
|
|
/// Returns the Core's live port; reserve this round-trip for user-triggered paths.
|
|
pub async fn effective() -> u16 {
|
|
let desired = Self::desired().await;
|
|
resolve_effective(
|
|
|| async { Ok(Handle::mihomo().get_base_config().await?.mixed_port) },
|
|
desired,
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
/// The session-only fallback outranks persisted choices.
|
|
const fn resolve_desired(session_fallback: Option<u16>, selected: Option<u16>, merged: u16) -> u16 {
|
|
match (session_fallback, selected) {
|
|
(Some(port), _) | (None, Some(port)) => port,
|
|
(None, None) => merged,
|
|
}
|
|
}
|
|
|
|
/// Prefer what the Core reports; fall back to what we configured.
|
|
async fn resolve_effective<Live, LiveFuture>(live: Live, desired: u16) -> u16
|
|
where
|
|
Live: FnOnce() -> LiveFuture,
|
|
LiveFuture: Future<Output = Result<u16>>,
|
|
{
|
|
match live().await {
|
|
// A Core reporting port 0 is a Core that has not bound anything yet.
|
|
Ok(port) if port != 0 => port,
|
|
_ => desired,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn desired_port_uses_session_then_selection_then_merge_priority() {
|
|
assert_eq!(resolve_desired(Some(7900), Some(9000), 8080), 7900);
|
|
assert_eq!(resolve_desired(None, Some(9000), 8080), 9000);
|
|
assert_eq!(resolve_desired(None, None, 8080), 8080);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn effective_port_prefers_a_bound_core_port() {
|
|
assert_eq!(resolve_effective(|| async { Ok(7898) }, 7897).await, 7898);
|
|
assert_eq!(resolve_effective(|| async { Ok(0) }, 7897).await, 7897);
|
|
assert_eq!(
|
|
resolve_effective(|| async { anyhow::bail!("core is not running") }, 7897).await,
|
|
7897
|
|
);
|
|
}
|
|
}
|