use crate::{Command, Frequency};
use serde::Deserialize;
use std::time::Duration;
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum Event {
#[serde(rename = "kVoiceConnectedState")]
VoiceConnectedState(VoiceConnectedState),
#[serde(rename = "kStationAdded")]
StationAdded(StationAdded),
#[serde(rename = "kStationStateUpdate")]
StationStateUpdate(StationState),
#[serde(rename = "kFrequencyRemoved")]
FrequencyRemoved(FrequencyRemoved),
#[serde(rename = "kStationStates")]
StationStates(StationStates),
#[serde(rename = "kTxBegin")]
TxBegin(TxBegin),
#[serde(rename = "kTxEnd")]
TxEnd(TxEnd),
#[serde(rename = "kRxBegin")]
RxBegin(RxBegin),
#[serde(rename = "kRxEnd")]
RxEnd(RxEnd),
#[serde(rename = "kMainVolumeChange")]
MainVolumeChange(MainVolumeChange),
#[serde(rename = "kFrequencyStateUpdate")]
#[deprecated(
since = "0.1.0",
note = "This event is deprecated by TrackAudio and only emitted for backwards compatibility. Use StationStateUpdate instead."
)]
#[allow(deprecated)]
FrequencyStateUpdate(FrequencyStateUpdate),
#[serde(skip)]
Client(ClientEvent),
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct VoiceConnectedState {
pub connected: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct StationAdded {
pub callsign: String,
pub frequency: Frequency,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StationState {
pub callsign: Option<String>,
pub is_available: bool,
pub frequency: Option<Frequency>,
pub headset: Option<bool>,
pub is_output_muted: Option<bool>,
pub output_volume: Option<f32>,
pub rx: Option<bool>,
pub tx: Option<bool>,
pub xc: Option<bool>,
pub xca: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct FrequencyRemoved {
pub frequency: Frequency,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct StationStateEnvelope {
#[serde(rename = "type")]
pub msg_type: String,
pub value: StationState,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct StationStates {
pub stations: Vec<StationStateEnvelope>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct TxBegin {}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct TxEnd {}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct RxBegin {
pub callsign: String,
#[serde(rename = "pFrequencyHz")]
pub frequency: Frequency,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct RxEnd {
pub callsign: String,
#[serde(rename = "pFrequencyHz")]
pub frequency: Frequency,
#[serde(default, rename = "activeTransmitters")]
pub active_transmitters: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct MainVolumeChange {
pub volume: f32,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[allow(dead_code)]
#[deprecated(
since = "0.1.0",
note = "This payload is deprecated by TrackAudio. Use StationState instead."
)]
pub struct FrequencyStateUpdate {
#[allow(deprecated)]
rx: Vec<FrequencyState>,
#[allow(deprecated)]
tx: Vec<FrequencyState>,
#[allow(deprecated)]
xc: Vec<FrequencyState>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[deprecated(
since = "0.1.0",
note = "This payload is deprecated by TrackAudio. Use StationState instead."
)]
pub struct FrequencyState {
#[serde(rename = "pCallsign")]
pub callsign: String,
#[serde(rename = "pFrequencyHz")]
pub frequency: Frequency,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DisconnectReason {
Shutdown,
ManualReconnect,
PingFailed(String),
CommandSendFailed(String),
PongFailed(String),
ClosedByPeer {
code: Option<u16>,
reason: Option<String>,
},
WebSocketError(String),
StreamEnded,
ConnectionFailed(String),
}
impl std::fmt::Display for DisconnectReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Shutdown => write!(f, "Shutdown requested"),
Self::ManualReconnect => write!(f, "Manual reconnection requested"),
Self::PingFailed(err) => write!(f, "Failed to send ping: {err}"),
Self::CommandSendFailed(err) => write!(f, "Failed to send command: {err}"),
Self::PongFailed(err) => write!(f, "Failed to send pong: {err}"),
Self::ClosedByPeer { code, reason } => {
write!(f, "WebSocket closed by peer")?;
if let Some(code) = code {
write!(f, " (code: {code})")?;
}
if let Some(reason) = reason {
if !reason.is_empty() {
write!(f, ": {reason}")?;
}
}
Ok(())
}
Self::WebSocketError(err) => write!(f, "WebSocket error: {err}"),
Self::StreamEnded => write!(f, "WebSocket stream ended unexpectedly"),
Self::ConnectionFailed(err) => write!(f, "Connection failed: {err}"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConnectionState {
Connecting {
attempt: usize,
},
Connected,
Disconnected {
reason: DisconnectReason,
},
Reconnecting {
attempt: usize,
next_delay: Duration,
},
ReconnectFailed {
attempts: usize,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum ClientEvent {
ConnectionStateChanged(ConnectionState),
CommandSendFailed {
command: Command,
error: String,
},
EventDeserializationFailed {
raw: String,
error: String,
},
}