use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WatchPortsOptions {
#[serde(default)]
pub single_port_per_device: Option<bool>,
#[serde(default)]
pub poll_interval_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum PortListEvent {
Snapshot {
ports: std::collections::HashMap<String, std::collections::HashMap<String, String>>,
},
Added {
path: String,
info: std::collections::HashMap<String, String>,
},
Removed { path: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum SerialEvent {
Data {
path: String,
data: Vec<u8>,
size: usize,
},
Disconnect {
path: String,
reason: String,
},
Urc {
path: String,
line: String,
},
Error {
path: String,
message: String,
},
}
#[derive(Debug, Clone, Serialize)]
pub struct Capabilities {
pub transport: &'static str,
pub platform: &'static str,
pub version: &'static str,
}
impl Capabilities {
pub fn current() -> Self {
Self {
transport: if cfg!(mobile) { "mobile" } else { "desktop" },
platform: current_platform(),
version: env!("CARGO_PKG_VERSION"),
}
}
}
const fn current_platform() -> &'static str {
if cfg!(target_os = "windows") {
"windows"
} else if cfg!(target_os = "macos") {
"macos"
} else if cfg!(target_os = "linux") {
"linux"
} else if cfg!(target_os = "android") {
"android"
} else if cfg!(target_os = "ios") {
"ios"
} else {
"unknown"
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WatchOptions {
#[serde(default)]
pub timeout: Option<u64>,
#[serde(default)]
pub size: Option<usize>,
#[serde(default)]
pub serial_data_flush_interval_ms: Option<u64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum RxPrepareMode {
#[default]
Drain,
Purge,
None,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum AtResultFormat {
#[default]
Verbose,
Numeric,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ExchangeCompletionMode {
#[default]
AtFinalLine,
AtIntermediate,
Substring,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExchangeOptions {
#[serde(default)]
pub timeout_ms: Option<u64>,
#[serde(default)]
pub max_bytes: Option<usize>,
#[serde(default)]
pub terminators: Option<Vec<String>>,
#[serde(default)]
pub idle_ms: Option<u64>,
#[serde(default)]
pub rx_prepare: Option<RxPrepareMode>,
#[serde(default)]
pub drain_idle_ms: Option<u64>,
#[serde(default)]
pub drain_max_ms: Option<u64>,
#[serde(default)]
pub completion_mode: Option<ExchangeCompletionMode>,
#[serde(default)]
pub command: Option<String>,
#[serde(default)]
pub solicited_prefixes: Option<Vec<String>>,
#[serde(default)]
pub result_format: Option<AtResultFormat>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serial_event_data_roundtrip() {
let event = SerialEvent::Data {
path: "/dev/ttyUSB0".into(),
data: vec![1, 2, 255],
size: 3,
};
let json = serde_json::to_value(&event).unwrap();
assert_eq!(json["kind"], "data");
assert_eq!(json["path"], "/dev/ttyUSB0");
let back: SerialEvent = serde_json::from_value(json).unwrap();
match back {
SerialEvent::Data { size, .. } => assert_eq!(size, 3),
_ => panic!("expected data"),
}
}
#[test]
fn serial_event_error_roundtrip() {
let event = SerialEvent::Error {
path: "/dev/ttyUSB0".into(),
message: "read glitch".into(),
};
let json = serde_json::to_value(&event).unwrap();
assert_eq!(json["kind"], "error");
assert_eq!(json["message"], "read glitch");
let back: SerialEvent = serde_json::from_value(json).unwrap();
match back {
SerialEvent::Error { message, .. } => assert_eq!(message, "read glitch"),
_ => panic!("expected error"),
}
}
#[test]
fn serial_event_disconnect_roundtrip() {
let event = SerialEvent::Disconnect {
path: "/dev/ttyUSB0".into(),
reason: "USB unplugged".into(),
};
let json = serde_json::to_value(&event).unwrap();
assert_eq!(json["kind"], "disconnect");
assert_eq!(json["reason"], "USB unplugged");
let back: SerialEvent = serde_json::from_value(json).unwrap();
match back {
SerialEvent::Disconnect { reason, .. } => assert_eq!(reason, "USB unplugged"),
_ => panic!("expected disconnect"),
}
}
#[test]
fn capabilities_has_version() {
let caps = Capabilities::current();
assert!(!caps.version.is_empty());
assert!(caps.transport == "desktop" || caps.transport == "mobile");
}
}