mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-10 04:12:08 +08:00
refactor(ipc): generate bindings with Specta
This commit is contained in:
@@ -38,7 +38,7 @@ pub(crate) fn proxy_listener_keys() -> impl Iterator<Item = &'static str> {
|
||||
PROXY_LISTENERS.iter().map(|(key, _, _)| *key)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProxyPortSettings {
|
||||
pub mixed_port: u16,
|
||||
@@ -48,27 +48,27 @@ pub struct ProxyPortSettings {
|
||||
pub tproxy: ToggleableProxyPort,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, specta::Type)]
|
||||
pub struct ToggleableProxyPort {
|
||||
pub enabled: bool,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ListenerProbe {
|
||||
pub address: String,
|
||||
pub transports: Vec<ListenerTransport>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ListenerTransport {
|
||||
Tcp,
|
||||
Udp,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(tag = "status", rename_all = "camelCase")]
|
||||
pub enum ListenerProbeOutcome {
|
||||
Available,
|
||||
@@ -77,7 +77,7 @@ pub enum ListenerProbeOutcome {
|
||||
Indeterminate { message: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(tag = "status", rename_all = "camelCase")]
|
||||
pub enum SaveProxyPortsOutcome {
|
||||
Saved,
|
||||
|
||||
@@ -57,7 +57,7 @@ fn claim_core_readiness_generation(state: &AtomicU64, captured_generation: u64)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, PartialEq, Eq, specta::Type)]
|
||||
pub enum RunningMode {
|
||||
Service,
|
||||
Sidecar,
|
||||
@@ -75,7 +75,7 @@ impl fmt::Display for RunningMode {
|
||||
}
|
||||
|
||||
/// Why the Core is not running, kept until a Core starts again.
|
||||
#[derive(Clone, Debug, serde::Serialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, serde::Serialize, PartialEq, Eq, specta::Type)]
|
||||
#[serde(tag = "kind", content = "detail", rename_all = "camelCase")]
|
||||
pub enum CoreFailure {
|
||||
StartFailed(String),
|
||||
|
||||
@@ -52,10 +52,7 @@ impl Handle {
|
||||
/// Sent on every transition, so the frontend does not have to poll to notice that the Core
|
||||
/// stopped or that the Service came back.
|
||||
pub fn notify_run_state(state: &crate::core::runstate::RunStateView) {
|
||||
let Ok(state) = serde_json::to_value(state) else {
|
||||
return;
|
||||
};
|
||||
Self::send_event(FrontendEvent::RunStateChanged { state });
|
||||
Self::send_event(FrontendEvent::RunStateChanged { state: state.clone() });
|
||||
}
|
||||
|
||||
pub fn notify_profile_changed(profile_id: &String) {
|
||||
|
||||
@@ -3,9 +3,8 @@ pub use announce::{Refresh, announce};
|
||||
pub mod desktop;
|
||||
pub mod handle;
|
||||
pub mod notification;
|
||||
pub use notification::frontend_wire_contract;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, specta::Type)]
|
||||
pub enum NoticeStatus {
|
||||
#[serde(rename = "info")]
|
||||
Info,
|
||||
@@ -83,6 +82,7 @@ impl NoticeStatus {
|
||||
/// Every variant in golden order. The wire-contract export and the golden
|
||||
/// test both derive their variant list from here; expected strings stay
|
||||
/// hand-written in the test so renames cannot slip through.
|
||||
#[cfg(test)]
|
||||
pub const ALL: [Self; 34] = [
|
||||
Self::Info,
|
||||
Self::ImportSubUrlOk,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::NoticeStatus;
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use parking_lot::Mutex;
|
||||
#[cfg(test)]
|
||||
use serde_json::json;
|
||||
use smartstring::alias::String;
|
||||
use std::sync::Arc;
|
||||
@@ -31,7 +32,7 @@ pub enum FrontendEvent<'a> {
|
||||
uid: &'a String,
|
||||
},
|
||||
RunStateChanged {
|
||||
state: serde_json::Value,
|
||||
state: crate::core::runstate::RunStateView,
|
||||
},
|
||||
PendingFailuresChanged,
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -42,7 +43,7 @@ pub enum FrontendEvent<'a> {
|
||||
|
||||
/// Operation associated with a pending failure.
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum FailedOperation {
|
||||
SystemProxyEnable,
|
||||
@@ -72,12 +73,14 @@ impl FailedOperation {
|
||||
}
|
||||
|
||||
/// Latest unresolved failure for one stable code.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PendingFailure {
|
||||
/// Stable code used as the table key.
|
||||
#[specta(type = std::string::String)]
|
||||
pub code: String,
|
||||
/// Full diagnostic context chain.
|
||||
#[specta(type = std::string::String)]
|
||||
pub detail: String,
|
||||
pub operation: FailedOperation,
|
||||
/// Monotonic identity for repeated failures under the same code.
|
||||
@@ -241,9 +244,10 @@ pub fn what_was_asked() -> FailedOperation {
|
||||
static PENDING_FAILURES: LazyLock<FailureTable> = LazyLock::new(FailureTable::default);
|
||||
|
||||
/// A removal has a revision too, so a delayed read cannot restore a resolved failure.
|
||||
#[derive(Debug, Default, Clone, serde::Serialize)]
|
||||
#[derive(Debug, Default, Clone, serde::Serialize, specta::Type)]
|
||||
pub struct SidecarFailureSnapshot {
|
||||
pub revision: u64,
|
||||
#[specta(type = Option<std::string::String>)]
|
||||
pub detail: Option<String>,
|
||||
}
|
||||
|
||||
@@ -327,24 +331,7 @@ impl NotificationSystem {
|
||||
}
|
||||
|
||||
fn serialize_event(event: FrontendEvent) -> (&'static str, Result<serde_json::Value, serde_json::Error>) {
|
||||
match event {
|
||||
FrontendEvent::RefreshClash => ("verge://refresh-clash-config", Ok(json!("yes"))),
|
||||
FrontendEvent::RefreshVerge => ("verge://refresh-verge-config", Ok(json!("yes"))),
|
||||
FrontendEvent::RefreshProfiles => ("verge://refresh-profiles", Ok(json!("yes"))),
|
||||
FrontendEvent::RefreshProxyConfig => ("verge://refresh-proxy-config", Ok(serde_json::Value::Null)),
|
||||
FrontendEvent::NoticeMessage { status, message } => (
|
||||
"verge://notice-message",
|
||||
serde_json::to_value((status, message.as_ref())),
|
||||
),
|
||||
FrontendEvent::ProfileChanged { current_profile_id } => ("profile-changed", Ok(json!(current_profile_id))),
|
||||
FrontendEvent::TimerUpdated { profile_index } => ("verge://timer-updated", Ok(json!(profile_index))),
|
||||
FrontendEvent::ProfileUpdateStarted { uid } => ("profile-update-started", Ok(json!({ "uid": uid }))),
|
||||
FrontendEvent::ProfileUpdateCompleted { uid } => ("profile-update-completed", Ok(json!({ "uid": uid }))),
|
||||
FrontendEvent::RunStateChanged { state } => ("verge://run-state-changed", Ok(state)),
|
||||
FrontendEvent::PendingFailuresChanged => ("verge://pending-failures-changed", Ok(serde_json::Value::Null)),
|
||||
#[cfg(target_os = "linux")]
|
||||
FrontendEvent::ThemeChanged { theme } => ("tauri://theme-changed", serde_json::to_value(theme)),
|
||||
}
|
||||
serialize_frontend_event(event)
|
||||
}
|
||||
|
||||
pub(crate) fn send_event(app_handle: AppHandle, event: FrontendEvent) {
|
||||
@@ -381,8 +368,17 @@ mod wire_tests {
|
||||
}
|
||||
|
||||
/// One sample per `FrontendEvent` variant, paired with its wire name and payload.
|
||||
/// Shared by the wire golden test and the contract export so the two cannot drift.
|
||||
#[cfg(test)]
|
||||
fn sample_frontend_events(uid: &String) -> Vec<(FrontendEvent<'_>, &'static str, serde_json::Value)> {
|
||||
let state = crate::core::runstate::RunState {
|
||||
health: Default::default(),
|
||||
pending: None,
|
||||
sidecar_allowed: false,
|
||||
mode: crate::core::manager::RunningMode::NotRunning,
|
||||
is_admin: false,
|
||||
op_in_flight: false,
|
||||
}
|
||||
.to_view();
|
||||
vec![
|
||||
(
|
||||
FrontendEvent::RefreshClash,
|
||||
@@ -414,11 +410,9 @@ fn sample_frontend_events(uid: &String) -> Vec<(FrontendEvent<'_>, &'static str,
|
||||
json!("profile"),
|
||||
),
|
||||
(
|
||||
FrontendEvent::RunStateChanged {
|
||||
state: json!({"running": true}),
|
||||
},
|
||||
FrontendEvent::RunStateChanged { state: state.clone() },
|
||||
"verge://run-state-changed",
|
||||
json!({"running": true}),
|
||||
serde_json::to_value(state).unwrap_or(serde_json::Value::Null),
|
||||
),
|
||||
(
|
||||
FrontendEvent::PendingFailuresChanged,
|
||||
@@ -445,24 +439,41 @@ fn sample_frontend_events(uid: &String) -> Vec<(FrontendEvent<'_>, &'static str,
|
||||
]
|
||||
}
|
||||
|
||||
/// Machine-readable form of the frontend wire contract: every event name with a
|
||||
/// serialized sample payload, plus every notice status string. The sample
|
||||
/// payload's JSON shape (string / null / array / object) is what the frontend
|
||||
/// generator maps to TypeScript types. `ThemeChanged` is a Tauri built-in
|
||||
/// linux-only window event, not part of this contract.
|
||||
pub fn frontend_wire_contract() -> serde_json::Value {
|
||||
let uid = String::from("sample");
|
||||
let events: Vec<_> = sample_frontend_events(&uid)
|
||||
.into_iter()
|
||||
.map(|(event, _, _)| {
|
||||
let (name, payload) = NotificationSystem::serialize_event(event);
|
||||
json!({ "name": name, "sample": payload.unwrap_or(serde_json::Value::Null) })
|
||||
})
|
||||
.collect();
|
||||
let statuses: Vec<_> = NoticeStatus::ALL
|
||||
.iter()
|
||||
// A unit variant with a string rename cannot fail to serialize.
|
||||
.filter_map(|status| serde_json::to_value(status).ok())
|
||||
.collect();
|
||||
json!({ "version": 1, "events": events, "noticeStatuses": statuses })
|
||||
macro_rules! frontend_events {
|
||||
($( $field:ident: $payload:ty = $name:literal, $pattern:pat => $value:expr; )*) => {
|
||||
#[derive(serde::Serialize, specta::Type)]
|
||||
pub struct VergeEventPayloads {
|
||||
$(#[serde(rename = $name)] pub $field: $payload,)*
|
||||
}
|
||||
|
||||
fn serialize_frontend_event(event: FrontendEvent) -> (&'static str, Result<serde_json::Value, serde_json::Error>) {
|
||||
match event {
|
||||
$($pattern => {
|
||||
let payload: $payload = $value;
|
||||
($name, serde_json::to_value(payload))
|
||||
},)*
|
||||
#[cfg(target_os = "linux")]
|
||||
FrontendEvent::ThemeChanged { theme } => ("tauri://theme-changed", serde_json::to_value(theme)),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
frontend_events! {
|
||||
refresh_clash: std::string::String = "verge://refresh-clash-config", FrontendEvent::RefreshClash => "yes".to_owned();
|
||||
refresh_verge: std::string::String = "verge://refresh-verge-config", FrontendEvent::RefreshVerge => "yes".to_owned();
|
||||
refresh_profiles: std::string::String = "verge://refresh-profiles", FrontendEvent::RefreshProfiles => "yes".to_owned();
|
||||
refresh_proxy: () = "verge://refresh-proxy-config", FrontendEvent::RefreshProxyConfig => ();
|
||||
notice: (NoticeStatus, std::string::String) = "verge://notice-message", FrontendEvent::NoticeMessage { status, ref message } => (status, message.to_string());
|
||||
profile_changed: std::string::String = "profile-changed", FrontendEvent::ProfileChanged { current_profile_id } => current_profile_id.to_string();
|
||||
timer_updated: std::string::String = "verge://timer-updated", FrontendEvent::TimerUpdated { profile_index } => profile_index.to_string();
|
||||
profile_started: ProfileUpdatePayload = "profile-update-started", FrontendEvent::ProfileUpdateStarted { uid } => ProfileUpdatePayload { uid: uid.to_string() };
|
||||
profile_completed: ProfileUpdatePayload = "profile-update-completed", FrontendEvent::ProfileUpdateCompleted { uid } => ProfileUpdatePayload { uid: uid.to_string() };
|
||||
run_state: crate::core::runstate::RunStateView = "verge://run-state-changed", FrontendEvent::RunStateChanged { state } => state;
|
||||
pending_failures: () = "verge://pending-failures-changed", FrontendEvent::PendingFailuresChanged => ();
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, specta::Type)]
|
||||
pub struct ProfileUpdatePayload {
|
||||
pub uid: std::string::String,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct GroupScope {
|
||||
|
||||
pub struct ProxyViewBuilder;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProxyViewV1 {
|
||||
pub schema_version: u8,
|
||||
@@ -34,21 +34,21 @@ pub struct ProxyViewV1 {
|
||||
pub providers: Vec<ProxyProviderView>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ProxyViewOrderSource {
|
||||
Runtime,
|
||||
Fallback,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ProxyViewProviderState {
|
||||
Ready,
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
pub struct ProxyCapabilities {
|
||||
pub udp: bool,
|
||||
pub xudp: bool,
|
||||
@@ -57,11 +57,12 @@ pub struct ProxyCapabilities {
|
||||
pub smux: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProxyGroupView {
|
||||
pub name: String,
|
||||
#[serde(rename = "type")]
|
||||
#[specta(type = String)]
|
||||
pub proxy_type: ProxyType,
|
||||
pub alive: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -74,20 +75,23 @@ pub struct ProxyGroupView {
|
||||
pub icon: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub test_url: Option<String>,
|
||||
#[specta(type = Vec<crate::ipc::DelayHistory>)]
|
||||
pub history: Vec<DelayHistory>,
|
||||
#[serde(flatten)]
|
||||
pub capabilities: ProxyCapabilities,
|
||||
pub members: Vec<ProxyMemberRef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProxyNodeView {
|
||||
pub record_id: String,
|
||||
pub name: String,
|
||||
#[serde(rename = "type")]
|
||||
#[specta(type = String)]
|
||||
pub proxy_type: ProxyType,
|
||||
pub alive: bool,
|
||||
#[specta(type = Vec<crate::ipc::DelayHistory>)]
|
||||
pub history: Vec<DelayHistory>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<String>,
|
||||
@@ -102,7 +106,7 @@ pub struct ProxyNodeView {
|
||||
pub source: ProxyNodeSource,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(tag = "kind", rename_all = "camelCase")]
|
||||
pub enum ProxyNodeSource {
|
||||
Core {
|
||||
@@ -117,7 +121,7 @@ pub enum ProxyNodeSource {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(tag = "kind", rename_all = "camelCase")]
|
||||
pub enum ProxyMemberRef {
|
||||
Group {
|
||||
@@ -134,7 +138,7 @@ pub enum ProxyMemberRef {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
pub enum ProxyMemberUnresolvedReason {
|
||||
#[serde(rename = "missing")]
|
||||
Missing,
|
||||
@@ -144,7 +148,7 @@ pub enum ProxyMemberUnresolvedReason {
|
||||
ProviderUnavailable,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProxyProviderView {
|
||||
pub name: String,
|
||||
@@ -156,7 +160,7 @@ pub struct ProxyProviderView {
|
||||
pub proxy_record_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
pub enum ProxyProviderVehicleType {
|
||||
#[serde(rename = "HTTP")]
|
||||
Http,
|
||||
@@ -166,7 +170,7 @@ pub enum ProxyProviderVehicleType {
|
||||
Inline,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, specta::Type)]
|
||||
pub struct ProxySubscriptionInfo {
|
||||
pub upload: i64,
|
||||
pub download: i64,
|
||||
|
||||
@@ -14,13 +14,13 @@ pub enum ServiceHealth {
|
||||
}
|
||||
|
||||
impl ServiceHealth {
|
||||
const fn kind(&self) -> &'static str {
|
||||
const fn kind(&self) -> ServiceHealthKind {
|
||||
match self {
|
||||
Self::Unknown => "unknown",
|
||||
Self::Ready => "ready",
|
||||
Self::NotInstalled => "notInstalled",
|
||||
Self::VersionMismatch => "versionMismatch",
|
||||
Self::Unavailable(_) => "unavailable",
|
||||
Self::Unknown => ServiceHealthKind::Unknown,
|
||||
Self::Ready => ServiceHealthKind::Ready,
|
||||
Self::NotInstalled => ServiceHealthKind::NotInstalled,
|
||||
Self::VersionMismatch => ServiceHealthKind::VersionMismatch,
|
||||
Self::Unavailable(_) => ServiceHealthKind::Unavailable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl ServiceHealth {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum PendingAction {
|
||||
Install,
|
||||
@@ -118,11 +118,11 @@ impl RunState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
#[derive(Debug, Clone, serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RunStateView {
|
||||
pub mode: RunningMode,
|
||||
pub service: &'static str,
|
||||
pub service: ServiceHealthKind,
|
||||
pub service_unavailable_reason: Option<String>,
|
||||
pub pending_action: Option<PendingAction>,
|
||||
pub sidecar_allowed: bool,
|
||||
@@ -232,7 +232,7 @@ mod tests {
|
||||
|
||||
let view = run_state.to_view();
|
||||
|
||||
assert_eq!(view.service, "unavailable");
|
||||
assert_eq!(view.service, ServiceHealthKind::Unavailable);
|
||||
assert_eq!(view.service_unavailable_reason.as_deref(), Some("socket refused"));
|
||||
assert_eq!(view.pending_action, Some(PendingAction::Reinstall));
|
||||
assert!(view.tun_capable, "elevation alone makes TUN possible");
|
||||
@@ -244,7 +244,7 @@ mod tests {
|
||||
fn a_healthy_view_reports_no_unavailable_reason() {
|
||||
let view = state(ServiceHealth::Ready, false, false).to_view();
|
||||
|
||||
assert_eq!(view.service, "ready");
|
||||
assert_eq!(view.service, ServiceHealthKind::Ready);
|
||||
assert_eq!(view.service_unavailable_reason, None);
|
||||
assert!(view.service_usable);
|
||||
assert!(!view.service_needs_attention);
|
||||
@@ -350,3 +350,13 @@ mod tests {
|
||||
assert!(settled.tun_should_be_disabled(true));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum ServiceHealthKind {
|
||||
Unknown,
|
||||
Ready,
|
||||
NotInstalled,
|
||||
VersionMismatch,
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ static PENDING_SERVICE_REPAIR_NOTICE: AtomicBool = AtomicBool::new(false);
|
||||
static PENDING_SERVICE_OWNER_NOTICE: Mutex<Option<String>> = Mutex::new(None);
|
||||
|
||||
/// Why the Service was unavailable when the core fell back to Sidecar.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, specta::Type)]
|
||||
#[serde(tag = "kind", content = "reason", rename_all = "camelCase")]
|
||||
pub enum ServiceFallbackNotice {
|
||||
Unavailable,
|
||||
|
||||
@@ -34,7 +34,7 @@ impl SilentUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, specta::Type)]
|
||||
struct UpdateCacheMeta {
|
||||
version: String,
|
||||
downloaded_at: String,
|
||||
@@ -214,7 +214,7 @@ fn nsis_language_id(app_language: &str) -> &'static str {
|
||||
}
|
||||
|
||||
/// Same wire shape as the updater plugin's JS `DownloadEvent`.
|
||||
#[derive(Clone, Serialize)]
|
||||
#[derive(Clone, Serialize, specta::Type)]
|
||||
#[serde(tag = "event", content = "data")]
|
||||
pub enum DownloadEvent {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -51,7 +51,7 @@ pub struct CoreConfigValidator {
|
||||
is_processing: AtomicBool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum ValidationErrorKind {
|
||||
FileMissing,
|
||||
@@ -94,7 +94,7 @@ impl ValidationErrorKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum ValidationSkipReason {
|
||||
Exiting,
|
||||
@@ -110,12 +110,18 @@ impl fmt::Display for ValidationSkipReason {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, specta::Type)]
|
||||
#[serde(tag = "status", rename_all = "camelCase")]
|
||||
pub enum ValidationOutcome {
|
||||
Valid,
|
||||
Invalid { kind: ValidationErrorKind, message: String },
|
||||
Skipped { reason: ValidationSkipReason },
|
||||
Invalid {
|
||||
kind: ValidationErrorKind,
|
||||
#[specta(type = std::string::String)]
|
||||
message: String,
|
||||
},
|
||||
Skipped {
|
||||
reason: ValidationSkipReason,
|
||||
},
|
||||
Busy,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user