feat: generate typed frontend API contracts

This commit is contained in:
sjg
2026-08-01 11:58:09 +02:00
parent 888f793eb8
commit bbc53d56b0
16 changed files with 504 additions and 38 deletions
+1
View File
@@ -14,5 +14,6 @@ serde_json = { workspace = true }
tracing = { workspace = true }
flate2 = { workspace = true }
uuid = { workspace = true }
ts-rs = { version = "12.0.1", features = ["uuid-impl"] }
sgp4 = "2"
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
+6 -2
View File
@@ -3,13 +3,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
use serde::{Deserialize, Serialize};
use ts_rs::TS;
const SPEED_OF_LIGHT_M_PER_S: f64 = 299_792_458.0;
/// Supported band range in Hz.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
pub struct Band {
#[ts(type = "number")]
pub low_hz: u64,
#[ts(type = "number")]
pub high_hz: u64,
pub tx_allowed: bool,
}
@@ -23,8 +26,9 @@ impl Band {
}
/// Frequency wrapper (Hz).
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, TS)]
pub struct Freq {
#[ts(type = "number")]
pub hz: u64,
}
+10 -8
View File
@@ -6,6 +6,7 @@ use std::future::Future;
use std::pin::Pin;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use crate::radio::freq::{Band, Freq};
use crate::{DynResult, RigMode};
@@ -21,14 +22,14 @@ pub mod response;
pub mod state;
/// How this backend communicates with the rig.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
pub enum RigAccessMethod {
Serial { path: String, baud: u32 },
Tcp { addr: String },
}
/// Static info describing a rig backend.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigInfo {
pub manufacturer: String,
pub model: String,
@@ -37,9 +38,10 @@ pub struct RigInfo {
pub access: RigAccessMethod,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
pub struct RigCapabilities {
#[serde(default = "default_min_freq_step_hz")]
#[ts(type = "number")]
pub min_freq_step_hz: u64,
pub supported_bands: Vec<Band>,
pub supported_modes: Vec<RigMode>,
@@ -314,7 +316,7 @@ pub trait RigSdr: Send {
}
/// Snapshot of a rig's status that every backend can expose.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigStatus {
pub freq: Freq,
pub mode: RigMode,
@@ -330,21 +332,21 @@ pub trait RigStatusProvider {
fn status(&self) -> RigStatus;
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigVfo {
pub entries: Vec<RigVfoEntry>,
/// Index into `entries` for the active VFO, if known.
pub active: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigVfoEntry {
pub name: String,
pub freq: Freq,
pub mode: Option<RigMode>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigTxStatus {
pub power: Option<u8>,
pub limit: Option<u8>,
@@ -352,7 +354,7 @@ pub struct RigTxStatus {
pub alc: Option<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigRxStatus {
pub sig: Option<f64>,
}
+10 -8
View File
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
use crate::radio::freq::Freq;
@@ -12,7 +13,7 @@ use crate::rig::{RigControl, RigInfo, RigRxStatus, RigStatus, RigStatusProvider,
///
/// Flattened into `RigState` and `RigSnapshot` so the JSON wire format is
/// unchanged (backward compatible with existing clients).
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, TS)]
pub struct DecoderConfig {
#[serde(default)]
pub aprs_decode_enabled: bool,
@@ -113,7 +114,7 @@ pub struct RigState {
}
/// Mode supported by the rig.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
pub enum RigMode {
LSB,
USB,
@@ -315,7 +316,7 @@ impl RigState {
}
/// Current filter/DSP state for backends that support runtime filter adjustment.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigFilterState {
pub bandwidth_hz: u32,
pub cw_center_hz: u32,
@@ -355,7 +356,7 @@ pub struct RigFilterState {
pub sam_carrier_sync: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, TS)]
#[serde(rename_all = "lowercase")]
pub enum WfmDenoiseLevel {
Off,
@@ -386,11 +387,12 @@ fn default_wfm_denoise_level() -> WfmDenoiseLevel {
}
/// Spectrum data from SDR backends (FFT magnitude over the full capture bandwidth).
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct SpectrumData {
/// FFT magnitude bins in dBFS, FFT-shifted so DC (centre frequency) is at index N/2.
pub bins: Vec<f32>,
/// Centre frequency of the SDR capture in Hz.
#[ts(type = "number")]
pub center_hz: u64,
/// SDR capture sample rate in Hz; the displayed span is ±sample_rate/2.
pub sample_rate: u32,
@@ -400,7 +402,7 @@ pub struct SpectrumData {
}
/// Live RDS metadata decoded from a WFM broadcast.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, TS)]
pub struct RdsData {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pi: Option<u16>,
@@ -437,7 +439,7 @@ pub struct RdsData {
/// `PartialEq` intentionally ignores `signal_db` so that rapidly-changing
/// signal levels do not cause the main state snapshot to diff on every poll
/// cycle (signal_db flows through the spectrum SSE instead).
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct VchanRdsEntry {
/// Virtual channel UUID.
pub id: Uuid,
@@ -456,7 +458,7 @@ impl PartialEq for VchanRdsEntry {
}
/// Read-only projection of state shared with clients.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
pub struct RigSnapshot {
pub info: RigInfo,
pub status: RigStatus,