Skip to content

Handle APRS packets consisting of arbitrary bytes #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extern crate aprs_parser;

fn main() {
let result = aprs_parser::parse(
r"ICA3D17F2>APRS,qAS,dl4mea:/074849h4821.61N\01224.49E^322/103/A=003054"
br"ICA3D17F2>APRS,qAS,dl4mea:/074849h4821.61N\01224.49E^322/103/A=003054"
);

println!("{:#?}", result);
Expand Down
2 changes: 1 addition & 1 deletion examples/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ extern crate aprs_parser;

fn main() {
let result = aprs_parser::parse(
r"ICA3D17F2>APRS,qAS,dl4mea:/074849h4821.61N\01224.49E^322/103/A=003054",
br"ICA3D17F2>APRS,qAS,dl4mea:/074849h4821.61N\01224.49E^322/103/A=003054",
);

println!("{:#?}", result);
Expand Down
5 changes: 5 additions & 0 deletions src/bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// functions for working with byte arrays

pub fn parse_bytes<T: std::str::FromStr>(b: &[u8]) -> Option<T> {
std::str::from_utf8(b).ok()?.parse().ok()
}
50 changes: 27 additions & 23 deletions src/callsign.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::convert::TryFrom;
use std::fmt::{Display, Formatter};
use std::str::FromStr;

use AprsError;

Expand All @@ -18,28 +18,25 @@ impl Callsign {
}
}

impl FromStr for Callsign {
type Err = AprsError;
impl TryFrom<&[u8]> for Callsign {
type Error = AprsError;

fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
let delimiter = s.find('-'); //.ok_or_else(|| AprsError::EmptyCallsign(s.to_owned()))?;
if delimiter.is_none() {
return Ok(Callsign::new(s, None));
}

let delimiter = delimiter.unwrap();
if delimiter == 0 {
return Err(AprsError::EmptyCallsign(s.to_owned()));
}
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
let s = std::str::from_utf8(b).map_err(|_| AprsError::NonUtf8Callsign(b.to_owned()))?;

let (call, rest) = s.split_at(delimiter);
let ssid = &rest[1..rest.len()];
match s.split_once('-') {
Some((call, ssid)) => {
if call.is_empty() {
Err(AprsError::EmptyCallsign(s.to_owned()))
} else if ssid.is_empty() {
Err(AprsError::EmptySSID(s.to_owned()))
} else {
Ok(Callsign::new(call, Some(ssid)))
}
}

if ssid.is_empty() {
return Err(AprsError::EmptySSID(s.to_owned()));
None => Ok(Callsign::new(s, None)),
}

Ok(Callsign::new(call, Some(ssid)))
}
}

Expand All @@ -58,29 +55,36 @@ impl Display for Callsign {
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryInto;

#[test]
fn parse_callsign() {
assert_eq!("ABCDEF".parse(), Ok(Callsign::new("ABCDEF", None)));
assert_eq!(
"ABCDEF".as_bytes().try_into(),
Ok(Callsign::new("ABCDEF", None))
);
}

#[test]
fn parse_with_ssid() {
assert_eq!("ABCDEF-42".parse(), Ok(Callsign::new("ABCDEF", Some("42"))));
assert_eq!(
"ABCDEF-42".as_bytes().try_into(),
Ok(Callsign::new("ABCDEF", Some("42")))
);
}

#[test]
fn empty_callsign() {
assert_eq!(
"-42".parse::<Callsign>(),
Callsign::try_from("-42".as_bytes()),
Err(AprsError::EmptyCallsign("-42".to_owned()))
);
}

#[test]
fn empty_ssid() {
assert_eq!(
"ABCDEF-".parse::<Callsign>(),
Callsign::try_from("ABCDEF-".as_bytes()),
Err(AprsError::EmptySSID("ABCDEF-".to_owned()))
);
}
Expand Down
42 changes: 24 additions & 18 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum AprsError {
#[error("Empty Callsign: {0}")]
#[error("Non-UTF8 Callsign: {0:?}")]
NonUtf8Callsign(Vec<u8>),
#[error("Empty Callsign: {0:?}")]
EmptyCallsign(String),
#[error("Empty Callsign SSID: {0}")]
#[error("Empty Callsign SSID: {0:?}")]
EmptySSID(String),
#[error("Invalid Timestamp: {0}")]
InvalidTimestamp(String),
#[error("Unsupported Position Format: {0}")]
UnsupportedPositionFormat(String),
#[error("Invalid Position: {0}")]
InvalidPosition(String),
#[error("Invalid Latitude: {0}")]
InvalidLatitude(String),
#[error("Invalid Longitude: {0}")]
InvalidLongitude(String),
#[error("Invalid Packet: {0}")]
InvalidPacket(String),
#[error("Invalid Message Destination: {0}")]
InvalidMessageDestination(String),
#[error("Invalid Timestamp: {0:?}")]
InvalidTimestamp(Vec<u8>),
#[error("Unsupported Position Format: {0:?}")]
UnsupportedPositionFormat(Vec<u8>),
#[error("Invalid Position: {0:?}")]
InvalidPosition(Vec<u8>),
#[error("Invalid Latitude: {0:?}")]
InvalidLatitude(Vec<u8>),
#[error("Invalid Longitude: {0:?}")]
InvalidLongitude(Vec<u8>),
#[error("Invalid Packet: {0:?}")]
InvalidPacket(Vec<u8>),
#[error("Invalid Message Destination: {0:?}")]
InvalidMessageDestination(Vec<u8>),
#[error("Invalid Message ID: {0:?}")]
InvalidMessageId(Vec<u8>),
}

#[derive(Debug, PartialEq, thiserror::Error)]
#[derive(Debug, thiserror::Error)]
pub enum EncodeError {
#[error("Invalid Latitude: {0}")]
InvalidLatitude(f32),
#[error("Invalid Longitude: {0}")]
InvalidLongitude(f32),
#[error("Invalid Aprs Data")]
InvalidData,
#[error("Invalid Message Addressee: {0:?}")]
InvalidMessageAddressee(Vec<u8>),
#[error(transparent)]
Format(#[from] std::fmt::Error),
Write(#[from] std::io::Error),
}
9 changes: 5 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//!
//! fn main() {
//! let result = aprs_parser::parse(
//! r"ICA3D17F2>APRS,qAS,dl4mea:/074849h4821.61N\01224.49E^322/103/A=003054"
//! br"ICA3D17F2>APRS,qAS,dl4mea:/074849h4821.61N\01224.49E^322/103/A=003054"
//! );
//!
//! println!("{:#?}", result);
Expand Down Expand Up @@ -66,6 +66,7 @@ extern crate thiserror;
#[macro_use]
extern crate approx;

mod bytes;
mod callsign;
mod error;
mod lonlat;
Expand All @@ -74,7 +75,7 @@ mod packet;
mod position;
mod timestamp;

use std::str::FromStr;
use std::convert::TryFrom;

pub use callsign::Callsign;
pub use error::{AprsError, EncodeError};
Expand All @@ -84,6 +85,6 @@ pub use packet::{AprsData, AprsPacket};
pub use position::AprsPosition;
pub use timestamp::Timestamp;

pub fn parse(s: &str) -> Result<AprsPacket, AprsError> {
AprsPacket::from_str(s)
pub fn parse(b: &[u8]) -> Result<AprsPacket, AprsError> {
AprsPacket::try_from(b)
}
Loading