diff --git a/justfile b/justfile index 704b921..77f1b55 100644 --- a/justfile +++ b/justfile @@ -1,4 +1,4 @@ -all: check clippy fmt build docs +all: check build test clippy fmt docs clippy: cargo clippy -- -D warnings @@ -9,6 +9,10 @@ fmt: check: cargo check --all-features +test: + cargo nextest r --all-features + cargo test --doc + build: cargo build --all-features diff --git a/src/ecss/mod.rs b/src/ecss/mod.rs index b0dae80..ef54f45 100644 --- a/src/ecss/mod.rs +++ b/src/ecss/mod.rs @@ -260,21 +260,6 @@ pub fn verify_crc16_ccitt_false_from_raw_no_table(raw_data: &[u8]) -> bool { false } -macro_rules! ccsds_impl { - () => { - delegate!(to self.sp_header { - #[inline] - fn ccsds_version(&self) -> u3; - #[inline] - fn packet_id(&self) -> crate::PacketId; - #[inline] - fn psc(&self) -> crate::PacketSequenceControl; - #[inline] - fn data_len(&self) -> u16; - }); - } -} - macro_rules! sp_header_impls { () => { delegate!(to self.sp_header { @@ -289,7 +274,6 @@ macro_rules! sp_header_impls { } use crate::util::{GenericUnsignedByteField, ToBeBytes, UnsignedEnum}; -pub(crate) use ccsds_impl; pub(crate) use sp_header_impls; /// Generic trait for ECSS enumeration which consist of a PFC field denoting their bit length diff --git a/src/ecss/tc.rs b/src/ecss/tc.rs index dc867b5..ba3c7bf 100644 --- a/src/ecss/tc.rs +++ b/src/ecss/tc.rs @@ -5,7 +5,6 @@ //! //! ```rust //! use spacepackets::SpHeader; -//! use spacepackets::ecss::WritablePusPacket; //! use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader, CreatorConfig}; //! use arbitrary_int::u11; //! @@ -45,7 +44,7 @@ use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; pub use crate::ecss::CreatorConfig; use crate::ecss::{ - ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw, + crc_from_raw_data, sp_header_impls, user_data_from_raw, verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion, WritablePusPacket, }; @@ -497,7 +496,16 @@ impl WritablePusPacket for PusTcCreator<'_> { } impl CcsdsPacket for PusTcCreator<'_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTcCreator<'_> { @@ -708,6 +716,7 @@ impl<'buf> PusTcCreatorWithReservedAppData<'buf> { } } +#[derive(Debug)] pub struct PusTcBuilder<'a> { sp_header: SpHeader, sec_header: PusTcSecondaryHeader, @@ -948,7 +957,16 @@ impl PartialEq for PusTcReader<'_> { } impl CcsdsPacket for PusTcReader<'_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTcReader<'_> { diff --git a/src/ecss/tc_pus_a.rs b/src/ecss/tc_pus_a.rs index 24b4c64..bf6f6fc 100644 --- a/src/ecss/tc_pus_a.rs +++ b/src/ecss/tc_pus_a.rs @@ -36,7 +36,7 @@ use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; use crate::ecss::tc::{AckFlags, ACK_ALL}; use crate::ecss::{ - ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw, + crc_from_raw_data, sp_header_impls, user_data_from_raw, verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion, WritablePusPacket, }; @@ -483,7 +483,16 @@ impl WritablePusPacket for PusTcCreator<'_> { } impl CcsdsPacket for PusTcCreator<'_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTcCreator<'_> { @@ -770,7 +779,16 @@ impl PartialEq for PusTcReader<'_> { } impl CcsdsPacket for PusTcReader<'_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTcReader<'_> { diff --git a/src/ecss/tm.rs b/src/ecss/tm.rs index eb11e39..ee995e7 100644 --- a/src/ecss/tm.rs +++ b/src/ecss/tm.rs @@ -4,10 +4,8 @@ //! # Examples //! //! ```rust -//! use spacepackets::time::TimeWriter; //! use spacepackets::time::cds::CdsTime; -//! use spacepackets::{CcsdsPacket, SpHeader}; -//! use spacepackets::ecss::{PusPacket, WritablePusPacket}; +//! use spacepackets::SpHeader; //! use spacepackets::ecss::tm::{PusTmCreator, PusTmReader, PusTmSecondaryHeader, CreatorConfig}; //! use arbitrary_int::u11; //! @@ -42,17 +40,26 @@ //! assert_eq!(ping_tm_reader.subservice(), 2); //! assert_eq!(ping_tm_reader.apid().value(), 0x02); //! assert_eq!(ping_tm_reader.timestamp(), &time_buf); +//! +//! // Alternative builder API +//! let pus_tm_by_builder = PusTmCreator::builder() +//! .with_service(17) +//! .with_subservice(2) +//! .with_apid(u11::new(0x02)) +//! .with_timestamp(&time_buf) +//! .build(); +//! assert_eq!(pus_tm_by_builder, ping_tm); //! ``` use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; pub use crate::ecss::CreatorConfig; use crate::ecss::{ - calc_pus_crc16, ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw, + calc_pus_crc16, crc_from_raw_data, sp_header_impls, user_data_from_raw, verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion, WritablePusPacket, }; use crate::{ - ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SpHeader, CCSDS_HEADER_LEN, - MAX_APID, + ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl, PacketType, SequenceFlags, + SpHeader, CCSDS_HEADER_LEN, MAX_APID, }; use arbitrary_int::traits::Integer; use arbitrary_int::{u11, u14, u3, u4}; @@ -344,8 +351,12 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> { Self::new(sp_header, sec_header, &[], packet_config) } + pub fn builder() -> PusTmBuilder<'time, 'src_data> { + PusTmBuilder::new() + } + #[inline] - fn has_checksum(&self) -> bool { + pub fn has_checksum(&self) -> bool { self.has_checksum } @@ -359,6 +370,21 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> { self.source_data } + #[inline] + pub fn service(&self) -> u8 { + self.sec_header.service + } + + #[inline] + pub fn subservice(&self) -> u8 { + self.sec_header.subservice + } + + #[inline] + pub fn apid(&self) -> u11 { + self.sp_header.packet_id.apid + } + #[inline] pub fn set_dest_id(&mut self, dest_id: u16) { self.sec_header.dest_id = dest_id; @@ -516,7 +542,16 @@ impl PartialEq for PusTmCreator<'_, '_> { } impl CcsdsPacket for PusTmCreator<'_, '_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTmCreator<'_, '_> { @@ -570,6 +605,126 @@ impl GenericPusTmSecondaryHeader for PusTmCreator<'_, '_> { impl IsPusTelemetry for PusTmCreator<'_, '_> {} +#[derive(Debug)] +pub struct PusTmBuilder<'time, 'src_data> { + sp_header: SpHeader, + sec_header: PusTmSecondaryHeader<'time>, + source_data: &'src_data [u8], + has_checksum: bool, +} + +impl Default for PusTmBuilder<'_, '_> { + fn default() -> Self { + Self::new() + } +} + +impl PusTmBuilder<'_, '_> { + pub fn new() -> Self { + Self { + sp_header: SpHeader::new( + PacketId::new_for_tm(true, u11::new(0)), + PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0)), + 0, + ), + sec_header: PusTmSecondaryHeader::new(0, 0, 0, 0, &[]), + source_data: &[], + has_checksum: true, + } + } + + #[inline] + pub fn with_apid(mut self, apid: u11) -> Self { + self.sp_header.packet_id.set_apid(apid); + self + } + + #[inline] + pub fn with_packet_id(mut self, mut packet_id: PacketId) -> Self { + packet_id.packet_type = PacketType::Tc; + self.sp_header.packet_id = packet_id; + self + } + + #[inline] + pub fn with_packet_sequence_control(mut self, psc: PacketSequenceControl) -> Self { + self.sp_header.psc = psc; + self + } + + #[inline] + pub fn with_sequence_count(mut self, seq_count: u14) -> Self { + self.sp_header.psc.seq_count = seq_count; + self + } + + #[inline] + pub fn with_service(mut self, service: u8) -> Self { + self.sec_header.service = service; + self + } + + #[inline] + pub fn with_subservice(mut self, service: u8) -> Self { + self.sec_header.subservice = service; + self + } + + #[inline] + pub fn with_dest_id(mut self, dest_id: u16) -> Self { + self.sec_header.dest_id = dest_id; + self + } + + #[inline] + pub fn with_msg_counter(mut self, msg_counter: u16) -> Self { + self.sec_header.msg_counter = msg_counter; + self + } + + #[inline] + pub fn with_sc_time_ref_status(mut self, sc_time_ref_status: u4) -> Self { + self.sec_header.sc_time_ref_status = sc_time_ref_status; + self + } + + #[inline] + pub fn with_checksum(mut self, has_checksum: bool) -> Self { + self.has_checksum = has_checksum; + self + } +} + +impl<'src_data> PusTmBuilder<'_, 'src_data> { + #[inline] + pub fn with_source_data(mut self, source_data: &'src_data [u8]) -> Self { + self.source_data = source_data; + self + } +} + +impl<'time> PusTmBuilder<'time, '_> { + #[inline] + pub fn with_timestamp(mut self, timestamp: &'time [u8]) -> Self { + self.sec_header.timestamp = timestamp; + self + } +} + +impl<'time, 'src_data> PusTmBuilder<'time, 'src_data> { + pub fn build(self) -> PusTmCreator<'time, 'src_data> { + PusTmCreator::new( + self.sp_header, + self.sec_header, + self.source_data, + CreatorConfig { + has_checksum: self.has_checksum, + set_ccsds_len: true, + }, + ) + } +} + /// A specialized variant of [PusTmCreator] designed for efficiency when handling large source /// data. /// @@ -882,6 +1037,26 @@ impl<'raw_data> PusTmReader<'raw_data> { self.user_data() } + #[inline] + pub fn service(&self) -> u8 { + self.sec_header.service + } + + #[inline] + pub fn subservice(&self) -> u8 { + self.sec_header.subservice + } + + #[inline] + pub fn packet_len(&self) -> usize { + self.sp_header.packet_len() + } + + #[inline] + pub fn apid(&self) -> u11 { + self.sp_header.packet_id.apid + } + #[inline] pub fn timestamp(&self) -> &[u8] { self.sec_header.timestamp @@ -909,7 +1084,16 @@ impl PartialEq for PusTmReader<'_> { } impl CcsdsPacket for PusTmReader<'_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTmReader<'_> { @@ -1171,14 +1355,13 @@ impl GenericPusTmSecondaryHeader for PusTmZeroCopyWriter<'_> { #[cfg(test)] mod tests { - use alloc::string::ToString; - use super::*; use crate::time::cds::CdsTime; #[cfg(feature = "serde")] use crate::time::CcsdsTimeProvider; use crate::SpHeader; use crate::{ecss::PusVersion::PusC, MAX_SEQ_COUNT}; + use alloc::string::ToString; #[cfg(feature = "serde")] use postcard::{from_bytes, to_allocvec}; @@ -1190,6 +1373,28 @@ mod tests { PusTmCreator::new_no_source_data(sph, tm_header, CreatorConfig::default()) } + fn base_ping_reply_full_ctor_builder<'a, 'b>( + timestamp: &'a [u8], + alt_api: bool, + ) -> PusTmCreator<'a, 'b> { + if alt_api { + return PusTmCreator::builder() + .with_apid(u11::new(0x123)) + .with_sequence_count(u14::new(0x234)) + .with_service(17) + .with_subservice(2) + .with_timestamp(timestamp) + .build(); + } + PusTmBuilder::new() + .with_apid(u11::new(0x123)) + .with_sequence_count(u14::new(0x234)) + .with_service(17) + .with_subservice(2) + .with_timestamp(timestamp) + .build() + } + fn base_ping_reply_full_ctor_no_checksum<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> { let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); @@ -1938,4 +2143,41 @@ mod tests { let output_converted_back: PusTmReader = from_bytes(&output).unwrap(); assert_eq!(output_converted_back, tm_reader); } + + #[test] + fn test_builder() { + assert_eq!( + base_ping_reply_full_ctor_builder(dummy_timestamp(), false), + base_ping_reply_full_ctor(dummy_timestamp()) + ); + } + + #[test] + fn test_builder_2() { + assert_eq!( + base_ping_reply_full_ctor_builder(dummy_timestamp(), true), + base_ping_reply_full_ctor(dummy_timestamp()) + ); + } + + #[test] + fn test_builder_3() { + let tm = PusTmBuilder::new() + .with_packet_id(PacketId::new_for_tc(true, u11::new(0x02))) + .with_packet_sequence_control(PacketSequenceControl::new( + SequenceFlags::Unsegmented, + u14::new(0x34), + )) + .with_service(17) + .with_subservice(2) + .with_dest_id(0x2f2f) + .with_checksum(false) + .build(); + assert_eq!(tm.seq_count().value(), 0x34); + assert_eq!(tm.sequence_flags(), SequenceFlags::Unsegmented); + assert_eq!(tm.apid().value(), 0x02); + assert_eq!(tm.packet_type(), PacketType::Tm); + assert_eq!(tm.service(), 17); + assert_eq!(tm.subservice(), 2); + } } diff --git a/src/ecss/tm_pus_a.rs b/src/ecss/tm_pus_a.rs index aeffc1b..7f1367e 100644 --- a/src/ecss/tm_pus_a.rs +++ b/src/ecss/tm_pus_a.rs @@ -49,7 +49,7 @@ //! ``` use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; use crate::ecss::{ - calc_pus_crc16, ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw, + calc_pus_crc16, crc_from_raw_data, sp_header_impls, user_data_from_raw, verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion, WritablePusPacket, }; @@ -541,7 +541,16 @@ impl PartialEq for PusTmCreator<'_, '_> { } impl CcsdsPacket for PusTmCreator<'_, '_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTmCreator<'_, '_> { @@ -840,7 +849,16 @@ impl PartialEq for PusTmReader<'_> { } impl CcsdsPacket for PusTmReader<'_> { - ccsds_impl!(); + delegate!(to self.sp_header { + #[inline] + fn ccsds_version(&self) -> u3; + #[inline] + fn packet_id(&self) -> crate::PacketId; + #[inline] + fn psc(&self) -> crate::PacketSequenceControl; + #[inline] + fn data_len(&self) -> u16; + }); } impl PusPacket for PusTmReader<'_> { diff --git a/src/time/cds.rs b/src/time/cds.rs index 81f10ab..0cfc676 100644 --- a/src/time/cds.rs +++ b/src/time/cds.rs @@ -642,14 +642,12 @@ impl CdsTime { self.unix_time = UnixTime::new(unix_days_seconds, subsec_nanos); } - fn length_check(&self, buf: &[u8], len_as_bytes: usize) -> Result<(), TimestampError> { + fn length_check(&self, buf: &[u8], len_as_bytes: usize) -> Result<(), ByteConversionError> { if buf.len() < len_as_bytes { - return Err(TimestampError::ByteConversion( - ByteConversionError::ToSliceTooSmall { - expected: len_as_bytes, - found: buf.len(), - }, - )); + return Err(ByteConversionError::ToSliceTooSmall { + expected: len_as_bytes, + found: buf.len(), + }); } Ok(()) } @@ -957,6 +955,23 @@ impl CdsTime { Self::from_now_generic_ps_prec(LengthOfDaySegment::Short16Bits) } + pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result { + self.length_check(buf, self.len_as_bytes())?; + buf[0] = self.pfield; + buf[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice()); + buf[3..7].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice()); + match self.submillis_precision() { + SubmillisPrecision::Microseconds => { + buf[7..9].copy_from_slice((self.submillis() as u16).to_be_bytes().as_slice()); + } + SubmillisPrecision::Picoseconds => { + buf[7..11].copy_from_slice(self.submillis().to_be_bytes().as_slice()); + } + _ => (), + } + Ok(self.len_as_bytes()) + } + pub fn from_bytes_with_u16_days(buf: &[u8]) -> Result { let submillis_precision = Self::generic_raw_read_checks(buf, LengthOfDaySegment::Short16Bits)?; @@ -1212,20 +1227,7 @@ impl TimeReader for CdsTime { impl TimeWriter for CdsTime { fn write_to_bytes(&self, buf: &mut [u8]) -> Result { - self.length_check(buf, self.len_as_bytes())?; - buf[0] = self.pfield; - buf[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice()); - buf[3..7].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice()); - match self.submillis_precision() { - SubmillisPrecision::Microseconds => { - buf[7..9].copy_from_slice((self.submillis() as u16).to_be_bytes().as_slice()); - } - SubmillisPrecision::Picoseconds => { - buf[7..11].copy_from_slice(self.submillis().to_be_bytes().as_slice()); - } - _ => (), - } - Ok(self.len_as_bytes()) + self.write_to_bytes(buf).map_err(TimestampError::from) } fn len_written(&self) -> usize { @@ -1233,8 +1235,8 @@ impl TimeWriter for CdsTime { } } -impl TimeWriter for CdsTime { - fn write_to_bytes(&self, buf: &mut [u8]) -> Result { +impl CdsTime { + pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result { self.length_check(buf, self.len_as_bytes())?; buf[0] = self.pfield; let be_days = self.ccsds_days.to_be_bytes(); @@ -1251,6 +1253,12 @@ impl TimeWriter for CdsTime { } Ok(self.len_as_bytes()) } +} + +impl TimeWriter for CdsTime { + fn write_to_bytes(&self, buf: &mut [u8]) -> Result { + self.write_to_bytes(buf).map_err(TimestampError::from) + } fn len_written(&self) -> usize { self.len_as_bytes() @@ -1331,7 +1339,7 @@ mod tests { use super::*; use crate::time::TimestampError::{ByteConversion, InvalidTimeCode}; use crate::time::{UnixTime, DAYS_CCSDS_TO_UNIX, MS_PER_DAY}; - use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall}; + use crate::ByteConversionError::FromSliceTooSmall; use alloc::string::ToString; use chrono::{Datelike, NaiveDate, Timelike}; #[cfg(feature = "serde")] @@ -1486,12 +1494,14 @@ mod tests { assert!(res.is_err()); let error = res.unwrap_err(); match error { - ByteConversion(ToSliceTooSmall { found, expected }) => { + ByteConversionError::ToSliceTooSmall { found, expected } => { assert_eq!(found, i); assert_eq!(expected, 7); assert_eq!( error.to_string(), - format!("time stamp: target slice with size {i} is too small, expected size of at least 7") + format!( + "target slice with size {i} is too small, expected size of at least 7" + ) ); } _ => panic!( @@ -1537,10 +1547,7 @@ mod tests { if let InvalidTimeCode { expected, found } = err { assert_eq!(expected, CcsdsTimeCode::Cds); assert_eq!(found, 0); - assert_eq!( - err.to_string(), - "invalid raw time code value 0 for time code Cds" - ); + assert_eq!(err.to_string(), "invalid time code, expected Cds, found 0"); } } @@ -2301,7 +2308,7 @@ mod tests { fn test_serialization() { let stamp_now = CdsTime::now_with_u16_days().expect("Error retrieving time"); let val = to_allocvec(&stamp_now).expect("Serializing timestamp failed"); - assert!(val.len() > 0); + assert!(!val.is_empty()); let stamp_deser: CdsTime = from_bytes(&val).expect("Stamp deserialization failed"); assert_eq!(stamp_deser, stamp_now); } diff --git a/src/time/cuc.rs b/src/time/cuc.rs index eec2801..503f2ef 100644 --- a/src/time/cuc.rs +++ b/src/time/cuc.rs @@ -6,7 +6,7 @@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use core::fmt::{Debug, Display, Formatter}; +use core::fmt::Debug; use core::ops::{Add, AddAssign}; use core::time::Duration; @@ -20,8 +20,6 @@ use super::{ TimestampError, UnixTime, }; #[cfg(feature = "std")] -use std::error::Error; -#[cfg(feature = "std")] use std::time::SystemTime; #[cfg(feature = "chrono")] @@ -103,63 +101,24 @@ pub fn fractional_part_from_subsec_ns(res: FractionalResolution, ns: u64) -> Fra } } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CucError { + #[error("invalid cuc counter byte width {0}")] InvalidCounterWidth(u8), /// Invalid counter supplied. - InvalidCounter { - width: u8, - counter: u64, - }, + #[error("invalid cuc counter {counter} for width {width}")] + InvalidCounter { width: u8, counter: u64 }, + #[error("invalid cuc fractional part {value} for resolution {resolution:?}")] InvalidFractions { resolution: FractionalResolution, value: u64, }, + #[error("error while correcting for leap seconds")] LeapSecondCorrectionError, - DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError), -} - -impl Display for CucError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - CucError::InvalidCounterWidth(w) => { - write!(f, "invalid cuc counter byte width {w}") - } - CucError::InvalidCounter { width, counter } => { - write!(f, "invalid cuc counter {counter} for width {width}") - } - CucError::InvalidFractions { resolution, value } => { - write!( - f, - "invalid cuc fractional part {value} for resolution {resolution:?}" - ) - } - CucError::LeapSecondCorrectionError => { - write!(f, "error while correcting for leap seconds") - } - CucError::DateBeforeCcsdsEpoch(e) => { - write!(f, "date before ccsds epoch: {e}") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for CucError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - CucError::DateBeforeCcsdsEpoch(e) => Some(e), - _ => None, - } - } -} - -impl From for CucError { - fn from(e: DateBeforeCcsdsEpochError) -> Self { - Self::DateBeforeCcsdsEpoch(e) - } + #[error("date before ccsds epoch: {0}")] + DateBeforeCcsdsEpoch(#[from] DateBeforeCcsdsEpochError), } /// Tuple object where the first value is the width of the counter and the second value diff --git a/src/time/mod.rs b/src/time/mod.rs index 044208e..47e8450 100644 --- a/src/time/mod.rs +++ b/src/time/mod.rs @@ -3,7 +3,6 @@ use crate::ByteConversionError; #[cfg(feature = "chrono")] use chrono::{TimeZone, Utc}; use core::cmp::Ordering; -use core::fmt::{Display, Formatter}; use core::ops::{Add, AddAssign, Sub}; use core::time::Duration; @@ -14,8 +13,6 @@ use num_traits::float::FloatCore; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -use std::error::Error; #[cfg(feature = "std")] use std::time::{SystemTime, SystemTimeError}; #[cfg(feature = "std")] @@ -69,67 +66,23 @@ pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result { #[error("date before ccsds epoch: {0:?}")] pub struct DateBeforeCcsdsEpochError(UnixTime); -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum TimestampError { + #[error("invalid time code, expected {expected:?}, found {found}")] InvalidTimeCode { expected: CcsdsTimeCode, found: u8 }, - ByteConversion(ByteConversionError), - Cds(cds::CdsError), - Cuc(cuc::CucError), + #[error("time stamp: byte conversion error: {0}")] + ByteConversion(#[from] ByteConversionError), + #[error("CDS error: {0}")] + Cds(#[from] cds::CdsError), + #[error("CUC error: {0}")] + Cuc(#[from] cuc::CucError), + #[error("custom epoch not supported")] CustomEpochNotSupported, } -impl Display for TimestampError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - TimestampError::InvalidTimeCode { expected, found } => { - write!( - f, - "invalid raw time code value {found} for time code {expected:?}" - ) - } - TimestampError::Cds(e) => { - write!(f, "cds error: {e}") - } - TimestampError::Cuc(e) => { - write!(f, "cuc error: {e}") - } - TimestampError::ByteConversion(e) => { - write!(f, "time stamp: {e}") - } - TimestampError::CustomEpochNotSupported => { - write!(f, "custom epochs are not supported") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for TimestampError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - TimestampError::ByteConversion(e) => Some(e), - TimestampError::Cds(e) => Some(e), - TimestampError::Cuc(e) => Some(e), - _ => None, - } - } -} - -impl From for TimestampError { - fn from(e: cds::CdsError) -> Self { - TimestampError::Cds(e) - } -} - -impl From for TimestampError { - fn from(e: cuc::CucError) -> Self { - TimestampError::Cuc(e) - } -} - #[cfg(feature = "std")] pub mod std_mod { use crate::time::TimestampError; @@ -735,7 +688,7 @@ mod tests { fn test_cuc_error_printout() { let cuc_error = CucError::InvalidCounterWidth(12); let stamp_error = TimestampError::from(cuc_error); - assert_eq!(stamp_error.to_string(), format!("cuc error: {cuc_error}")); + assert_eq!(stamp_error.to_string(), format!("CUC error: {cuc_error}")); } #[test]