]> code.octet-stream.net Git - m17rt/blobdiff - m17app/src/link_setup.rs
Spruce up the high-level API for specifying addresses for transmission
[m17rt] / m17app / src / link_setup.rs
diff --git a/m17app/src/link_setup.rs b/m17app/src/link_setup.rs
new file mode 100644 (file)
index 0000000..007dc78
--- /dev/null
@@ -0,0 +1,101 @@
+use std::fmt::Display;
+
+use m17core::{
+    address::{Address, Callsign, ALPHABET},
+    protocol::LsfFrame,
+};
+
+use crate::error::M17Error;
+
+pub struct LinkSetup {
+    pub(crate) raw: LsfFrame,
+}
+
+impl LinkSetup {
+    /// Provide a completed LsfFrame.
+    pub fn new_raw(frame: LsfFrame) -> Self {
+        Self { raw: frame }
+    }
+
+    /// Set up an unencrypted voice stream with channel access number 0 and the given source and destination.
+    pub fn new_voice(source: &M17Address, destination: &M17Address) -> Self {
+        Self {
+            raw: LsfFrame::new_voice(source.address(), destination.address()),
+        }
+    }
+
+    /// Set up an unencrypted packet data transmission with channel access number 0 and the given source and destination.
+    pub fn new_packet(source: &M17Address, destination: &M17Address) -> Self {
+        Self {
+            raw: LsfFrame::new_packet(source.address(), destination.address()),
+        }
+    }
+
+    /// Configure the channel access number for this transmission, which may be from 0 to 15 inclusive.
+    pub fn set_channel_access_number(&mut self, channel_access_number: u8) {
+        self.raw.set_channel_access_number(channel_access_number);
+    }
+
+    pub fn lich_part(&self, counter: u8) -> [u8; 5] {
+        let idx = counter as usize;
+        self.raw.0[idx * 5..(idx + 1) * 5].try_into().unwrap()
+    }
+}
+
+/// Station address. High level version of `Address` from core.
+
+#[derive(Debug, Clone)]
+pub struct M17Address(Address);
+
+impl M17Address {
+    pub fn new_broadcast() -> Self {
+        Self(Address::Broadcast)
+    }
+
+    pub fn from_callsign(callsign: &str) -> Result<Self, M17Error> {
+        let trimmed = callsign.trim().to_uppercase();
+        let len = trimmed.len();
+        if len > 9 {
+            return Err(M17Error::CallsignTooLong(len));
+        }
+        let mut address = [b' '; 9];
+        for (i, c) in trimmed.chars().enumerate() {
+            if !c.is_ascii() {
+                return Err(M17Error::InvalidCallsignCharacters(c));
+            }
+            if !ALPHABET.contains(&(c as u8)) {
+                return Err(M17Error::InvalidCallsignCharacters(c));
+            }
+            address[i] = c as u8;
+        }
+        Ok(Self(Address::Callsign(Callsign(address))))
+    }
+
+    pub(crate) fn address(&self) -> &Address {
+        &self.0
+    }
+}
+
+impl Display for M17Address {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self.0 {
+            Address::Invalid => unreachable!(),
+            Address::Callsign(ref callsign) => {
+                write!(
+                    f,
+                    "{}",
+                    callsign
+                        .0
+                        .iter()
+                        .map(|c| *c as char)
+                        .collect::<String>()
+                        .trim()
+                )
+            }
+            Address::Reserved(_) => unreachable!(),
+            Address::Broadcast => {
+                write!(f, "<BROADCAST>")
+            }
+        }
+    }
+}