Skip to content

Commit

Permalink
Added no_std and alloc. Closes #9
Browse files Browse the repository at this point in the history
  • Loading branch information
squidpickles committed Sep 17, 2022
1 parent 6b5485e commit cdd7b6e
Show file tree
Hide file tree
Showing 23 changed files with 576 additions and 138 deletions.
13 changes: 11 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,18 @@ categories = ["parser-implementations"]
license = "Apache-2.0"
edition = "2021"

[features]
std = ["nom/std"]
alloc = ["nom/alloc"]
default = ["std"]

[dependencies]
thiserror = "1"
nom = "7"
nom = { version = "7", default-features = false }
heapless = { version = "0.7" }

[[bin]]
name = "aisparser"
required-features = ["std"]

[badges]
travis-ci = { repository = "squidpickles/ais", branch = "master" }
10 changes: 6 additions & 4 deletions src/bin/ais.rs → src/bin/aisparser.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use ais::lib;

use ais::sentence::{AisFragments, AisParser};
use std::io::BufRead;
use lib::std::io::BufRead;

use std::io;
use lib::std::io;

fn parse_nmea_line(parser: &mut AisParser, line: &[u8]) -> Result<(), ais::errors::Error> {
let sentence = parser.parse(line, true)?;
if let AisFragments::Complete(sentence) = sentence {
println!(
"{:?}\t{:?}",
std::str::from_utf8(line).unwrap(),
lib::std::str::from_utf8(line).unwrap(),
sentence.message
);
}
Expand All @@ -26,7 +28,7 @@ fn main() {
.map(|line| line.unwrap())
.for_each(|line| {
parse_nmea_line(&mut parser, &line).unwrap_or_else(|err| {
eprintln!("{:?}\t{:?}", std::str::from_utf8(&line).unwrap(), err);
eprintln!("{:?}\t{:?}", lib::std::str::from_utf8(&line).unwrap(), err);
});
});
}
Expand Down
108 changes: 74 additions & 34 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,90 @@
//! Custom error types used by this crate
use thiserror::Error;

/// Custom `Result` to prepopulate `Error` type
pub type Result<T> = std::result::Result<T, Error>;

/// A general error in parsing an AIS message
#[derive(Error, Debug)]
pub enum Error {
#[error("invalid NMEA sentence: '{msg}'")]
Nmea { msg: String },
#[error("checksum mismatch; expected: {expected:#X}, received: {found:#X}")]
Checksum { expected: u8, found: u8 },
}
impl From<&str> for Error {
fn from(err: &str) -> Self {
Self::Nmea { msg: err.into() }
pub use err::*;

#[cfg(any(feature = "std", feature = "alloc"))]
mod err {
use crate::lib;
use lib::std::format;
use lib::std::string::{String, ToString};

/// Custom `Result` to prepopulate `Error` type
pub type Result<T> = lib::std::result::Result<T, Error>;
/// A general error in parsing an AIS message
#[derive(Debug)]
pub enum Error {
//#[error("invalid NMEA sentence: '{msg}'")]
Nmea { msg: String },
//#[error("checksum mismatch; expected: {expected:#X}, received: {found:#X}")]
Checksum { expected: u8, found: u8 },
}

impl From<&str> for Error {
fn from(err: &str) -> Self {
Self::Nmea { msg: err.into() }
}
}
}

impl From<String> for Error {
fn from(err: String) -> Self {
Self::Nmea { msg: err }
impl From<String> for Error {
fn from(err: String) -> Self {
Self::Nmea { msg: err }
}
}
}

impl From<nom::Err<&[u8]>> for Error {
fn from(err: nom::Err<&[u8]>) -> Self {
Self::Nmea {
msg: err.to_string(),
impl From<nom::Err<&[u8]>> for Error {
fn from(err: nom::Err<&[u8]>) -> Self {
Self::Nmea {
msg: err.to_string(),
}
}
}
}

impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for Error {
fn from(err: nom::Err<(&[u8], nom::error::ErrorKind)>) -> Self {
Self::Nmea {
msg: err.to_string(),
impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for Error {
fn from(err: nom::Err<(&[u8], nom::error::ErrorKind)>) -> Self {
Self::Nmea {
msg: err.to_string(),
}
}
}

impl<T: lib::std::fmt::Debug> From<nom::Err<nom::error::Error<T>>> for Error {
fn from(err: nom::Err<nom::error::Error<T>>) -> Self {
Self::Nmea {
msg: format!("{:?}", err),
}
}
}
}

impl<T: std::fmt::Debug> From<nom::Err<nom::error::Error<T>>> for Error {
fn from(err: nom::Err<nom::error::Error<T>>) -> Self {
Self::Nmea {
msg: format!("{:?}", err),
#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
mod err {
use crate::lib;

/// Custom `Result` to prepopulate `Error` type
pub type Result<T> = lib::std::result::Result<T, Error>;
/// A general error in parsing an AIS message
#[derive(Debug)]
pub enum Error {
//#[error("invalid NMEA sentence: '{msg}'")]
Nmea { msg: &'static str },
//#[error("checksum mismatch; expected: {expected:#X}, received: {found:#X}")]
Checksum { expected: u8, found: u8 },
}

impl From<&'static str> for Error {
fn from(err: &'static str) -> Self {
Self::Nmea { msg: err }
}
}

impl<T> From<nom::Err<T>> for Error {
fn from(err: nom::Err<T>) -> Self {
let err_str = match err {
nom::Err::Incomplete(_) => "Incomplete data",
nom::Err::Error(_) => "Parser error",
nom::Err::Failure(_) => "Parser unrecoverable failure",
};
Self::Nmea { msg: err_str }
}
}
}
66 changes: 66 additions & 0 deletions src/errors_nostd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! Custom error types used by this crate
use crate::lib;
use lib::std::format;
use lib::std::string::{String, ToString};

/// Custom `Result` to prepopulate `Error` type
pub type Result<T> = lib::std::result::Result<T, Error>;
/// A general error in parsing an AIS message
#[derive(Debug)]
pub enum Error {
//#[error("invalid NMEA sentence: '{msg}'")]
Nmea { msg: &'static str },
//#[error("checksum mismatch; expected: {expected:#X}, received: {found:#X}")]
Checksum { expected: u8, found: u8 },
}

impl From<&'static str> for Error {
fn from(err: &str) -> Self {
Self::Nmea { msg: err }
}
}

impl From<String> for Error {
fn from(err: String) -> Self {
Self::Nmea { msg: err }
}
}

#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
impl<T> From<nom::Err<T>> for Error {
fn from(err: nom::Err<T>) -> Self {
let err_str = match err {
nom::Err::Incomplete(_) => "Incomplete data",
nom::Err::Error(_) => "Parser error",
nom::Err::Failure(_) => "Parser unrecoverable failure",
};
Self::Nmea { msg: err_str }
}
}

impl<T> From<nom::Err<T>> for Error {
fn from(err: nom::Err<T>) -> Self {
let err_str = match err {
nom::Err::Incomplete(_) => "Incomplete data",
nom::Err::Error(_) => "Parser error",
nom::Err::Failure(_) => "Parser unrecoverable failure",
};
Self::Nmea { msg: err_str }
}
}

impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for Error {
fn from(err: nom::Err<(&[u8], nom::error::ErrorKind)>) -> Self {
Self::Nmea {
msg: err.to_string(),
}
}
}

impl<T: lib::std::fmt::Debug> From<nom::Err<nom::error::Error<T>>> for Error {
fn from(err: nom::Err<nom::error::Error<T>>) -> Self {
Self::Nmea {
msg: format!("{:?}", err),
}
}
}
46 changes: 45 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,62 @@
//! }
//! # Ok::<(), ais::errors::Error>(())
//! ```
#![cfg_attr(not(feature = "std"), no_std)]

#[doc(hidden)]
/// standard library stuff available crate-wide, regardless of `no_std` state
pub mod lib {
#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
pub mod std {
pub use core::{borrow, cmp, fmt, mem, result, str};

pub mod vec {
pub use heapless::Vec;
}

pub mod string {
pub use heapless::String;
}

pub trait Error: fmt::Debug + fmt::Display {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
}
#[cfg(all(not(feature = "std"), feature = "alloc"))]
pub mod std {
extern crate alloc;
pub use alloc::{borrow, fmt, format, str, string, vec};
pub use core::{cmp, mem, result};

pub trait Error: fmt::Debug + fmt::Display {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
}

#[cfg(feature = "std")]
pub mod std {
#[doc(hidden)]
pub use std::{borrow, cmp, error, fmt, format, io, mem, result, str, string, vec};
}
}

pub mod errors;
pub mod messages;
pub mod sentence;

pub use errors::Result;
pub use sentence::{AisFragments, AisParser};

#[cfg(test)]
mod test_helpers {
#[inline]
/// Compares two `f32`s, assuming they are both numeric, and panics if they differ
pub fn f32_equal_naive(a: f32, b: f32) {
if (a - b).abs() >= std::f32::EPSILON {
if (a - b).abs() >= f32::EPSILON {
panic!("float {} != {}", a, b);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/messages/aid_to_navigation_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub struct AidToNavigationReport {
pub repeat_indicator: u8,
pub mmsi: u32,
pub aid_type: Option<NavaidType>,
pub name: String,
pub name: AsciiString,
pub accuracy: Accuracy,
pub longitude: Option<f32>,
pub latitude: Option<f32>,
Expand All @@ -112,7 +112,7 @@ impl<'a> AisMessageType<'a> for AidToNavigationReport {
"Aid to Navigation Report"
}

fn parse(data: &[u8]) -> Result<Self> {
fn parse(data: &'a [u8]) -> Result<Self> {
let (_, report) = parse_message(data)?;
Ok(report)
}
Expand Down Expand Up @@ -177,7 +177,7 @@ mod tests {
fn test_type21_not_extended() {
let bytestream = b"E>kb9II9S@0`8@:9ah;0TahIW@@;Uafb:r5Ih00003vP100";
let bitstream = crate::messages::unarmor(bytestream, 0).unwrap();
let message = AidToNavigationReport::parse(&bitstream).unwrap();
let message = AidToNavigationReport::parse(bitstream.as_ref()).unwrap();
assert_eq!(message.message_type, 21);
assert_eq!(message.repeat_indicator, 0);
assert_eq!(message.mmsi, 993692005);
Expand Down
6 changes: 3 additions & 3 deletions src/messages/base_station_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ mod tests {
fn test_type4() {
let bytestream = b"403OtVAv7=i?;o?IaHE`4Iw020S:";
let bitstream = crate::messages::unarmor(bytestream, 0).unwrap();
let message = BaseStationReport::parse(&bitstream).unwrap();
let message = BaseStationReport::parse(bitstream.as_ref()).unwrap();
assert_eq!(message.message_type, 4);
assert_eq!(message.repeat_indicator, 0);
assert_eq!(message.mmsi, 003669145);
Expand Down Expand Up @@ -120,7 +120,7 @@ mod tests {
fn test_type4_2() {
let bytestream = b"403OviQuMGCqWrRO9>E6fE700@GO";
let bitstream = crate::messages::unarmor(bytestream, 0).unwrap();
let message = BaseStationReport::parse(&bitstream).unwrap();
let message = BaseStationReport::parse(bitstream.as_ref()).unwrap();
assert_eq!(message.message_type, 4);
assert_eq!(message.repeat_indicator, 0);
assert_eq!(message.mmsi, 3669702);
Expand Down Expand Up @@ -148,7 +148,7 @@ mod tests {
fn test_type4_invalid_date() {
let bytestream = b"4h2E:qT47wk?0<tSF0l4Q@000d;@";
let bitstream = crate::messages::unarmor(bytestream, 0).unwrap();
let message = BaseStationReport::parse(&bitstream).unwrap();
let message = BaseStationReport::parse(bitstream.as_ref()).unwrap();
assert_eq!(message.mmsi, 002444006);
assert_eq!(message.year, Some(4161));
assert_eq!(message.month, Some(15));
Expand Down
Loading

0 comments on commit cdd7b6e

Please sign in to comment.