Skip to content
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 .github/workflows/generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

- name: Generate tests
run: |
rm c/test/auto_check_*.c c/test/cpp/auto_check_*.cc java/test/auto_check_*.java rust/sbp/tests/auto_check_*.rs
rm c/test/auto_check_*.c c/test/cpp/auto_check_*.cc java/test/auto_check_*.java rust/sbp/tests/integration/auto_check_*.rs
make gen-c gen-java gen-rust

- name: Check generated tests are up to date
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ test-haskell:
test-rust:
$(call announce-begin,"Running Rust tests")
cargo test --verbose --all-features --all-targets
$(call announce-begin,"Running Rust doc tests")
cargo test --doc
$(call announce-begin,"Building Rust examples")
cargo build --examples --verbose --all-features --all-targets
$(call announce-end,"Finished running Rust tests")
Expand Down
4 changes: 4 additions & 0 deletions generator/sbpg/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ def main():
elif args.test_c:
test_c.render_check_suites(output_dir, all_specs)
test_c.render_check_main(output_dir, all_specs)
if args.test_rust:
import sbpg.targets.test_rust as test_rs
test_rs.render_main(output_dir, all_specs)


except KeyboardInterrupt:
pass
Expand Down
15 changes: 5 additions & 10 deletions generator/sbpg/targets/resources/sbp-cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,19 @@ keywords = ["encoding", "parsing"]
[features]
default = []
async = ["futures", "dencode/async"]
sbp_serde = ["serde"]
json = ["sbp_serde", "serde_json", "base64"]
json = ["serde", "serde_json", "base64"]
link = ["slotmap"]

[lib]
path = "src/lib.rs"

[dependencies]
byteorder = "1.2"
bytes = "1.0"
crc16 = "*"
bytes = "1"
crc16 = "0.4"
log = "0.4"
nom = "6.0"
thiserror = "1.0"

[dependencies.swiftnav]
git = "https://github.com/swift-nav/swiftnav-rs"
tag = "v0.6.1"
version = "0.7"
optional = true

[dependencies.slotmap]
Expand All @@ -61,7 +56,7 @@ version = "0.3"
optional = true

[dependencies.dencode]
version = "0.3.0"
version = "0.3"
default-features = false

[dev-dependencies]
Expand Down
183 changes: 97 additions & 86 deletions generator/sbpg/targets/resources/sbp_messages_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//! SBP message definitions.

((*- for m in mods *))
pub mod (((m)));
Expand All @@ -22,194 +23,204 @@ use self::(((p.identifier|mod_name)))::(((m.identifier|camel_case)));
((*- endfor *))
use self::unknown::Unknown;

use crate::serialize::SbpSerialize;
mod lib {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called a prelude? Or is that term just for common imports from an external crate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah I just took the idea from serde_json where they call it lib - https://docs.serde.rs/src/serde_json/lib.rs.html#366
Although they are doing it for a slightly different reason (still internal though)

//! Common imports so we can just `use super::lib::*` in all the message files

pub trait SBPMessage: SbpSerialize {
fn get_message_name(&self) -> &'static str;
fn get_message_type(&self) -> u16;
fn get_sender_id(&self) -> Option<u16>;
pub use std::convert::{TryFrom, TryInto};

pub use crate::wire_format::{WireFormat, PayloadParseError};
pub use crate::sbp_string::{SbpString, Unterminated, NullTerminated, Multipart, DoubleNullTerminated};

#[cfg(feature = "swiftnav")]
pub use crate::time;

pub use super::{ConcreteMessage, Sbp, SbpMessage, TryFromSbpError};
}

use lib::*;

/// Common functionality available to all SBP messages.
pub trait SbpMessage: WireFormat + Clone + Sized {
/// Get the message name.
fn message_name(&self) -> &'static str;
/// Get the message type.
fn message_type(&self) -> u16;
/// Get the sender_id if it is set.
fn sender_id(&self) -> Option<u16>;
/// Set the sender id.
fn set_sender_id(&mut self, new_id: u16);
fn to_frame(&self) -> std::result::Result<Vec<u8>, crate::FramerError>;
fn write_frame(&self, buf: &mut Vec<u8>) -> std::result::Result<(), crate::FramerError>;
/// Get the GPS time associated with the message.
#[cfg(feature = "swiftnav")]
fn gps_time(&self) -> Option<std::result::Result<crate::time::MessageTime, crate::time::GpsTimeError>> {
fn gps_time(&self) -> Option<Result<crate::time::MessageTime, crate::time::GpsTimeError>> {
None
}
}

pub trait ConcreteMessage: SBPMessage + std::convert::TryFrom<SBP, Error = TryFromSBPError> + Clone + Sized {
/// Implemented by messages who's message name and type are known at compile time.
/// This is everything that implements [SbpMessage] except for [Sbp].
pub trait ConcreteMessage: SbpMessage + TryFrom<Sbp, Error = TryFromSbpError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ConcreteMessage is only for use in the sbp crate, should this be marked as pub(crate)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm it can't be marked private because it a appears in Event which needs to be public. Could maybe add #[doc(hidden)]. Depends on if we think having access to message names/ids at compile time is useful. Right now this is the only way to get that

/// The message type.
const MESSAGE_TYPE: u16;
/// The message name.
const MESSAGE_NAME: &'static str;
}

/// The error returned when using [TryFrom] to convert [Sbp] to the wrong message type.
#[derive(Debug, Clone)]
pub struct TryFromSBPError;
pub struct TryFromSbpError;

impl std::fmt::Display for TryFromSBPError {
impl std::fmt::Display for TryFromSbpError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "invalid message type for conversion")
}
}

impl std::error::Error for TryFromSBPError {}
impl std::error::Error for TryFromSbpError {}

#[cfg_attr(feature = "sbp_serde", derive(serde::Serialize), serde(untagged))]
/// Represents any SBP message.
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(untagged))]
#[derive(Debug, Clone)]
pub enum SBP {
#[non_exhaustive]
pub enum Sbp {
((*- for m in msgs *))
/// (((m.short_desc | commentify(indent=2) )))
(((m.identifier|camel_case)))( (((m.identifier|camel_case))) ),
((*- endfor *))
/// Unknown message type
Unknown( Unknown ),
}

impl SBP {
pub fn parse(msg_id: u16, sender_id: u16, payload: &mut &[u8]) -> Result<SBP, crate::Error> {
match msg_id {
impl Sbp {
pub(crate) fn from_frame(mut frame: crate::de::Frame) -> Result<Sbp, PayloadParseError> {
match frame.msg_type {
((*- for m in msgs *))
(((m.sbp_id))) => {
let mut msg = (((m.identifier|camel_case)))::parse(payload)?;
msg.set_sender_id(sender_id);
Ok(SBP::(((m.identifier|camel_case)))(msg))
(((m.identifier|camel_case)))::MESSAGE_TYPE => {
let mut msg = (((m.identifier|camel_case)))::parse(&mut frame.payload)?;
msg.set_sender_id(frame.sender_id);
Ok(Sbp::(((m.identifier|camel_case)))(msg))
},
((*- endfor *))
_ => Ok(SBP::Unknown( Unknown{ msg_id: msg_id, sender_id: sender_id, payload: payload.to_vec() } ))
_ => {
let mut msg = Unknown::parse(&mut frame.payload)?;
msg.set_sender_id(frame.sender_id);
Ok(Sbp::Unknown(msg))
}
}
}
}

impl crate::SBPMessage for SBP {
fn get_message_name(&self) -> &'static str {
impl SbpMessage for Sbp {
fn message_name(&self) -> &'static str {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
msg.get_message_name()
Sbp::(((m.identifier|camel_case)))(msg) => {
msg.message_name()
},
((*- endfor *))
SBP::Unknown(msg) => {
msg.get_message_name()
Sbp::Unknown(msg) => {
msg.message_name()
},
}
}

fn get_message_type(&self) -> u16 {
fn message_type(&self) -> u16 {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
msg.get_message_type()
Sbp::(((m.identifier|camel_case)))(msg) => {
msg.message_type()
},
((*- endfor *))
SBP::Unknown(msg) => {
msg.get_message_type()
Sbp::Unknown(msg) => {
msg.message_type()
},
}
}

fn get_sender_id(&self) -> Option<u16> {
fn sender_id(&self) -> Option<u16> {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
msg.get_sender_id()
Sbp::(((m.identifier|camel_case)))(msg) => {
msg.sender_id()
},
((*- endfor *))
SBP::Unknown(msg) => {
msg.get_sender_id()
Sbp::Unknown(msg) => {
msg.sender_id()
},
}
}

fn set_sender_id(&mut self, new_id: u16) {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
Sbp::(((m.identifier|camel_case)))(msg) => {
msg.set_sender_id(new_id)
},
((*- endfor *))
SBP::Unknown(msg) => {
Sbp::Unknown(msg) => {
msg.set_sender_id(new_id)
},
}
}

fn to_frame(&self) -> Result<Vec<u8>, crate::FramerError> {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
msg.to_frame()
},
((*- endfor *))
SBP::Unknown(msg) => {
msg.to_frame()
},
}
}

fn write_frame(&self, buf: &mut Vec<u8>) -> Result<(), crate::FramerError> {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
msg.write_frame(buf)
},
((*- endfor *))
SBP::Unknown(msg) => {
msg.write_frame(buf)
},
}
}

#[cfg(feature = "swiftnav")]
fn gps_time(&self) -> Option<std::result::Result<crate::time::MessageTime, crate::time::GpsTimeError>> {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
Sbp::(((m.identifier|camel_case)))(msg) => {
msg.gps_time()
},
((*- endfor *))
SBP::Unknown(msg) => {
Sbp::Unknown(msg) => {
msg.gps_time()
},
}
}
}

impl WireFormat for Sbp {
const MIN_ENCODED_LEN: usize = crate::MAX_FRAME_LEN;

fn parse_unchecked(_: &mut bytes::BytesMut) -> Self {
unimplemented!("Sbp must be parsed with Sbp::from_frame");
}

impl crate::SbpSerialize for SBP {
fn append_to_sbp_buffer(&self, buf: &mut Vec<u8>) {
fn write(&self, buf: &mut bytes::BytesMut) {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
msg.append_to_sbp_buffer(buf)
Sbp::(((m.identifier|camel_case)))(msg) => {
WireFormat::write(msg, buf)
},
((*- endfor *))
SBP::Unknown(msg) => {
msg.append_to_sbp_buffer(buf)
Sbp::Unknown(msg) => {
WireFormat::write(msg, buf)
},
}
}

fn sbp_size(&self) -> usize {
fn encoded_len(&self) -> usize {
match self {
((*- for m in msgs *))
SBP::(((m.identifier|camel_case)))(msg) => {
msg.sbp_size()
Sbp::(((m.identifier|camel_case)))(msg) => {
WireFormat::encoded_len(msg)
},
((*- endfor *))
SBP::Unknown(msg) => {
msg.sbp_size()
Sbp::Unknown(msg) => {
WireFormat::encoded_len(msg)
},
}
}
}

((*- for m in msgs *))
impl From<(((m.identifier|camel_case)))> for SBP {
((* for m in msgs *))
impl From<(((m.identifier|camel_case)))> for Sbp {
fn from(msg: (((m.identifier|camel_case)))) -> Self {
SBP::(((m.identifier|camel_case)))(msg)
Sbp::(((m.identifier|camel_case)))(msg)
}
}
((*- endfor *))

impl From<Unknown> for SBP {
}
((* endfor *))
impl From<Unknown> for Sbp {
fn from(msg: Unknown) -> Self {
SBP::Unknown(msg)
Sbp::Unknown(msg)
}
}
Loading