kenwood_thd75/protocol/
tone.rs1use crate::error::ProtocolError;
12use crate::types::DstarSlot;
13use crate::types::radio_params::{TncBaud, TncMode};
14
15use super::Response;
16
17pub(crate) fn parse_tone(mnemonic: &str, payload: &str) -> Option<Result<Response, ProtocolError>> {
21 match mnemonic {
22 "TN" => Some(parse_tn(payload)),
23 "DC" => Some(parse_dc(payload)),
24 "RT" => Some(parse_rt(payload)),
25 _ => None,
26 }
27}
28
29fn parse_u8_field(s: &str, cmd: &str, field: &str) -> Result<u8, ProtocolError> {
35 s.parse::<u8>().map_err(|_| ProtocolError::FieldParse {
36 command: cmd.to_owned(),
37 field: field.to_owned(),
38 detail: format!("invalid u8: {s:?}"),
39 })
40}
41
42fn parse_tn(payload: &str) -> Result<Response, ProtocolError> {
50 let parts: Vec<&str> = payload.splitn(2, ',').collect();
51 if parts.len() != 2 {
52 return Err(ProtocolError::FieldParse {
53 command: "TN".to_owned(),
54 field: "all".to_owned(),
55 detail: format!("expected mode,setting, got {payload:?}"),
56 });
57 }
58 let mode_raw = parse_u8_field(parts[0], "TN", "mode")?;
59 let mode = TncMode::try_from(mode_raw).map_err(|e| ProtocolError::FieldParse {
60 command: "TN".to_owned(),
61 field: "mode".to_owned(),
62 detail: e.to_string(),
63 })?;
64 let setting_raw = parse_u8_field(parts[1], "TN", "setting")?;
65 let setting = TncBaud::try_from(setting_raw).map_err(|e| ProtocolError::FieldParse {
66 command: "TN".to_owned(),
67 field: "setting".to_owned(),
68 detail: e.to_string(),
69 })?;
70 Ok(Response::TncMode { mode, setting })
71}
72
73fn parse_dc(payload: &str) -> Result<Response, ProtocolError> {
78 let parts: Vec<&str> = payload.splitn(3, ',').collect();
79 if parts.len() != 3 {
80 return Err(ProtocolError::FieldParse {
81 command: "DC".to_owned(),
82 field: "all".to_owned(),
83 detail: format!("expected slot,callsign,suffix, got {payload:?}"),
84 });
85 }
86 let raw_slot = parse_u8_field(parts[0], "DC", "slot")?;
87 let slot = DstarSlot::new(raw_slot).map_err(|e| ProtocolError::FieldParse {
88 command: "DC".into(),
89 field: "slot".into(),
90 detail: e.to_string(),
91 })?;
92 let callsign = parts[1].to_owned();
93 let suffix = parts[2].to_owned();
94 Ok(Response::DstarCallsign {
95 slot,
96 callsign,
97 suffix,
98 })
99}
100
101fn parse_rt(payload: &str) -> Result<Response, ProtocolError> {
106 if payload.is_empty() {
107 return Err(ProtocolError::FieldParse {
108 command: "RT".to_owned(),
109 field: "datetime".to_owned(),
110 detail: "empty datetime payload".to_owned(),
111 });
112 }
113 Ok(Response::RealTimeClock {
114 datetime: payload.to_owned(),
115 })
116}