diff --git a/src/cfdp/lv.rs b/src/cfdp/lv.rs index dc12a66..fd07a5b 100644 --- a/src/cfdp/lv.rs +++ b/src/cfdp/lv.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::string::String; -use super::TlvLvDataTooLarge; +use super::TlvLvDataTooLargeError; pub const MIN_LV_LEN: usize = 1; @@ -64,9 +64,9 @@ pub(crate) fn generic_len_check_deserialization( impl<'data> Lv<'data> { #[inline] - pub fn new(data: &[u8]) -> Result { + pub fn new(data: &[u8]) -> Result { if data.len() > u8::MAX as usize { - return Err(TlvLvDataTooLarge(data.len())); + return Err(TlvLvDataTooLargeError(data.len())); } Ok(Lv { data, @@ -86,7 +86,7 @@ impl<'data> Lv<'data> { /// Helper function to build a string LV. This is especially useful for the file or directory /// path LVs #[inline] - pub fn new_from_str(str_slice: &str) -> Result { + pub fn new_from_str(str_slice: &str) -> Result { Self::new(str_slice.as_bytes()) } @@ -94,7 +94,7 @@ impl<'data> Lv<'data> { /// path LVs #[cfg(feature = "std")] #[inline] - pub fn new_from_string(string: &'data String) -> Result, TlvLvDataTooLarge> { + pub fn new_from_string(string: &'data String) -> Result, TlvLvDataTooLargeError> { Self::new(string.as_bytes()) } diff --git a/src/cfdp/mod.rs b/src/cfdp/mod.rs index a1aae5f..09f2241 100644 --- a/src/cfdp/mod.rs +++ b/src/cfdp/mod.rs @@ -1,11 +1,8 @@ //! Low-level CCSDS File Delivery Protocol (CFDP) support according to [CCSDS 727.0-B-5](https://public.ccsds.org/Pubs/727x0b5.pdf). use crate::ByteConversionError; -use core::fmt::{Display, Formatter}; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -use std::error::Error; pub mod lv; pub mod pdu; @@ -176,97 +173,43 @@ impl Default for ChecksumType { pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4]; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TlvLvDataTooLarge(pub usize); +#[error("data with size {0} larger than allowed {max} bytes", max = u8::MAX)] +pub struct TlvLvDataTooLargeError(pub usize); -impl Display for TlvLvDataTooLarge { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!( - f, - "data with size {} larger than allowed {} bytes", - self.0, - u8::MAX - ) - } +/// First value: Found value. Second value: Expected value if there is one. +#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("invalid TLV type field, found {found}, expected {expected:?}")] +pub struct InvalidTlvTypeFieldError { + found: u8, + expected: Option, } -#[cfg(feature = "std")] -impl Error for TlvLvDataTooLarge {} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TlvLvError { - DataTooLarge(TlvLvDataTooLarge), - ByteConversion(ByteConversionError), - /// First value: Found value. Second value: Expected value if there is one. - InvalidTlvTypeField { - found: u8, - expected: Option, - }, - /// Logically invalid value length detected. The value length may not exceed 255 bytes. - /// Depending on the concrete TLV type, the value length may also be logically invalid. + #[error("{0}")] + DataTooLarge(#[from] TlvLvDataTooLargeError), + #[error("byte conversion error: {0}")] + ByteConversion(#[from] ByteConversionError), + #[error("{0}")] + InvalidTlvTypeField(#[from] InvalidTlvTypeFieldError), + #[error("invalid value length {0}")] InvalidValueLength(usize), /// Only applies to filestore requests and responses. Second name was missing where one is /// expected. + #[error("second name missing for filestore request or response")] SecondNameMissing, /// Invalid action code for filestore requests or responses. + #[error("invalid action code {0}")] InvalidFilestoreActionCode(u8), } -impl From for TlvLvError { - fn from(value: TlvLvDataTooLarge) -> Self { - Self::DataTooLarge(value) - } -} - -impl From for TlvLvError { - fn from(value: ByteConversionError) -> Self { - Self::ByteConversion(value) - } -} - -impl Display for TlvLvError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - TlvLvError::DataTooLarge(e) => { - write!(f, "{}", e) - } - TlvLvError::ByteConversion(e) => { - write!(f, "tlv or lv byte conversion: {}", e) - } - TlvLvError::InvalidTlvTypeField { found, expected } => { - write!( - f, - "invalid TLV type field, found {found}, expected {expected:?}" - ) - } - TlvLvError::InvalidValueLength(len) => { - write!(f, "invalid value length {len}") - } - TlvLvError::SecondNameMissing => { - write!(f, "second name missing for filestore request or response") - } - TlvLvError::InvalidFilestoreActionCode(raw) => { - write!(f, "invalid filestore action code with raw value {raw}") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for TlvLvError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - TlvLvError::DataTooLarge(e) => Some(e), - TlvLvError::ByteConversion(e) => Some(e), - _ => None, - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/cfdp/pdu/eof.rs b/src/cfdp/pdu/eof.rs index c7c1111..2461255 100644 --- a/src/cfdp/pdu/eof.rs +++ b/src/cfdp/pdu/eof.rs @@ -297,7 +297,7 @@ mod tests { buf[written - 1] -= 1; let crc: u16 = ((buf[written - 2] as u16) << 8) as u16 | buf[written - 1] as u16; let error = EofPdu::from_bytes(&buf).unwrap_err(); - if let PduError::ChecksumError(e) = error { + if let PduError::Checksum(e) = error { assert_eq!(e, crc); } else { panic!("expected crc error"); diff --git a/src/cfdp/pdu/file_data.rs b/src/cfdp/pdu/file_data.rs index db6e09d..114e235 100644 --- a/src/cfdp/pdu/file_data.rs +++ b/src/cfdp/pdu/file_data.rs @@ -544,7 +544,7 @@ mod tests { buf[written - 1] -= 1; let crc: u16 = ((buf[written - 2] as u16) << 8) | buf[written - 1] as u16; let error = FileDataPdu::from_bytes(&buf).unwrap_err(); - if let PduError::ChecksumError(e) = error { + if let PduError::Checksum(e) = error { assert_eq!(e, crc); } else { panic!("expected crc error"); @@ -753,7 +753,7 @@ mod tests { assert!(pdu_reader_error.is_err()); let error = pdu_reader_error.unwrap_err(); match error { - PduError::ChecksumError(_) => (), + PduError::Checksum(_) => (), _ => { panic!("unexpected PDU error {}", error) } diff --git a/src/cfdp/pdu/finished.rs b/src/cfdp/pdu/finished.rs index 742c449..a7b8261 100644 --- a/src/cfdp/pdu/finished.rs +++ b/src/cfdp/pdu/finished.rs @@ -4,14 +4,14 @@ use crate::cfdp::pdu::{ use crate::cfdp::tlv::{ EntityIdTlv, FilestoreResponseTlv, GenericTlv, Tlv, TlvType, TlvTypeField, WritableTlv, }; -use crate::cfdp::{ConditionCode, CrcFlag, Direction, PduType, TlvLvError}; +use crate::cfdp::{ConditionCode, CrcFlag, Direction, PduType}; use crate::ByteConversionError; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use super::tlv::ReadableTlv; -use super::{CfdpPdu, WritablePduPacket}; +use super::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket}; #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -332,22 +332,26 @@ impl<'buf> FinishedPduReader<'buf> { // last TLV, everything else would break the whole handling of the packet // TLVs. if current_idx != full_len_without_crc { - return Err(PduError::FormatError); + return Err(PduError::Format); } } else { - return Err(TlvLvError::InvalidTlvTypeField { - found: tlv_type.into(), - expected: Some(TlvType::FilestoreResponse.into()), - } - .into()); + return Err(PduError::TlvLv( + InvalidTlvTypeFieldError { + found: tlv_type.into(), + expected: Some(TlvType::FilestoreResponse.into()), + } + .into(), + )); } } TlvTypeField::Custom(raw) => { - return Err(TlvLvError::InvalidTlvTypeField { - found: raw, - expected: None, - } - .into()); + return Err(PduError::TlvLv( + InvalidTlvTypeFieldError { + found: raw, + expected: None, + } + .into(), + )); } } } @@ -564,7 +568,7 @@ mod tests { buf[written - 1] -= 1; let crc: u16 = ((buf[written - 2] as u16) << 8) as u16 | buf[written - 1] as u16; let error = FinishedPduReader::new(&buf).unwrap_err(); - if let PduError::ChecksumError(e) = error { + if let PduError::Checksum(e) = error { assert_eq!(e, crc); } else { panic!("expected crc error"); diff --git a/src/cfdp/pdu/metadata.rs b/src/cfdp/pdu/metadata.rs index 6454538..a63e11c 100644 --- a/src/cfdp/pdu/metadata.rs +++ b/src/cfdp/pdu/metadata.rs @@ -720,7 +720,7 @@ pub mod tests { fn test_with_owned_opts() { let tlv1 = TlvOwned::new_empty(TlvType::FlowLabel); let msg_to_user: [u8; 4] = [1, 2, 3, 4]; - let tlv2 = TlvOwned::new(TlvType::MsgToUser, &msg_to_user).unwrap(); + let tlv2 = TlvOwned::new(TlvType::MsgToUser, &msg_to_user); let mut all_tlvs = tlv1.to_vec(); all_tlvs.extend(tlv2.to_vec()); let (src_filename, dest_filename, metadata_pdu) = generic_metadata_pdu( @@ -780,7 +780,7 @@ pub mod tests { assert_eq!(expected, Some(FileDirectiveType::MetadataPdu)); assert_eq!( error.to_string(), - "invalid directive type value 255, expected Some(MetadataPdu)" + "invalid directive type, found 255, expected Some(MetadataPdu)" ); } else { panic!("Expected InvalidDirectiveType error, got {:?}", error); @@ -806,7 +806,7 @@ pub mod tests { assert_eq!(expected, FileDirectiveType::MetadataPdu); assert_eq!( error.to_string(), - "found directive type EofPdu, expected MetadataPdu" + "wrong directive type, found EofPdu, expected MetadataPdu" ); } else { panic!("Expected InvalidDirectiveType error, got {:?}", error); diff --git a/src/cfdp/pdu/mod.rs b/src/cfdp/pdu/mod.rs index b1c6b14..4f6eef3 100644 --- a/src/cfdp/pdu/mod.rs +++ b/src/cfdp/pdu/mod.rs @@ -5,9 +5,6 @@ use crate::ByteConversionError; use crate::CRC_CCITT_FALSE; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use core::fmt::{Display, Formatter}; -#[cfg(feature = "std")] -use std::error::Error; pub mod ack; pub mod eof; @@ -30,137 +27,60 @@ pub enum FileDirectiveType { KeepAlivePdu = 0x0c, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PduError { - ByteConversion(ByteConversionError), - /// Found version ID invalid, not equal to [CFDP_VERSION_2]. + #[error("byte conversion error: {0}")] + ByteConversion(#[from] ByteConversionError), + /// Found version ID invalid, not equal to [super::CFDP_VERSION_2]. + #[error("CFDP version missmatch, found {0}, expected {ver}", ver = super::CFDP_VERSION_2)] CfdpVersionMissmatch(u8), /// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported. + #[error("invalid PDU entity ID length {0}, only [1, 2, 4, 8] are allowed")] InvalidEntityLen(u8), /// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported. + #[error("invalid transaction ID length {0}")] InvalidTransactionSeqNumLen(u8), + #[error("missmatch of PDU source ID length {src_id_len} and destination ID length {dest_id_len}")] SourceDestIdLenMissmatch { src_id_len: usize, dest_id_len: usize, }, /// Wrong directive type, for example when parsing the directive field for a file directive /// PDU. + #[error("wrong directive type, found {found:?}, expected {expected:?}")] WrongDirectiveType { found: FileDirectiveType, expected: FileDirectiveType, }, /// The directive type field contained a value not in the range of permitted values. This can /// also happen if an invalid value is passed to the ACK PDU constructor. + #[error("invalid directive type, found {found:?}, expected {expected:?}")] InvalidDirectiveType { found: u8, expected: Option, }, + #[error("invalid start or end of scope value for NAK PDU")] InvalidStartOrEndOfScopeValue, /// Invalid condition code. Contains the raw detected value. + #[error("invalid condition code {0}")] InvalidConditionCode(u8), /// Invalid checksum type which is not part of the checksums listed in the /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/). + #[error("invalid checksum type {0}")] InvalidChecksumType(u8), + #[error("file size {0} too large")] FileSizeTooLarge(u64), /// If the CRC flag for a PDU is enabled and the checksum check fails. Contains raw 16-bit CRC. - ChecksumError(u16), + #[error("checksum error for checksum {0}")] + Checksum(u16), /// Generic error for invalid PDU formats. - FormatError, + #[error("generic PDU format error")] + Format, /// Error handling a TLV field. - TlvLvError(TlvLvError), -} - -impl Display for PduError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - PduError::InvalidEntityLen(raw_id) => { - write!( - f, - "invalid PDU entity ID length {raw_id}, only [1, 2, 4, 8] are allowed" - ) - } - PduError::InvalidStartOrEndOfScopeValue => { - write!(f, "invalid start or end of scope for NAK PDU") - } - PduError::InvalidTransactionSeqNumLen(raw_id) => { - write!( - f, - "invalid PDUtransaction seq num length {raw_id}, only [1, 2, 4, 8] are allowed" - ) - } - PduError::CfdpVersionMissmatch(raw) => { - write!( - f, - "cfdp version missmatch, found {raw}, expected {CFDP_VERSION_2}" - ) - } - PduError::SourceDestIdLenMissmatch { - src_id_len, - dest_id_len, - } => { - write!( - f, - "missmatch of PDU source length {src_id_len} and destination length {dest_id_len}" - ) - } - PduError::ByteConversion(e) => { - write!(f, "{}", e) - } - PduError::FileSizeTooLarge(value) => { - write!(f, "file size value {value} exceeds allowed 32 bit width") - } - PduError::WrongDirectiveType { found, expected } => { - write!(f, "found directive type {found:?}, expected {expected:?}") - } - PduError::InvalidConditionCode(raw_code) => { - write!(f, "found invalid condition code with raw value {raw_code}") - } - PduError::InvalidDirectiveType { found, expected } => { - write!( - f, - "invalid directive type value {found}, expected {expected:?}" - ) - } - PduError::InvalidChecksumType(checksum_type) => { - write!(f, "invalid checksum type {checksum_type}") - } - PduError::ChecksumError(checksum) => { - write!(f, "checksum error for CRC {checksum:#04x}") - } - PduError::TlvLvError(error) => { - write!(f, "pdu tlv error: {error}") - } - PduError::FormatError => { - write!(f, "generic PDU format error") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for PduError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - PduError::ByteConversion(e) => Some(e), - PduError::TlvLvError(e) => Some(e), - _ => None, - } - } -} - -impl From for PduError { - #[inline] - fn from(value: ByteConversionError) -> Self { - Self::ByteConversion(value) - } -} - -impl From for PduError { - #[inline] - fn from(e: TlvLvError) -> Self { - Self::TlvLvError(e) - } + #[error("PDU error: {0}")] + TlvLv(#[from] TlvLvError), } pub trait WritablePduPacket { @@ -532,7 +452,7 @@ impl PduHeader { let mut digest = CRC_CCITT_FALSE.digest(); digest.update(&buf[..self.pdu_len()]); if digest.finalize() != 0 { - return Err(PduError::ChecksumError(u16::from_be_bytes( + return Err(PduError::Checksum(u16::from_be_bytes( buf[self.pdu_len() - 2..self.pdu_len()].try_into().unwrap(), ))); } @@ -981,7 +901,7 @@ mod tests { assert_eq!(raw_version, CFDP_VERSION_2 + 1); assert_eq!( error.to_string(), - "cfdp version missmatch, found 2, expected 1" + "CFDP version missmatch, found 2, expected 1" ); } else { panic!("invalid exception: {}", error); @@ -1029,7 +949,7 @@ mod tests { assert_eq!(expected, 7); assert_eq!( error.to_string(), - "source slice with size 6 too small, expected at least 7 bytes" + "byte conversion error: source slice with size 6 too small, expected at least 7 bytes" ); } } @@ -1084,7 +1004,7 @@ mod tests { assert_eq!(dest_id_len, 2); assert_eq!( error.to_string(), - "missmatch of PDU source length 1 and destination length 2" + "missmatch of PDU source ID length 1 and destination ID length 2" ); } } diff --git a/src/cfdp/pdu/nak.rs b/src/cfdp/pdu/nak.rs index 472e80e..0c9aedc 100644 --- a/src/cfdp/pdu/nak.rs +++ b/src/cfdp/pdu/nak.rs @@ -751,7 +751,7 @@ mod tests { if let PduError::InvalidStartOrEndOfScopeValue = error { assert_eq!( error.to_string(), - "invalid start or end of scope for NAK PDU" + "invalid start or end of scope value for NAK PDU" ); } else { panic!("unexpected error {error}"); @@ -796,7 +796,7 @@ mod tests { nak_vec[nak_pdu.len_written() - 1] -= 1; let nak_pdu_deser = NakPduReader::new(&nak_vec); assert!(nak_pdu_deser.is_err()); - if let Err(PduError::ChecksumError(raw)) = nak_pdu_deser { + if let Err(PduError::Checksum(raw)) = nak_pdu_deser { assert_eq!( raw, u16::from_be_bytes(nak_vec[nak_pdu.len_written() - 2..].try_into().unwrap()) diff --git a/src/cfdp/tlv/mod.rs b/src/cfdp/tlv/mod.rs index 71154f3..bb4973d 100644 --- a/src/cfdp/tlv/mod.rs +++ b/src/cfdp/tlv/mod.rs @@ -15,7 +15,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use super::TlvLvDataTooLarge; +use super::{InvalidTlvTypeFieldError, TlvLvDataTooLargeError}; pub mod msg_to_user; @@ -153,14 +153,14 @@ pub struct Tlv<'data> { } impl<'data> Tlv<'data> { - pub fn new(tlv_type: TlvType, data: &[u8]) -> Result { + pub fn new(tlv_type: TlvType, data: &[u8]) -> Result { Ok(Tlv { tlv_type_field: TlvTypeField::Standard(tlv_type), lv: Lv::new(data)?, }) } - pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Result { + pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Result { Ok(Tlv { tlv_type_field: TlvTypeField::Custom(tlv_type), lv: Lv::new(data)?, @@ -179,7 +179,7 @@ impl<'data> Tlv<'data> { /// bytestream with the exact size of the expected TLV. This function will take care /// of parsing the length byte, and the length of the parsed TLV can be retrieved using /// [Self::len_full]. - pub fn from_bytes(buf: &'data [u8]) -> Result, TlvLvError> { + pub fn from_bytes(buf: &'data [u8]) -> Result, ByteConversionError> { generic_len_check_deserialization(buf, MIN_TLV_LEN)?; let mut tlv = Self { tlv_type_field: TlvTypeField::from(buf[0]), @@ -239,8 +239,6 @@ impl GenericTlv for Tlv<'_> { #[cfg(feature = "alloc")] pub mod alloc_mod { - use crate::cfdp::TlvLvDataTooLarge; - use super::*; /// Owned variant of [Tlv] which is consequently [Clone]able and does not have a lifetime @@ -254,24 +252,18 @@ pub mod alloc_mod { } impl TlvOwned { - pub fn new(tlv_type: TlvType, data: &[u8]) -> Result { - if data.len() > u8::MAX as usize { - return Err(TlvLvDataTooLarge(data.len())); - } - Ok(Self { + pub fn new(tlv_type: TlvType, data: &[u8]) -> Self { + Self { tlv_type_field: TlvTypeField::Standard(tlv_type), data: data.to_vec(), - }) + } } - pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Result { - if data.len() > u8::MAX as usize { - return Err(TlvLvDataTooLarge(data.len())); - } - Ok(Self { + pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Self { + Self { tlv_type_field: TlvTypeField::Custom(tlv_type), data: data.to_vec(), - }) + } } /// Creates a TLV with an empty value field. @@ -281,15 +273,6 @@ pub mod alloc_mod { data: Vec::new(), } } - - pub fn as_tlv(&self) -> Tlv<'_> { - Tlv { - tlv_type_field: self.tlv_type_field, - // The API should ensure that the data length is never to large, so the unwrap for the - // LV creation should never be an issue. - lv: Lv::new(&self.data).expect("lv creation failed unexpectedly"), - } - } } impl ReadableTlv for TlvOwned { @@ -343,7 +326,7 @@ impl EntityIdTlv { Self { entity_id } } - fn check_min_len(buf: &[u8]) -> Result<(), ByteConversionError> { + fn len_check(buf: &[u8]) -> Result<(), ByteConversionError> { if buf.len() < 2 { return Err(ByteConversionError::ToSliceTooSmall { found: buf.len(), @@ -366,7 +349,7 @@ impl EntityIdTlv { } pub fn from_bytes(buf: &[u8]) -> Result { - Self::check_min_len(buf)?; + Self::len_check(buf)?; verify_tlv_type(buf[0], TlvType::EntityId)?; let len = buf[1]; if len != 1 && len != 2 && len != 4 && len != 8 { @@ -377,31 +360,19 @@ impl EntityIdTlv { Ok(Self { entity_id }) } - /// Convert to a generic [Tlv], which also erases the type information. + /// Convert to a generic [Tlv], which also erases the programmatic type information. pub fn to_tlv(self, buf: &mut [u8]) -> Result { - Self::check_min_len(buf)?; + Self::len_check(buf)?; self.entity_id .write_to_be_bytes(&mut buf[2..2 + self.entity_id.size()])?; - if buf.len() < self.len_value() { - return Err(ByteConversionError::ToSliceTooSmall { - found: buf.len(), - expected: self.len_value(), - }); - } - // We performed all checks necessary to ensure this call never panics. + // Can't fail. Ok(Tlv::new(TlvType::EntityId, &buf[2..2 + self.entity_id.size()]).unwrap()) } - - #[cfg(feature = "alloc")] - pub fn to_owned(&self) -> TlvOwned { - // Unwrap is okay here, entity ID should never be larger than maximum allowed size. - TlvOwned::new(TlvType::EntityId, &self.entity_id.to_vec()).unwrap() - } } impl WritableTlv for EntityIdTlv { fn write_to_bytes(&self, buf: &mut [u8]) -> Result { - Self::check_min_len(buf)?; + Self::len_check(buf)?; buf[0] = TlvType::EntityId as u8; buf[1] = self.entity_id.size() as u8; Ok(2 + self.entity_id.write_to_be_bytes(&mut buf[2..])?) @@ -421,21 +392,23 @@ impl GenericTlv for EntityIdTlv { impl<'data> TryFrom> for EntityIdTlv { type Error = TlvLvError; - fn try_from(value: Tlv) -> Result { + fn try_from(value: Tlv) -> Result { match value.tlv_type_field { TlvTypeField::Standard(tlv_type) => { if tlv_type != TlvType::EntityId { - return Err(TlvLvError::InvalidTlvTypeField { + return Err(InvalidTlvTypeFieldError { found: tlv_type as u8, expected: Some(TlvType::EntityId as u8), - }); + } + .into()); } } TlvTypeField::Custom(val) => { - return Err(TlvLvError::InvalidTlvTypeField { + return Err(InvalidTlvTypeFieldError { found: val, expected: Some(TlvType::EntityId as u8), - }); + } + .into()); } } let len_value = value.value().len(); @@ -640,12 +613,6 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> { }, }) } - - #[cfg(feature = "alloc")] - pub fn to_owned(&self) -> TlvOwned { - // The API should ensure the data field is never too large, so unwrapping here is okay. - TlvOwned::new(TlvType::FilestoreRequest, &self.to_vec()[2..]).unwrap() - } } impl WritableTlv for FilestoreRequestTlv<'_, '_> { @@ -831,12 +798,6 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco filestore_message, }) } - - #[cfg(feature = "alloc")] - pub fn to_owned(&self) -> TlvOwned { - // The API should ensure the data field is never too large, so unwrap is okay here. - TlvOwned::new(TlvType::FilestoreResponse, &self.to_vec()[2..]).unwrap() - } } impl WritableTlv for FilestoreResponseTlv<'_, '_, '_> { @@ -878,13 +839,16 @@ impl GenericTlv for FilestoreResponseTlv<'_, '_, '_> { } } -pub(crate) fn verify_tlv_type(raw_type: u8, expected_tlv_type: TlvType) -> Result<(), TlvLvError> { - let tlv_type = TlvType::try_from(raw_type).map_err(|_| TlvLvError::InvalidTlvTypeField { +pub(crate) fn verify_tlv_type( + raw_type: u8, + expected_tlv_type: TlvType, +) -> Result<(), InvalidTlvTypeFieldError> { + let tlv_type = TlvType::try_from(raw_type).map_err(|_| InvalidTlvTypeFieldError { found: raw_type, expected: Some(expected_tlv_type.into()), })?; if tlv_type != expected_tlv_type { - return Err(TlvLvError::InvalidTlvTypeField { + return Err(InvalidTlvTypeFieldError { found: tlv_type as u8, expected: Some(expected_tlv_type as u8), }); @@ -1079,11 +1043,15 @@ mod tests { let tlv_res = Tlv::new(TlvType::MsgToUser, &buf_too_large); assert!(tlv_res.is_err()); let error = tlv_res.unwrap_err(); - assert_eq!(error.0, u8::MAX as usize + 1); - assert_eq!( - error.to_string(), - "data with size 256 larger than allowed 255 bytes" - ); + match error { + TlvLvDataTooLargeError(size) => { + assert_eq!(size, u8::MAX as usize + 1); + assert_eq!( + error.to_string(), + "data with size 256 larger than allowed 255 bytes" + ); + } + } } #[test] @@ -1392,7 +1360,8 @@ mod tests { let error = EntityIdTlv::try_from(msg_to_user_tlv); assert!(error.is_err()); let error = error.unwrap_err(); - if let TlvLvError::InvalidTlvTypeField { found, expected } = error { + if let TlvLvError::InvalidTlvTypeField(InvalidTlvTypeFieldError { found, expected }) = error + { assert_eq!(found, TlvType::MsgToUser as u8); assert_eq!(expected, Some(TlvType::EntityId as u8)); assert_eq!( @@ -1457,7 +1426,7 @@ mod tests { let entity_id = UbfU8::new(5); let mut buf: [u8; 4] = [0; 4]; assert!(entity_id.write_to_be_bytes(&mut buf).is_ok()); - let tlv_res = TlvOwned::new(TlvType::EntityId, &buf[0..1]).expect("creating TLV failed"); + let tlv_res = TlvOwned::new(TlvType::EntityId, &buf[0..1]); assert_eq!( tlv_res.tlv_type_field(), TlvTypeField::Standard(TlvType::EntityId) @@ -1484,7 +1453,7 @@ mod tests { #[test] fn test_owned_tlv_custom_type() { - let tlv_res = TlvOwned::new_with_custom_type(32, &[]).unwrap(); + let tlv_res = TlvOwned::new_with_custom_type(32, &[]); assert_eq!(tlv_res.tlv_type_field(), TlvTypeField::Custom(32)); assert_eq!(tlv_res.len_full(), 2); assert_eq!(tlv_res.value().len(), 0); diff --git a/src/cfdp/tlv/msg_to_user.rs b/src/cfdp/tlv/msg_to_user.rs index 4971489..0b7bd96 100644 --- a/src/cfdp/tlv/msg_to_user.rs +++ b/src/cfdp/tlv/msg_to_user.rs @@ -2,7 +2,7 @@ #[cfg(feature = "alloc")] use super::TlvOwned; use super::{GenericTlv, ReadableTlv, Tlv, TlvLvError, TlvType, TlvTypeField, WritableTlv}; -use crate::{cfdp::TlvLvDataTooLarge, ByteConversionError}; +use crate::{cfdp::{InvalidTlvTypeFieldError, TlvLvDataTooLargeError}, ByteConversionError}; use delegate::delegate; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -12,7 +12,7 @@ pub struct MsgToUserTlv<'data> { impl<'data> MsgToUserTlv<'data> { /// Create a new message to user TLV where the type field is set correctly. - pub fn new(value: &'data [u8]) -> Result, TlvLvDataTooLarge> { + pub fn new(value: &'data [u8]) -> Result, TlvLvDataTooLargeError> { Ok(Self { tlv: Tlv::new(TlvType::MsgToUser, value)?, }) @@ -62,17 +62,17 @@ impl<'data> MsgToUserTlv<'data> { match msg_to_user.tlv.tlv_type_field() { TlvTypeField::Standard(tlv_type) => { if tlv_type != TlvType::MsgToUser { - return Err(TlvLvError::InvalidTlvTypeField { + return Err(InvalidTlvTypeFieldError { found: tlv_type as u8, expected: Some(TlvType::MsgToUser as u8), - }); + }.into()); } } TlvTypeField::Custom(raw) => { - return Err(TlvLvError::InvalidTlvTypeField { + return Err(InvalidTlvTypeFieldError { found: raw, expected: Some(TlvType::MsgToUser as u8), - }); + }.into()); } } Ok(msg_to_user) @@ -205,9 +205,9 @@ mod tests { fn test_reserved_msg_deserialization_invalid_type() { let trash: [u8; 5] = [TlvType::FlowLabel as u8, 3, 1, 2, 3]; let error = MsgToUserTlv::from_bytes(&trash).unwrap_err(); - if let TlvLvError::InvalidTlvTypeField { found, expected } = error { - assert_eq!(found, TlvType::FlowLabel as u8); - assert_eq!(expected, Some(TlvType::MsgToUser as u8)); + if let TlvLvError::InvalidTlvTypeField(inner) = error { + assert_eq!(inner.found, TlvType::FlowLabel as u8); + assert_eq!(inner.expected, Some(TlvType::MsgToUser as u8)); } else { panic!("Wrong error type returned: {:?}", error); } diff --git a/src/ecss/mod.rs b/src/ecss/mod.rs index cfac598..20ce84d 100644 --- a/src/ecss/mod.rs +++ b/src/ecss/mod.rs @@ -6,13 +6,11 @@ use crate::{ByteConversionError, CcsdsPacket, CRC_CCITT_FALSE}; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use core::fmt::{Debug, Display, Formatter}; +use core::fmt::Debug; use core::mem::size_of; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -use std::error::Error; pub mod event; pub mod hk; @@ -148,50 +146,19 @@ pub enum PfcReal { DoubleMilStd = 4, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PusError { + #[error("PUS version {0:?} not supported")] VersionNotSupported(PusVersion), + #[error("checksum verification for crc16 {0:#06x} failed")] ChecksumFailure(u16), /// CRC16 needs to be calculated first - CrcCalculationMissing, - ByteConversion(ByteConversionError), -} - -impl Display for PusError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - PusError::VersionNotSupported(v) => { - write!(f, "PUS version {v:?} not supported") - } - PusError::ChecksumFailure(crc) => { - write!(f, "checksum verification for crc16 {crc:#06x} failed") - } - PusError::CrcCalculationMissing => { - write!(f, "crc16 was not calculated") - } - PusError::ByteConversion(e) => { - write!(f, "pus error: {e}") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for PusError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - if let PusError::ByteConversion(e) = self { - return Some(e); - } - None - } -} - -impl From for PusError { - fn from(e: ByteConversionError) -> Self { - PusError::ByteConversion(e) - } + //#[error("crc16 was not calculated")] + //CrcCalculationMissing, + #[error("pus error: {0}")] + ByteConversion(#[from] ByteConversionError), } /// Generic trait to describe common attributes for both PUS Telecommands (TC) and PUS Telemetry diff --git a/src/lib.rs b/src/lib.rs index bdb1fd9..b824379 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,17 +61,11 @@ extern crate alloc; #[cfg(any(feature = "std", test))] extern crate std; -use core::{ - fmt::{Debug, Display, Formatter}, - hash::Hash, -}; +use core::{fmt::Debug, hash::Hash}; use crc::{Crc, CRC_16_IBM_3740}; use delegate::delegate; use zerocopy::{FromBytes, IntoBytes}; -#[cfg(feature = "std")] -use std::error::Error; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -94,55 +88,24 @@ pub const MAX_APID: u16 = 2u16.pow(11) - 1; pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1; /// Generic error type when converting to and from raw byte slices. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ByteConversionError { /// The passed slice is too small. Returns the passed slice length and expected minimum size - ToSliceTooSmall { - found: usize, - expected: usize, - }, + #[error("target slice with size {found} is too small, expected size of at least {expected}")] + ToSliceTooSmall { found: usize, expected: usize }, /// The provider buffer is too small. Returns the passed slice length and expected minimum size - FromSliceTooSmall { - found: usize, - expected: usize, - }, + #[error("source slice with size {found} too small, expected at least {expected} bytes")] + FromSliceTooSmall { found: usize, expected: usize }, /// The [zerocopy] library failed to write to bytes + #[error("zerocopy serialization error")] ZeroCopyToError, + /// The [zerocopy] library failed to read from bytes + #[error("zerocopy deserialization error")] ZeroCopyFromError, } -impl Display for ByteConversionError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - ByteConversionError::ToSliceTooSmall { found, expected } => { - write!( - f, - "target slice with size {} is too small, expected size of at least {}", - found, expected - ) - } - ByteConversionError::FromSliceTooSmall { found, expected } => { - write!( - f, - "source slice with size {} too small, expected at least {} bytes", - found, expected - ) - } - ByteConversionError::ZeroCopyToError => { - write!(f, "zerocopy serialization error") - } - ByteConversionError::ZeroCopyFromError => { - write!(f, "zerocopy deserialization error") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for ByteConversionError {} - /// CCSDS packet type enumeration. #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/time/cds.rs b/src/time/cds.rs index 9b7eb94..8e1ee00 100644 --- a/src/time/cds.rs +++ b/src/time/cds.rs @@ -7,15 +7,13 @@ use crate::private::Sealed; use crate::ByteConversionError; use core::cmp::Ordering; -use core::fmt::{Debug, Display, Formatter}; +use core::fmt::Debug; use core::ops::{Add, AddAssign}; use core::time::Duration; #[cfg(feature = "std")] use super::StdTimestampError; #[cfg(feature = "std")] -use std::error::Error; -#[cfg(feature = "std")] use std::time::{SystemTime, SystemTimeError}; #[cfg(feature = "chrono")] @@ -91,49 +89,19 @@ pub enum SubmillisPrecision { Reserved = 0b11, } -#[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))] pub enum CdsError { /// CCSDS days value exceeds maximum allowed size or is negative + #[error("invalid ccsds days {0}")] InvalidCcsdsDays(i64), /// There are distinct constructors depending on the days field width detected in the preamble /// field. This error will be returned if there is a missmatch. + #[error("wrong constructor for length of day {0:?} detected in preamble")] InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment), - DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError), -} - -impl Display for CdsError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - CdsError::InvalidCcsdsDays(days) => { - write!(f, "invalid ccsds days {days}") - } - CdsError::InvalidCtorForDaysOfLenInPreamble(length_of_day) => { - write!( - f, - "wrong constructor for length of day {length_of_day:?} detected in preamble", - ) - } - CdsError::DateBeforeCcsdsEpoch(e) => write!(f, "date before CCSDS epoch: {e}"), - } - } -} - -#[cfg(feature = "std")] -impl Error for CdsError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - CdsError::DateBeforeCcsdsEpoch(e) => Some(e), - _ => None, - } - } -} - -impl From for CdsError { - fn from(value: DateBeforeCcsdsEpochError) -> Self { - Self::DateBeforeCcsdsEpoch(value) - } + #[error("date before CCSDS epoch: {0}")] + DateBeforeCcsdsEpoch(#[from] DateBeforeCcsdsEpochError), } pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment { diff --git a/src/time/mod.rs b/src/time/mod.rs index 63cb5af..044208e 100644 --- a/src/time/mod.rs +++ b/src/time/mod.rs @@ -63,20 +63,12 @@ pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result { CcsdsTimeCode::try_from(raw_bits).map_err(|_| raw_bits) } -#[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))] +#[error("date before ccsds epoch: {0:?}")] pub struct DateBeforeCcsdsEpochError(UnixTime); -impl Display for DateBeforeCcsdsEpochError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "date before ccsds epoch: {:?}", self.0) - } -} - -#[cfg(feature = "std")] -impl Error for DateBeforeCcsdsEpochError {} - #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]