use crate::builtin_interfaces::Time;
use crate::cdr::*;
#[derive(PartialEq, Clone, Copy, Debug)]
pub struct ColorRGBA {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl CdrFixed for ColorRGBA {
const CDR_SIZE: usize = 16; fn read_cdr(cursor: &mut CdrCursor<'_>) -> Result<Self, CdrError> {
Ok(ColorRGBA {
r: cursor.read_f32()?,
g: cursor.read_f32()?,
b: cursor.read_f32()?,
a: cursor.read_f32()?,
})
}
fn write_cdr(&self, writer: &mut CdrWriter<'_>) {
writer.write_f32(self.r);
writer.write_f32(self.g);
writer.write_f32(self.b);
writer.write_f32(self.a);
}
fn size_cdr(sizer: &mut CdrSizer) {
sizer.size_f32();
sizer.size_f32();
sizer.size_f32();
sizer.size_f32();
}
}
pub struct Header<B> {
buf: B,
offsets: [usize; 1],
}
impl<B: AsRef<[u8]>> Header<B> {
pub fn from_cdr(buf: B) -> Result<Self, CdrError> {
let mut c = CdrCursor::new(buf.as_ref())?;
c.skip(8)?;
let _ = c.read_string()?;
Ok(Header {
offsets: [c.offset()],
buf,
})
}
pub fn stamp(&self) -> Time {
rd_time(self.buf.as_ref(), CDR_HEADER_SIZE)
}
pub fn frame_id(&self) -> &str {
rd_string(self.buf.as_ref(), CDR_HEADER_SIZE + 8).0
}
pub fn end_offset(&self) -> usize {
self.offsets[0]
}
pub fn as_cdr(&self) -> &[u8] {
self.buf.as_ref()
}
pub fn cdr_size(&self) -> usize {
self.buf.as_ref().len()
}
pub fn to_cdr(&self) -> Vec<u8> {
self.buf.as_ref().to_vec()
}
}
impl Header<Vec<u8>> {
#[deprecated(
since = "3.2.0",
note = "use Header::builder() for allocation-free buffer reuse; Header::new will be removed in 4.0"
)]
pub fn new(stamp: Time, frame_id: &str) -> Result<Self, CdrError> {
let mut sizer = CdrSizer::new();
Time::size_cdr(&mut sizer);
sizer.size_string(frame_id);
let mut buf = vec![0u8; sizer.size()];
let mut w = CdrWriter::new(&mut buf)?;
stamp.write_cdr(&mut w);
w.write_string(frame_id);
let offsets = [w.offset()];
w.finish()?;
Ok(Header { buf, offsets })
}
pub fn into_cdr(self) -> Vec<u8> {
self.buf
}
pub fn builder<'a>() -> HeaderBuilder<'a> {
HeaderBuilder::new()
}
}
pub struct HeaderBuilder<'a> {
stamp: Time,
frame_id: std::borrow::Cow<'a, str>,
}
impl<'a> Default for HeaderBuilder<'a> {
fn default() -> Self {
Self {
stamp: Time { sec: 0, nanosec: 0 },
frame_id: std::borrow::Cow::Borrowed(""),
}
}
}
impl<'a> HeaderBuilder<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn stamp(&mut self, t: Time) -> &mut Self {
self.stamp = t;
self
}
pub fn frame_id(&mut self, s: impl Into<std::borrow::Cow<'a, str>>) -> &mut Self {
self.frame_id = s.into();
self
}
fn size(&self) -> usize {
let mut s = CdrSizer::new();
Time::size_cdr(&mut s);
s.size_string(&self.frame_id);
s.size()
}
fn write_into(&self, buf: &mut [u8]) -> Result<(), CdrError> {
let mut w = CdrWriter::new(buf)?;
self.stamp.write_cdr(&mut w);
w.write_string(&self.frame_id);
w.finish()
}
pub fn build(&self) -> Result<Header<Vec<u8>>, CdrError> {
let mut buf = vec![0u8; self.size()];
self.write_into(&mut buf)?;
Header::from_cdr(buf)
}
pub fn encode_into_vec(&self, buf: &mut Vec<u8>) -> Result<(), CdrError> {
buf.resize(self.size(), 0);
self.write_into(buf)
}
pub fn encode_into_slice(&self, buf: &mut [u8]) -> Result<usize, CdrError> {
let need = self.size();
if buf.len() < need {
return Err(CdrError::BufferTooShort {
need,
have: buf.len(),
});
}
self.write_into(&mut buf[..need])?;
Ok(need)
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> Header<B> {
pub fn set_stamp(&mut self, t: Time) -> Result<(), CdrError> {
let b = self.buf.as_mut();
wr_i32(b, CDR_HEADER_SIZE, t.sec)?;
wr_u32(b, CDR_HEADER_SIZE + 4, t.nanosec)
}
}
pub fn is_type_supported(type_name: &str) -> bool {
matches!(type_name, "Header" | "ColorRGBA")
}
pub fn list_types() -> &'static [&'static str] {
&["std_msgs/msg/Header", "std_msgs/msg/ColorRGBA"]
}
use crate::schema_registry::SchemaType;
impl SchemaType for ColorRGBA {
const SCHEMA_NAME: &'static str = "std_msgs/msg/ColorRGBA";
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
use crate::builtin_interfaces::Time;
#[test]
fn header_roundtrip() {
let cases = [
(0, 0, "", "empty"),
(100, 500_000_000, "camera", "typical"),
(42, 0, "a]long_frame_id", "with special chars"),
(1, 0, "camera/optical_frame", "path separator"),
(i32::MAX, 999_999_999, "max_frame", "max time"),
];
for (sec, nanosec, frame_id, name) in cases {
let header = Header::new(Time::new(sec, nanosec), frame_id).unwrap();
assert_eq!(
header.stamp(),
Time::new(sec, nanosec),
"stamp failed: {}",
name
);
assert_eq!(header.frame_id(), frame_id, "frame_id failed: {}", name);
let bytes = header.to_cdr();
let decoded = Header::from_cdr(bytes).unwrap();
assert_eq!(
decoded.stamp(),
Time::new(sec, nanosec),
"rt stamp failed: {}",
name
);
assert_eq!(decoded.frame_id(), frame_id, "rt frame_id failed: {}", name);
}
}
#[test]
fn header_set_stamp() {
let mut header = Header::new(Time::new(0, 0), "test").unwrap();
header.set_stamp(Time::new(42, 123)).unwrap();
assert_eq!(header.stamp(), Time::new(42, 123));
}
#[test]
fn color_rgba_roundtrip() {
use crate::cdr::{decode_fixed, encode_fixed};
let cases = [
(0.0, 0.0, 0.0, 0.0, "zero/transparent"),
(1.0, 0.0, 0.0, 1.0, "red"),
(0.0, 1.0, 0.0, 1.0, "green"),
(0.0, 0.0, 1.0, 1.0, "blue"),
(1.0, 1.0, 1.0, 1.0, "white"),
(0.5, 0.5, 0.5, 0.5, "gray semi-transparent"),
];
for (r, g, b, a, name) in cases {
let color = ColorRGBA { r, g, b, a };
let bytes = encode_fixed(&color).unwrap();
let decoded: ColorRGBA = decode_fixed(&bytes).unwrap();
assert_eq!(color, decoded, "failed for case: {}", name);
}
}
}