ostp/ostp-client/src/app.rs

120 lines
2.9 KiB
Rust

use std::collections::VecDeque;
use ostp_core::TrafficProfile;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionStatus {
Stopped,
Handshaking,
Established,
}
impl ConnectionStatus {
pub fn as_str(self) -> &'static str {
match self {
Self::Stopped => "Stopped",
Self::Handshaking => "Handshaking",
Self::Established => "Established",
}
}
}
#[derive(Debug, Clone)]
pub enum UiEvent {
Metrics {
status: ConnectionStatus,
rtt_ms: f64,
throughput_bps: u64,
},
Traffic {
incoming_bps: u64,
outgoing_bps: u64,
},
Log(String),
ProfileChanged(TrafficProfile),
TunnelStopped,
}
#[derive(Debug, Clone)]
pub enum BridgeCommand {
ToggleTunnel,
NextProfile,
ReloadConfig,
/// Triggered by Android NetworkCallback when the active network changes (WiFi→LTE, etc.).
/// Causes an immediate background reconnect without waiting for stall detection.
NetworkChanged,
Shutdown,
}
pub struct AppState {
pub status: ConnectionStatus,
pub active_profile: TrafficProfile,
pub rtt_ms: f64,
pub throughput_bps: u64,
pub incoming_history: Vec<u64>,
pub outgoing_history: Vec<u64>,
pub logs: VecDeque<String>,
pub log_scroll: u16,
}
impl Default for AppState {
fn default() -> Self {
Self::new()
}
}
impl AppState {
pub fn new() -> Self {
Self {
status: ConnectionStatus::Stopped,
active_profile: TrafficProfile::JsonRpc,
rtt_ms: 0.0,
throughput_bps: 0,
incoming_history: vec![0; 64],
outgoing_history: vec![0; 64],
logs: VecDeque::with_capacity(512),
log_scroll: 0,
}
}
pub fn apply_event(&mut self, event: UiEvent) {
match event {
UiEvent::Metrics {
status,
rtt_ms,
throughput_bps,
} => {
self.status = status;
self.rtt_ms = rtt_ms;
self.throughput_bps = throughput_bps;
}
UiEvent::Traffic {
incoming_bps,
outgoing_bps,
} => {
push_sample(&mut self.incoming_history, incoming_bps);
push_sample(&mut self.outgoing_history, outgoing_bps);
}
UiEvent::Log(line) => {
if self.logs.len() >= 500 {
self.logs.pop_front();
}
self.logs.push_back(line);
}
UiEvent::ProfileChanged(profile) => {
self.active_profile = profile;
}
UiEvent::TunnelStopped => {
self.status = ConnectionStatus::Stopped;
}
}
}
}
fn push_sample(history: &mut Vec<u64>, value: u64) {
if !history.is_empty() {
history.remove(0);
}
history.push(value);
}