use crate::error::Result;
use crate::frame::{Frame, Header, MAX_PAYLOAD};
#[macro_use]
mod macros;
mod common;
mod enums;
mod schema;
pub use common::*;
pub use enums::*;
pub use schema::*;
pub trait Message: Sized {
const ID: u32;
const NAME: &'static str;
const CRC_EXTRA: u8;
const WIRE_LEN: usize;
const BASE_FIELDS: &'static [(&'static str, &'static str, u8)];
const DESCRIPTOR: &'static MessageDescriptor<'static>;
fn encode(&self, out: &mut [u8]) -> usize;
fn decode(payload: &[u8]) -> Result<Self>;
}
pub fn encode_message<M: Message>(header: Header, message: &M) -> Result<Frame> {
let mut payload = [0u8; MAX_PAYLOAD];
let len = message.encode(&mut payload);
Frame::encode_v2(header, M::ID, &payload[..len], M::CRC_EXTRA)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RawMessage<'a> {
pub msgid: u32,
pub crc_extra: u8,
pub payload: &'a [u8],
}
impl<'a> RawMessage<'a> {
pub fn to_frame(&self, header: Header) -> Result<Frame> {
Frame::encode_v2(header, self.msgid, self.payload, self.crc_extra)
}
}
pub fn crc_extra(msgid: u32) -> Option<u8> {
COMMON_CRC_EXTRA
.iter()
.find(|(id, _)| *id == msgid)
.map(|(_, crc)| *crc)
}
const COMMON_CRC_EXTRA: &[(u32, u8)] = &[
(0, 50),
(1, 124),
(2, 137),
(4, 237),
(11, 89),
(20, 214),
(21, 159),
(22, 220),
(23, 168),
(24, 24),
(30, 39),
(31, 246),
(32, 185),
(33, 104),
(36, 222),
(40, 230),
(42, 28),
(43, 132),
(44, 221),
(45, 232),
(47, 153),
(51, 196),
(65, 118),
(69, 243),
(73, 38),
(74, 20),
(75, 158),
(76, 152),
(77, 143),
(84, 143),
(86, 5),
(147, 154),
(148, 178),
(242, 104),
(245, 130),
(253, 83),
];
pub(crate) const fn xml_name(name: &str) -> &str {
let bytes = name.as_bytes();
let len = bytes.len();
if len > 0 && bytes[len - 1] == b'_' {
let (head, _) = bytes.split_at(len - 1);
match core::str::from_utf8(head) {
Ok(trimmed) => trimmed,
Err(_) => name,
}
} else {
name
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_registry_resolves_known_ids_and_rejects_others() {
assert_eq!(crc_extra(0), Some(50));
assert_eq!(crc_extra(76), Some(152));
assert_eq!(crc_extra(9999), None);
}
#[test]
fn xml_name_trims_a_keyword_field() {
assert_eq!(xml_name("type_"), "type");
assert_eq!(xml_name("custom_mode"), "custom_mode");
assert_eq!(xml_name(""), "");
}
}