From 3f4f76849f691dbe81ab9f730205ecc4b03d5778 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 4 Nov 2025 18:12:24 +0100 Subject: [PATCH] docs and minor cfdp change --- CHANGELOG.md | 1 + justfile | 5 ++- src/cfdp/lv.rs | 2 +- src/cfdp/mod.rs | 1 + src/cfdp/pdu/ack.rs | 60 ++++++++++++++++++------------- src/cfdp/pdu/eof.rs | 26 ++++++++------ src/cfdp/pdu/file_data.rs | 37 +++++++++++++++---- src/cfdp/pdu/finished.rs | 43 +++++++++++++++++----- src/cfdp/pdu/metadata.rs | 55 ++++++++++++++++++++-------- src/cfdp/pdu/mod.rs | 72 +++++++++++++++++++++++++++++++++---- src/cfdp/pdu/nak.rs | 59 ++++++++++++++++++++---------- src/cfdp/tlv/mod.rs | 55 ++++++++++++++++++++++++++++ src/cfdp/tlv/msg_to_user.rs | 15 ++++++-- src/lib.rs | 2 +- src/time/mod.rs | 1 + 15 files changed, 338 insertions(+), 96 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17613f4..143dfe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Changed - `CdsCommon` renamed to `CdsBase` +- cfdp: Removed `FileDirectiveType` variant `*Pdu` suffix - Simplified CDS short timestamp, contains one less field which reduced serialization length. - Renamed `UnsignedEnum::value` to `UnsignedEnum::value_raw`, `value` is reserved for the `const` value getter. diff --git a/justfile b/justfile index b442f56..4ce05c0 100644 --- a/justfile +++ b/justfile @@ -1,9 +1,12 @@ -all: check build embedded test clippy fmt docs coverage +all: check build embedded test clippy check-fmt docs coverage clippy: cargo clippy -- -D warnings fmt: + cargo fmt --all + +check-fmt: cargo fmt --all -- --check check: diff --git a/src/cfdp/lv.rs b/src/cfdp/lv.rs index 6c7040c..69f4109 100644 --- a/src/cfdp/lv.rs +++ b/src/cfdp/lv.rs @@ -183,7 +183,7 @@ impl<'data> Lv<'data> { } #[cfg(test)] -pub mod tests { +mod tests { use alloc::string::ToString; use super::*; diff --git a/src/cfdp/mod.rs b/src/cfdp/mod.rs index 23ad656..e474f6e 100644 --- a/src/cfdp/mod.rs +++ b/src/cfdp/mod.rs @@ -1,4 +1,5 @@ //! Low-level CCSDS File Delivery Protocol (CFDP) support according to [CCSDS 727.0-B-5](https://public.ccsds.org/Pubs/727x0b5.pdf). +#![warn(missing_docs)] use crate::ByteConversionError; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] diff --git a/src/cfdp/pdu/ack.rs b/src/cfdp/pdu/ack.rs index eef3e62..1c4c241 100644 --- a/src/cfdp/pdu/ack.rs +++ b/src/cfdp/pdu/ack.rs @@ -1,3 +1,4 @@ +//! # Acknowledgement (ACK) PDU packet implementation. use crate::{ cfdp::{ConditionCode, CrcFlag, Direction, TransactionStatus}, ByteConversionError, @@ -10,6 +11,7 @@ use super::{ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +/// Invalid [FileDirectiveType] of the acknowledged PDU error. #[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] #[error("invalid directive code of acknowledged PDU")] pub struct InvalidAckedDirectiveCodeError(pub FileDirectiveType); @@ -28,15 +30,16 @@ pub struct AckPdu { } impl AckPdu { + /// Constructor. pub fn new( mut pdu_header: PduHeader, directive_code_of_acked_pdu: FileDirectiveType, condition_code: ConditionCode, transaction_status: TransactionStatus, ) -> Result { - if directive_code_of_acked_pdu == FileDirectiveType::EofPdu { + if directive_code_of_acked_pdu == FileDirectiveType::Eof { pdu_header.pdu_conf.direction = Direction::TowardsSender; - } else if directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu { + } else if directive_code_of_acked_pdu == FileDirectiveType::Finished { pdu_header.pdu_conf.direction = Direction::TowardsReceiver; } else { return Err(InvalidAckedDirectiveCodeError(directive_code_of_acked_pdu)); @@ -52,6 +55,9 @@ impl AckPdu { Ok(ack_pdu) } + /// Constructor for an ACK PDU acknowledging an EOF PDU. + /// + /// Relevant for the file receiver. pub fn new_for_eof_pdu( pdu_header: PduHeader, condition_code: ConditionCode, @@ -60,13 +66,16 @@ impl AckPdu { // Unwrap okay here, [new] can only fail on invalid directive codes. Self::new( pdu_header, - FileDirectiveType::EofPdu, + FileDirectiveType::Eof, condition_code, transaction_status, ) .unwrap() } + /// Constructor for an ACK PDU acknowledging a Finished PDU. + /// + /// Relevant for the file sender. pub fn new_for_finished_pdu( pdu_header: PduHeader, condition_code: ConditionCode, @@ -75,28 +84,32 @@ impl AckPdu { // Unwrap okay here, [new] can only fail on invalid directive codes. Self::new( pdu_header, - FileDirectiveType::FinishedPdu, + FileDirectiveType::Finished, condition_code, transaction_status, ) .unwrap() } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header } + /// Directive code of the acknowledged PDU. #[inline] pub fn directive_code_of_acked_pdu(&self) -> FileDirectiveType { self.directive_code_of_acked_pdu } + /// Condition code. #[inline] pub fn condition_code(&self) -> ConditionCode { self.condition_code } + /// Transaction status. #[inline] pub fn transaction_status(&self) -> TransactionStatus { self.transaction_status @@ -110,6 +123,7 @@ impl AckPdu { 3 } + /// Construct [Self] from the provided byte slice. pub fn from_bytes(buf: &[u8]) -> Result { let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; @@ -117,13 +131,13 @@ impl AckPdu { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { PduError::InvalidDirectiveType { found: buf[current_idx], - expected: Some(FileDirectiveType::AckPdu), + expected: Some(FileDirectiveType::Ack), } })?; - if directive_type != FileDirectiveType::AckPdu { + if directive_type != FileDirectiveType::Ack { return Err(PduError::WrongDirectiveType { found: directive_type, - expected: FileDirectiveType::AckPdu, + expected: FileDirectiveType::Ack, }); } current_idx += 1; @@ -134,8 +148,8 @@ impl AckPdu { expected: None, } })?; - if acked_directive_type != FileDirectiveType::EofPdu - && acked_directive_type != FileDirectiveType::FinishedPdu + if acked_directive_type != FileDirectiveType::Eof + && acked_directive_type != FileDirectiveType::Finished { return Err(PduError::InvalidDirectiveType { found: acked_directive_type as u8, @@ -167,11 +181,11 @@ impl AckPdu { .into()); } let mut current_idx = self.pdu_header.write_to_bytes(buf)?; - buf[current_idx] = FileDirectiveType::AckPdu as u8; + buf[current_idx] = FileDirectiveType::Ack as u8; current_idx += 1; buf[current_idx] = (self.directive_code_of_acked_pdu as u8) << 4; - if self.directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu { + if self.directive_code_of_acked_pdu == FileDirectiveType::Finished { // This is the directive subtype code. It needs to be set to 0b0001 if the ACK PDU // acknowledges a Finished PDU, and to 0b0000 otherwise. buf[current_idx] |= 0b0001; @@ -185,6 +199,7 @@ impl AckPdu { Ok(current_idx) } + /// Length of the written PDU in bytes. pub fn len_written(&self) -> usize { self.pdu_header.header_len() + self.calc_pdu_datafield_len() } @@ -198,7 +213,7 @@ impl CfdpPdu for AckPdu { #[inline] fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::AckPdu) + Some(FileDirectiveType::Ack) } } @@ -230,10 +245,7 @@ mod tests { assert_eq!(ack_pdu.crc_flag(), expected_crc_flag); assert_eq!(ack_pdu.file_flag(), LargeFileFlag::Normal); assert_eq!(ack_pdu.pdu_type(), PduType::FileDirective); - assert_eq!( - ack_pdu.file_directive_type(), - Some(FileDirectiveType::AckPdu) - ); + assert_eq!(ack_pdu.file_directive_type(), Some(FileDirectiveType::Ack)); assert_eq!(ack_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(ack_pdu.direction(), expected_dir); assert_eq!(ack_pdu.source_id(), TEST_SRC_ID.into()); @@ -247,14 +259,14 @@ mod tests { let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0); let ack_pdu = AckPdu::new( pdu_header, - FileDirectiveType::FinishedPdu, + FileDirectiveType::Finished, ConditionCode::NoError, TransactionStatus::Active, ) .expect("creating ACK PDU failed"); assert_eq!( ack_pdu.directive_code_of_acked_pdu(), - FileDirectiveType::FinishedPdu + FileDirectiveType::Finished ); verify_state(&ack_pdu, CrcFlag::NoCrc, Direction::TowardsReceiver); } @@ -273,8 +285,8 @@ mod tests { assert_eq!(written, ack_pdu.len_written()); verify_raw_header(ack_pdu.pdu_header(), &buf); - assert_eq!(buf[7], FileDirectiveType::AckPdu as u8); - assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::FinishedPdu as u8); + assert_eq!(buf[7], FileDirectiveType::Ack as u8); + assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::Finished as u8); assert_eq!(buf[8] & 0b1111, 0b0001); assert_eq!(buf[9] >> 4 & 0b1111, condition_code as u8); assert_eq!(buf[9] & 0b11, transaction_status as u8); @@ -292,7 +304,7 @@ mod tests { let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0); let ack_pdu = AckPdu::new( pdu_header, - FileDirectiveType::FinishedPdu, + FileDirectiveType::Finished, ConditionCode::NoError, TransactionStatus::Active, ) @@ -320,12 +332,12 @@ mod tests { assert_eq!( AckPdu::new( pdu_header, - FileDirectiveType::MetadataPdu, + FileDirectiveType::Metadata, ConditionCode::NoError, TransactionStatus::Active, ) .unwrap_err(), - InvalidAckedDirectiveCodeError(FileDirectiveType::MetadataPdu) + InvalidAckedDirectiveCodeError(FileDirectiveType::Metadata) ); } @@ -372,7 +384,7 @@ mod tests { ); assert_eq!( ack_pdu.directive_code_of_acked_pdu(), - FileDirectiveType::EofPdu + FileDirectiveType::Eof ); verify_state(&ack_pdu, CrcFlag::WithCrc, Direction::TowardsSender); } diff --git a/src/cfdp/pdu/eof.rs b/src/cfdp/pdu/eof.rs index a896629..c9e85bb 100644 --- a/src/cfdp/pdu/eof.rs +++ b/src/cfdp/pdu/eof.rs @@ -1,3 +1,4 @@ +//! # End-of-File (EOF) PDU packet implementation. use crate::cfdp::pdu::{ add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field, FileDirectiveType, PduError, PduHeader, @@ -25,6 +26,7 @@ pub struct EofPdu { } impl EofPdu { + /// Constructor. pub fn new( mut pdu_header: PduHeader, condition_code: ConditionCode, @@ -45,6 +47,7 @@ impl EofPdu { eof_pdu } + /// Constructor for no error EOF PDUs. pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self { Self::new( pdu_header, @@ -55,21 +58,25 @@ impl EofPdu { ) } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header } + /// Condition code. #[inline] pub fn condition_code(&self) -> ConditionCode { self.condition_code } + /// File checksum. #[inline] pub fn file_checksum(&self) -> u32 { self.file_checksum } + /// File size. #[inline] pub fn file_size(&self) -> u64 { self.file_size @@ -90,6 +97,7 @@ impl EofPdu { len } + /// Construct [Self] from the provided byte slice. pub fn from_bytes(buf: &[u8]) -> Result { let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; @@ -102,13 +110,13 @@ impl EofPdu { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { PduError::InvalidDirectiveType { found: buf[current_idx], - expected: Some(FileDirectiveType::EofPdu), + expected: Some(FileDirectiveType::Eof), } })?; - if directive_type != FileDirectiveType::EofPdu { + if directive_type != FileDirectiveType::Eof { return Err(PduError::WrongDirectiveType { found: directive_type, - expected: FileDirectiveType::EofPdu, + expected: FileDirectiveType::Eof, }); } current_idx += 1; @@ -145,7 +153,7 @@ impl EofPdu { .into()); } let mut current_idx = self.pdu_header.write_to_bytes(buf)?; - buf[current_idx] = FileDirectiveType::EofPdu as u8; + buf[current_idx] = FileDirectiveType::Eof as u8; current_idx += 1; buf[current_idx] = (self.condition_code as u8) << 4; current_idx += 1; @@ -165,6 +173,7 @@ impl EofPdu { Ok(current_idx) } + /// Length of the written PDU in bytes. pub fn len_written(&self) -> usize { self.pdu_header.header_len() + self.calc_pdu_datafield_len() } @@ -178,7 +187,7 @@ impl CfdpPdu for EofPdu { #[inline] fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::EofPdu) + Some(FileDirectiveType::Eof) } } @@ -221,10 +230,7 @@ mod tests { assert_eq!(eof_pdu.crc_flag(), crc_flag); assert_eq!(eof_pdu.file_flag(), file_flag); assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective); - assert_eq!( - eof_pdu.file_directive_type(), - Some(FileDirectiveType::EofPdu) - ); + assert_eq!(eof_pdu.file_directive_type(), Some(FileDirectiveType::Eof)); assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver); assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into()); @@ -253,7 +259,7 @@ mod tests { assert_eq!(written, eof_pdu.len_written()); verify_raw_header(eof_pdu.pdu_header(), &buf); let mut current_idx = eof_pdu.pdu_header().header_len(); - buf[current_idx] = FileDirectiveType::EofPdu as u8; + buf[current_idx] = FileDirectiveType::Eof as u8; current_idx += 1; assert_eq!( (buf[current_idx] >> 4) & 0b1111, diff --git a/src/cfdp/pdu/file_data.rs b/src/cfdp/pdu/file_data.rs index e7a3f7c..c5b0932 100644 --- a/src/cfdp/pdu/file_data.rs +++ b/src/cfdp/pdu/file_data.rs @@ -1,3 +1,4 @@ +//! # File Data PDU packet implementation use crate::cfdp::pdu::{ add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field, PduError, PduHeader, @@ -10,18 +11,24 @@ use serde::{Deserialize, Serialize}; use super::{CfdpPdu, FileDirectiveType, WritablePduPacket}; +/// Record continuation state for segment metadata. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u2, exhaustive = true)] #[repr(u8)] pub enum RecordContinuationState { + /// No start and no end. NoStartNoEnd = 0b00, + /// Start without end. StartWithoutEnd = 0b01, + /// End without start. EndWithoutStart = 0b10, + /// Start and end. StartAndEnd = 0b11, } +/// Segment metadata structure. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SegmentMetadata<'seg_meta> { @@ -30,6 +37,7 @@ pub struct SegmentMetadata<'seg_meta> { } impl<'seg_meta> SegmentMetadata<'seg_meta> { + /// Constructor. pub fn new( record_continuation_state: RecordContinuationState, metadata: Option<&'seg_meta [u8]>, @@ -45,16 +53,19 @@ impl<'seg_meta> SegmentMetadata<'seg_meta> { }) } + /// Record continuation state. #[inline] pub fn record_continuation_state(&self) -> RecordContinuationState { self.record_continuation_state } + /// Raw metadata slice. #[inline] pub fn metadata(&self) -> Option<&'seg_meta [u8]> { self.metadata } + /// Length of the written segment metadata structure. #[inline] pub fn len_written(&self) -> usize { // Map empty metadata to 0 and slice to its length. @@ -169,24 +180,27 @@ pub struct FileDataPdu<'seg_meta, 'file_data> { } impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { + /// Constructor for a file data PDU including segment metadata. pub fn new_with_seg_metadata( pdu_header: PduHeader, segment_metadata: SegmentMetadata<'seg_meta>, offset: u64, file_data: &'file_data [u8], ) -> Self { - Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data) + Self::new(pdu_header, Some(segment_metadata), offset, file_data) } + /// Constructor for a file data PDU without segment metadata. pub fn new_no_seg_metadata( pdu_header: PduHeader, offset: u64, file_data: &'file_data [u8], ) -> Self { - Self::new_generic(pdu_header, None, offset, file_data) + Self::new(pdu_header, None, offset, file_data) } - pub fn new_generic( + /// Generic constructor for a file data PDU. + pub fn new( mut pdu_header: PduHeader, segment_metadata: Option>, offset: u64, @@ -213,26 +227,31 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { .calc_pdu_datafield_len(self.file_data.len() as u64) } + /// Optional segment metadata. #[inline] pub fn segment_metadata(&self) -> Option<&SegmentMetadata<'_>> { self.common.segment_metadata.as_ref() } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { self.common.pdu_header() } + /// File data offset. #[inline] pub fn offset(&self) -> u64 { self.common.offset } + /// File data. #[inline] pub fn file_data(&self) -> &'file_data [u8] { self.file_data } + /// Read [Self] from the provided buffer. pub fn from_bytes<'buf: 'seg_meta + 'file_data>(buf: &'buf [u8]) -> Result { let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; @@ -281,6 +300,7 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { Ok(current_idx) } + /// Length of the written PDU. pub fn len_written(&self) -> usize { self.common.pdu_header.header_len() + self.calc_pdu_datafield_len() } @@ -323,20 +343,23 @@ pub struct FileDataPduCreatorWithReservedDatafield<'seg_meta> { } impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> { + /// Constructor for a file data PDU including segment metadata. pub fn new_with_seg_metadata( pdu_header: PduHeader, segment_metadata: SegmentMetadata<'seg_meta>, offset: u64, file_data_len: u64, ) -> Self { - Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data_len) + Self::new(pdu_header, Some(segment_metadata), offset, file_data_len) } + /// Constructor for a file data PDU without segment metadata. pub fn new_no_seg_metadata(pdu_header: PduHeader, offset: u64, file_data_len: u64) -> Self { - Self::new_generic(pdu_header, None, offset, file_data_len) + Self::new(pdu_header, None, offset, file_data_len) } - pub fn new_generic( + /// Generic constructor. + pub fn new( mut pdu_header: PduHeader, segment_metadata: Option>, offset: u64, @@ -362,6 +385,7 @@ impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> { self.common.calc_pdu_datafield_len(self.file_data_len) } + /// Length of the written PDU. pub fn len_written(&self) -> usize { self.common.pdu_header.header_len() + self.calc_pdu_datafield_len() } @@ -423,6 +447,7 @@ pub struct FileDataPduCreatorWithUnwrittenData<'buf> { } impl FileDataPduCreatorWithUnwrittenData<'_> { + /// Mutable access to the file data field. pub fn file_data_field_mut(&mut self) -> &mut [u8] { &mut self.write_buf[self.file_data_offset as usize ..self.file_data_offset as usize + self.file_data_len as usize] diff --git a/src/cfdp/pdu/finished.rs b/src/cfdp/pdu/finished.rs index a860655..2e97572 100644 --- a/src/cfdp/pdu/finished.rs +++ b/src/cfdp/pdu/finished.rs @@ -1,3 +1,4 @@ +//! # Finished PDU packet implementation. use crate::cfdp::pdu::{ add_pdu_crc, generic_length_checks_pdu_deserialization, FileDirectiveType, PduError, PduHeader, }; @@ -13,23 +14,31 @@ use serde::{Deserialize, Serialize}; use super::tlv::ReadableTlv; use super::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket}; +/// Delivery code enumeration. #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum DeliveryCode { + /// Completed delivery. Complete = 0, + /// Incomplete delivery. Incomplete = 1, } +/// File status enumeration. #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum FileStatus { + /// File was discarded deliberately. DiscardDeliberately = 0b00, + /// File was rejected by the filestore. DiscardedFsRejection = 0b01, + /// File was retained (but not necesarilly complete). Retained = 0b10, + /// Unreported file status. Unreported = 0b11, } @@ -65,6 +74,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> { ) } + /// Constructor where the fault location is provided. pub fn new_with_error( pdu_header: PduHeader, condition_code: ConditionCode, @@ -82,6 +92,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> { ) } + /// Generic constructor. pub fn new( mut pdu_header: PduHeader, condition_code: ConditionCode, @@ -109,32 +120,37 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> { finished_pdu } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header } + /// Condition code. #[inline] pub fn condition_code(&self) -> ConditionCode { self.condition_code } + /// Delivery code. #[inline] pub fn delivery_code(&self) -> DeliveryCode { self.delivery_code } + /// File status. #[inline] pub fn file_status(&self) -> FileStatus { self.file_status } - // If there are no filestore responses, an empty slice will be returned. + /// Filestore responses as a slice. #[inline] pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] { self.fs_responses } + /// Optional fault location [EntityIdTlv]. #[inline] pub fn fault_location(&self) -> Option { self.fault_location @@ -166,7 +182,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> { } let mut current_idx = self.pdu_header.write_to_bytes(buf)?; - buf[current_idx] = FileDirectiveType::FinishedPdu as u8; + buf[current_idx] = FileDirectiveType::Finished as u8; current_idx += 1; buf[current_idx] = ((self.condition_code as u8) << 4) | ((self.delivery_code as u8) << 2) @@ -184,6 +200,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> { Ok(current_idx) } + /// Length of the written PDU in bytes. pub fn len_written(&self) -> usize { self.pdu_header.header_len() + self.calc_pdu_datafield_len() } @@ -197,7 +214,7 @@ impl CfdpPdu for FinishedPduCreator<'_> { #[inline] fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::FinishedPdu) + Some(FileDirectiveType::Finished) } } @@ -242,6 +259,7 @@ impl<'buf> Iterator for FilestoreResponseIterator<'buf> { } } +/// Fnished PDU reader structure. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -269,13 +287,13 @@ impl<'buf> FinishedPduReader<'buf> { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { PduError::InvalidDirectiveType { found: buf[current_idx], - expected: Some(FileDirectiveType::FinishedPdu), + expected: Some(FileDirectiveType::Finished), } })?; - if directive_type != FileDirectiveType::FinishedPdu { + if directive_type != FileDirectiveType::Finished { return Err(PduError::WrongDirectiveType { found: directive_type, - expected: FileDirectiveType::FinishedPdu, + expected: FileDirectiveType::Finished, }); } current_idx += 1; @@ -297,11 +315,13 @@ impl<'buf> FinishedPduReader<'buf> { }) } + /// Raw filestore responses. #[inline] pub fn fs_responses_raw(&self) -> &[u8] { self.fs_responses_raw } + /// Iterator over the filestore responses. #[inline] pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> { FilestoreResponseIterator { @@ -310,26 +330,31 @@ impl<'buf> FinishedPduReader<'buf> { } } + /// Condition code. #[inline] pub fn condition_code(&self) -> ConditionCode { self.condition_code } + /// Delivery code. #[inline] pub fn delivery_code(&self) -> DeliveryCode { self.delivery_code } + /// File status. #[inline] pub fn file_status(&self) -> FileStatus { self.file_status } + /// Optional fault location [EntityIdTlv]. #[inline] pub fn fault_location(&self) -> Option { self.fault_location } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header @@ -399,7 +424,7 @@ impl CfdpPdu for FinishedPduReader<'_> { #[inline] fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::FinishedPdu) + Some(FileDirectiveType::Finished) } } @@ -468,7 +493,7 @@ mod tests { assert_eq!(finished_pdu.pdu_type(), PduType::FileDirective); assert_eq!( finished_pdu.file_directive_type(), - Some(FileDirectiveType::FinishedPdu) + Some(FileDirectiveType::Finished) ); assert_eq!( finished_pdu.transmission_mode(), @@ -500,7 +525,7 @@ mod tests { ); verify_raw_header(finished_pdu.pdu_header(), &buf); let mut current_idx = finished_pdu.pdu_header().header_len(); - assert_eq!(buf[current_idx], FileDirectiveType::FinishedPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Finished as u8); current_idx += 1; assert_eq!( (buf[current_idx] >> 4) & 0b1111, diff --git a/src/cfdp/pdu/metadata.rs b/src/cfdp/pdu/metadata.rs index b04f8b1..fc21368 100644 --- a/src/cfdp/pdu/metadata.rs +++ b/src/cfdp/pdu/metadata.rs @@ -1,3 +1,4 @@ +//! # Metadata PDU packet implementation. #[cfg(feature = "alloc")] use super::tlv::TlvOwned; use crate::cfdp::lv::Lv; @@ -16,16 +17,21 @@ use serde::{Deserialize, Serialize}; use super::tlv::ReadableTlv; use super::{CfdpPdu, WritablePduPacket}; +/// Generic metadata parameters. #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct MetadataGenericParams { + /// Closure requested flag. pub closure_requested: bool, + /// Checksum type. pub checksum_type: ChecksumType, + /// File size. pub file_size: u64, } impl MetadataGenericParams { + /// Constructor. pub fn new(closure_requested: bool, checksum_type: ChecksumType, file_size: u64) -> Self { Self { closure_requested, @@ -35,6 +41,7 @@ impl MetadataGenericParams { } } +/// Build the metadata options from a slice of [Tlv]s pub fn build_metadata_opts_from_slice( buf: &mut [u8], tlvs: &[Tlv], @@ -46,6 +53,7 @@ pub fn build_metadata_opts_from_slice( Ok(written) } +/// Build the metadata options from a vector of [Tlv]s #[cfg(feature = "alloc")] pub fn build_metadata_opts_from_vec( buf: &mut [u8], @@ -54,6 +62,7 @@ pub fn build_metadata_opts_from_vec( build_metadata_opts_from_slice(buf, tlvs.as_slice()) } +/// Build the metadata options from a slice of [TlvOwned]s #[cfg(feature = "alloc")] pub fn build_metadata_opts_from_owned_slice(tlvs: &[TlvOwned]) -> Vec { let mut sum_vec = Vec::new(); @@ -77,6 +86,7 @@ pub struct MetadataPduCreator<'src_name, 'dest_name, 'opts> { } impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'opts> { + /// Constructor for a metadata PDU without options. pub fn new_no_opts( pdu_header: PduHeader, metadata_params: MetadataGenericParams, @@ -92,6 +102,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op ) } + /// Constructor for a metadata PDU with options. pub fn new_with_opts( pdu_header: PduHeader, metadata_params: MetadataGenericParams, @@ -108,6 +119,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op ) } + /// Generic constructor for a metadata PDU. pub fn new( mut pdu_header: PduHeader, metadata_params: MetadataGenericParams, @@ -128,26 +140,31 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op pdu } + /// Metadata generic parameters. #[inline] pub fn metadata_params(&self) -> &MetadataGenericParams { &self.metadata_params } + /// Source file name as a [Lv]. #[inline] pub fn src_file_name(&self) -> Lv<'src_name> { self.src_file_name } + /// Destination file name as a [Lv]. #[inline] pub fn dest_file_name(&self) -> Lv<'dest_name> { self.dest_file_name } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header } + /// Raw options. #[inline] pub fn options(&self) -> &'opts [u8] { self.options @@ -191,7 +208,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op } let mut current_idx = self.pdu_header.write_to_bytes(buf)?; - buf[current_idx] = FileDirectiveType::MetadataPdu as u8; + buf[current_idx] = FileDirectiveType::Metadata as u8; current_idx += 1; buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 6) | (self.metadata_params.checksum_type as u8); @@ -215,6 +232,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op Ok(current_idx) } + /// Length of the written PDU in bytes. pub fn len_written(&self) -> usize { self.pdu_header.header_len() + self.calc_pdu_datafield_len() } @@ -228,7 +246,7 @@ impl CfdpPdu for MetadataPduCreator<'_, '_, '_> { #[inline] fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::MetadataPdu) + Some(FileDirectiveType::Metadata) } } @@ -291,10 +309,12 @@ pub struct MetadataPduReader<'buf> { } impl<'raw> MetadataPduReader<'raw> { + /// Constructor from raw bytes. pub fn new(buf: &'raw [u8]) -> Result { Self::from_bytes(buf) } + /// Constructor from raw bytes. pub fn from_bytes(buf: &'raw [u8]) -> Result { let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; @@ -308,13 +328,13 @@ impl<'raw> MetadataPduReader<'raw> { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { PduError::InvalidDirectiveType { found: buf[current_idx], - expected: Some(FileDirectiveType::MetadataPdu), + expected: Some(FileDirectiveType::Metadata), } })?; - if directive_type != FileDirectiveType::MetadataPdu { + if directive_type != FileDirectiveType::Metadata { return Err(PduError::WrongDirectiveType { found: directive_type, - expected: FileDirectiveType::MetadataPdu, + expected: FileDirectiveType::Metadata, }); } current_idx += 1; @@ -350,26 +370,31 @@ impl<'raw> MetadataPduReader<'raw> { }) } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header } + /// Raw options. #[inline] pub fn options(&self) -> &'raw [u8] { self.options } + /// Generic metadata parameters. #[inline] pub fn metadata_params(&self) -> &MetadataGenericParams { &self.metadata_params } + /// Source file name as a [Lv]. #[inline] pub fn src_file_name(&self) -> Lv<'_> { self.src_file_name } + /// Destination file name as a [Lv]. #[inline] pub fn dest_file_name(&self) -> Lv<'_> { self.dest_file_name @@ -383,12 +408,12 @@ impl CfdpPdu for MetadataPduReader<'_> { } fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::MetadataPdu) + Some(FileDirectiveType::Metadata) } } #[cfg(test)] -pub mod tests { +mod tests { use alloc::string::ToString; use crate::cfdp::lv::Lv; @@ -471,7 +496,7 @@ pub mod tests { ); assert_eq!( metadata_pdu.file_directive_type(), - Some(FileDirectiveType::MetadataPdu) + Some(FileDirectiveType::Metadata) ); assert_eq!( metadata_pdu.transmission_mode(), @@ -502,7 +527,7 @@ pub mod tests { + expected_src_filename.len_full() + expected_dest_filename.len_full() ); - assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8); + assert_eq!(buf[7], FileDirectiveType::Metadata as u8); assert_eq!(buf[8] >> 6, closure_requested as u8); assert_eq!(buf[8] & 0b1111, checksum_type as u8); assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010); @@ -807,10 +832,10 @@ pub mod tests { let error = metadata_error.unwrap_err(); if let PduError::InvalidDirectiveType { found, expected } = error { assert_eq!(found, 0xff); - assert_eq!(expected, Some(FileDirectiveType::MetadataPdu)); + assert_eq!(expected, Some(FileDirectiveType::Metadata)); assert_eq!( error.to_string(), - "invalid directive type, found 255, expected Some(MetadataPdu)" + "invalid directive type, found 255, expected Some(Metadata)" ); } else { panic!("Expected InvalidDirectiveType error, got {:?}", error); @@ -827,16 +852,16 @@ pub mod tests { &[], ); let mut metadata_vec = metadata_pdu.to_vec().unwrap(); - metadata_vec[7] = FileDirectiveType::EofPdu as u8; + metadata_vec[7] = FileDirectiveType::Eof as u8; let metadata_error = MetadataPduReader::from_bytes(&metadata_vec); assert!(metadata_error.is_err()); let error = metadata_error.unwrap_err(); if let PduError::WrongDirectiveType { found, expected } = error { - assert_eq!(found, FileDirectiveType::EofPdu); - assert_eq!(expected, FileDirectiveType::MetadataPdu); + assert_eq!(found, FileDirectiveType::Eof); + assert_eq!(expected, FileDirectiveType::Metadata); assert_eq!( error.to_string(), - "wrong directive type, found EofPdu, expected MetadataPdu" + "wrong directive type, found Eof, expected Metadata" ); } else { panic!("Expected InvalidDirectiveType error, got {:?}", error); diff --git a/src/cfdp/pdu/mod.rs b/src/cfdp/pdu/mod.rs index 770c11c..bbb05f0 100644 --- a/src/cfdp/pdu/mod.rs +++ b/src/cfdp/pdu/mod.rs @@ -15,24 +15,34 @@ pub mod finished; pub mod metadata; pub mod nak; +/// File directive type. #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum FileDirectiveType { - EofPdu = 0x04, - FinishedPdu = 0x05, - AckPdu = 0x06, - MetadataPdu = 0x07, - NakPdu = 0x08, - PromptPdu = 0x09, - KeepAlivePdu = 0x0c, + /// EOF. + Eof = 0x04, + /// Finished. + Finished = 0x05, + /// ACK. + Ack = 0x06, + /// Metadata. + Metadata = 0x07, + /// NAK. + Nak = 0x08, + /// Prompt. + Prompt = 0x09, + /// Keep Alive. + KeepAlive = 0x0c, } +/// PDU error. #[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 { + /// Byte conversion error. #[error("byte conversion error: {0}")] ByteConversion(#[from] ByteConversionError), /// Found version ID invalid, not equal to [super::CFDP_VERSION_2]. @@ -44,27 +54,35 @@ pub enum PduError { /// 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), + /// Source and destination entity ID lengths do not match. #[error( "missmatch of PDU source ID length {src_id_len} and destination ID length {dest_id_len}" )] SourceDestIdLenMissmatch { + /// Source ID length. src_id_len: usize, + /// Destination ID length. 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 directive type. found: FileDirectiveType, + /// Expected directive type. 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 reader. #[error("invalid directive type, found {found:?}, expected {expected:?}")] InvalidDirectiveType { + /// Found raw directive type. found: u8, + /// Expected raw directive type if applicable. expected: Option, }, + /// Invalid start or end of scope for a NAK PDU. #[error("nak pdu: {0}")] InvalidStartOrEndOfScope(#[from] InvalidStartOrEndOfScopeError), /// Invalid condition code. Contains the raw detected value. @@ -74,6 +92,7 @@ pub enum PduError { /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/). #[error("invalid checksum type {0}")] InvalidChecksumType(u8), + /// File size is too large. #[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. @@ -96,9 +115,15 @@ impl From for PduError { } } +/// Generic trait for a PDU which can be written to bytes. pub trait WritablePduPacket { + /// Length when written to bytes. fn len_written(&self) -> usize; + + /// Write the PDU to a raw buffer, returning the written length. fn write_to_bytes(&self, buf: &mut [u8]) -> Result; + + /// Convert the PDU to an owned vector of bytes. #[cfg(feature = "alloc")] fn to_vec(&self) -> Result, PduError> { // This is the correct way to do this. See @@ -112,48 +137,58 @@ pub trait WritablePduPacket { /// Abstraction trait for fields and properties common for all PDUs. pub trait CfdpPdu { + /// PDU header. fn pdu_header(&self) -> &PduHeader; + /// Source ID (file sender). #[inline] fn source_id(&self) -> UnsignedByteField { self.pdu_header().common_pdu_conf().source_entity_id } + /// Destination ID (file sender). #[inline] fn dest_id(&self) -> UnsignedByteField { self.pdu_header().common_pdu_conf().dest_entity_id } + /// Transaction sequence number. #[inline] fn transaction_seq_num(&self) -> UnsignedByteField { self.pdu_header().common_pdu_conf().transaction_seq_num } + /// Transmission mode. #[inline] fn transmission_mode(&self) -> TransmissionMode { self.pdu_header().common_pdu_conf().trans_mode } + /// Direction. #[inline] fn direction(&self) -> Direction { self.pdu_header().common_pdu_conf().direction } + /// CRC flag. #[inline] fn crc_flag(&self) -> CrcFlag { self.pdu_header().common_pdu_conf().crc_flag } + /// File flag. #[inline] fn file_flag(&self) -> LargeFileFlag { self.pdu_header().common_pdu_conf().file_flag } + /// PDU type. #[inline] fn pdu_type(&self) -> PduType { self.pdu_header().pdu_type() } + /// File directive type when applicable. fn file_directive_type(&self) -> Option; } @@ -169,15 +204,21 @@ pub trait CfdpPdu { pub struct CommonPduConfig { source_entity_id: UnsignedByteField, dest_entity_id: UnsignedByteField, + /// Transaction sequence number. pub transaction_seq_num: UnsignedByteField, + /// Transmission mode. pub trans_mode: TransmissionMode, + /// File flag. pub file_flag: LargeFileFlag, + /// CRC flag. pub crc_flag: CrcFlag, + /// Direction. pub direction: Direction, } // TODO: Builder pattern might be applicable here.. impl CommonPduConfig { + /// Generic constructor. #[inline] pub fn new( source_id: impl Into, @@ -210,6 +251,7 @@ impl CommonPduConfig { }) } + /// Constructor for custom byte field with default field values for the other fields. #[inline] pub fn new_with_byte_fields( source_id: impl Into, @@ -227,6 +269,7 @@ impl CommonPduConfig { ) } + /// Source ID (file sender). #[inline] pub fn source_id(&self) -> UnsignedByteField { self.source_entity_id @@ -255,6 +298,7 @@ impl CommonPduConfig { Ok((source_id, dest_id)) } + /// Set the source and destination ID field. #[inline] pub fn set_source_and_dest_id( &mut self, @@ -267,6 +311,7 @@ impl CommonPduConfig { Ok(()) } + /// Destination ID (file receiver). #[inline] pub fn dest_id(&self) -> UnsignedByteField { self.dest_entity_id @@ -305,6 +350,7 @@ impl PartialEq for CommonPduConfig { } } +/// Fixed header length of the PDU header. pub const FIXED_HEADER_LEN: usize = 4; /// Abstraction for the PDU header common to all CFDP PDUs. @@ -322,8 +368,10 @@ pub struct PduHeader { } impl PduHeader { + /// Fixed length of the PDU header when written to a raw buffer. pub const FIXED_LEN: usize = FIXED_HEADER_LEN; + /// Constructor for a File Data PDU header. #[inline] pub fn new_for_file_data( pdu_conf: CommonPduConfig, @@ -340,6 +388,7 @@ impl PduHeader { ) } + /// Constructor for a file data PDU. #[inline] pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { Self::new_generic( @@ -351,6 +400,7 @@ impl PduHeader { ) } + /// Constructor for a file directive PDU. #[inline] pub fn new_for_file_directive(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { Self::new_generic( @@ -362,6 +412,7 @@ impl PduHeader { ) } + /// Constructor from a given [CommonPduConfig] and for a file directive PDU. #[inline] pub fn from_pdu_conf_for_file_directive(pdu_conf: CommonPduConfig) -> Self { Self::new_generic( @@ -373,6 +424,7 @@ impl PduHeader { ) } + /// Generic constructor. #[inline] pub fn new_generic( pdu_type: PduType, @@ -399,6 +451,7 @@ impl PduHeader { + self.pdu_conf.dest_entity_id.size() } + /// PDU data field length. #[inline] pub fn pdu_datafield_len(&self) -> usize { self.pdu_datafield_len.into() @@ -411,6 +464,7 @@ impl PduHeader { self.header_len() + self.pdu_datafield_len as usize } + /// Write the header to a raw buffer, returning the written length on success. pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result { // The API does not allow passing entity IDs with different sizes, so this should // never happen. @@ -578,21 +632,25 @@ impl PduHeader { )) } + /// PDU type. #[inline] pub fn pdu_type(&self) -> PduType { self.pdu_type } + /// Common PDU configuration fields. #[inline] pub fn common_pdu_conf(&self) -> &CommonPduConfig { &self.pdu_conf } + /// Segment metadata flag. #[inline] pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag { self.seg_metadata_flag } + /// Segmentation Control. #[inline] pub fn seg_ctrl(&self) -> SegmentationControl { self.seg_ctrl diff --git a/src/cfdp/pdu/nak.rs b/src/cfdp/pdu/nak.rs index 182b4fa..7cc447b 100644 --- a/src/cfdp/pdu/nak.rs +++ b/src/cfdp/pdu/nak.rs @@ -1,3 +1,4 @@ +//! # NAK PDU packet implementation. use crate::{ cfdp::{CrcFlag, Direction, LargeFileFlag}, ByteConversionError, @@ -8,6 +9,7 @@ use super::{ PduHeader, WritablePduPacket, }; +/// Invalid start or end of scope value. #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[error("invalid start or end of scope value for NAK PDU")] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -52,6 +54,7 @@ fn write_start_and_end_of_scope( current_index } +/// Buffer is too small to even hold a PDU header. #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[error("packet buffer too small for PDU header")] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -94,11 +97,14 @@ pub fn calculate_max_segment_requests( #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SegmentRequests<'a> { + /// Segment requests for normal file sizes bounded by [u32::MAX]. U32Pairs(&'a [(u32, u32)]), + /// Segment requests for large file sizes bounded by [u64::MAX]. U64Pairs(&'a [(u64, u64)]), } impl SegmentRequests<'_> { + /// Wrapped segment requests are empty. #[inline] pub fn is_empty(&self) -> bool { match self { @@ -152,6 +158,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> { ) } + /// Constructor for large file sizes. pub fn new_large_file_size( pdu_header: PduHeader, start_of_scope: u64, @@ -206,21 +213,25 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> { Ok(nak_pdu) } + /// Start of scope of the NAK PDU. #[inline] pub fn start_of_scope(&self) -> u64 { self.start_of_scope } + /// End of scope of the NAK PDU. #[inline] pub fn end_of_scope(&self) -> u64 { self.end_of_scope } + /// Semgent requests. #[inline] pub fn segment_requests(&self) -> Option<&SegmentRequests<'_>> { self.segment_requests.as_ref() } + /// Number of segment requests. #[inline] pub fn num_segment_reqs(&self) -> usize { match &self.segment_requests { @@ -232,6 +243,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> { } } + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header @@ -252,7 +264,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> { .into()); } let mut current_index = self.pdu_header.write_to_bytes(buf)?; - buf[current_index] = FileDirectiveType::NakPdu as u8; + buf[current_index] = FileDirectiveType::Nak as u8; current_index += 1; current_index = write_start_and_end_of_scope( @@ -309,7 +321,7 @@ impl CfdpPdu for NakPduCreator<'_> { #[inline] fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::NakPdu) + Some(FileDirectiveType::Nak) } } @@ -337,6 +349,8 @@ pub struct NakPduCreatorWithReservedSeqReqsBuf<'buf> { } impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> { + /// Calculartes the maximum amount of segment requests which fit into a packet with the + /// provided parameters. pub fn calculate_max_segment_requests( max_packet_size: usize, pdu_header: &PduHeader, @@ -344,6 +358,7 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> { calculate_max_segment_requests(max_packet_size, pdu_header) } + /// Constructor. pub fn new( buf: &'buf mut [u8], mut pdu_header: PduHeader, @@ -371,11 +386,13 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> { } impl NakPduCreatorWithReservedSeqReqsBuf<'_> { + /// PDU header. #[inline] pub fn pdu_header(&self) -> &PduHeader { &self.pdu_header } + /// Number of segment requests. #[inline] pub fn num_segment_reqs(&self) -> usize { self.num_segment_reqs @@ -407,6 +424,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> { Ok(()) } + /// Length when written to a buffer. pub fn len_written(&self) -> usize { self.pdu_header.header_len() + self.calc_pdu_datafield_len() } @@ -427,6 +445,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> { &self.buf[segment_req_buf_offset..segment_req_buf_offset + len] } + /// Iterator over all segment requests. #[inline] pub fn segment_request_iter(&self) -> SegmentRequestIter<'_> { SegmentRequestIter::new( @@ -447,7 +466,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> { ) -> Result { self.set_start_and_end_of_scope(start_of_scope, end_of_scope)?; let mut current_idx = self.pdu_header.write_to_bytes(self.buf).unwrap(); - self.buf[current_idx] = FileDirectiveType::NakPdu as u8; + self.buf[current_idx] = FileDirectiveType::Nak as u8; current_idx += 1; if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large { @@ -613,15 +632,17 @@ impl CfdpPdu for NakPduReader<'_> { } fn file_directive_type(&self) -> Option { - Some(FileDirectiveType::NakPdu) + Some(FileDirectiveType::Nak) } } impl<'seg_reqs> NakPduReader<'seg_reqs> { + /// Constructor from a raw bytestream. pub fn new(buf: &'seg_reqs [u8]) -> Result, PduError> { Self::from_bytes(buf) } + /// Constructor from a raw bytestream. pub fn from_bytes(buf: &'seg_reqs [u8]) -> Result, PduError> { let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; @@ -631,13 +652,13 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { PduError::InvalidDirectiveType { found: buf[current_idx], - expected: Some(FileDirectiveType::NakPdu), + expected: Some(FileDirectiveType::Nak), } })?; - if directive_type != FileDirectiveType::NakPdu { + if directive_type != FileDirectiveType::Nak { return Err(PduError::WrongDirectiveType { found: directive_type, - expected: FileDirectiveType::AckPdu, + expected: FileDirectiveType::Ack, }); } current_idx += 1; @@ -682,14 +703,17 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> { }) } + /// Start of scope. pub fn start_of_scope(&self) -> u64 { self.start_of_scope } + /// End of scope. pub fn end_of_scope(&self) -> u64 { self.end_of_scope } + /// Number of segment requests. pub fn num_segment_reqs(&self) -> usize { if self.seg_reqs_raw.is_empty() { return 0; @@ -747,10 +771,7 @@ mod tests { assert_eq!(nak_pdu.crc_flag(), CrcFlag::NoCrc); assert_eq!(nak_pdu.file_flag(), LargeFileFlag::Normal); assert_eq!(nak_pdu.pdu_type(), PduType::FileDirective); - assert_eq!( - nak_pdu.file_directive_type(), - Some(FileDirectiveType::NakPdu), - ); + assert_eq!(nak_pdu.file_directive_type(), Some(FileDirectiveType::Nak),); assert_eq!(nak_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(nak_pdu.direction(), Direction::TowardsSender); assert_eq!(nak_pdu.source_id(), TEST_SRC_ID.into()); @@ -794,7 +815,7 @@ mod tests { verify_raw_header(nak_pdu.pdu_header(), &buf); let mut current_idx = nak_pdu.pdu_header().header_len(); assert_eq!(current_idx + 9, nak_pdu.len_written()); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); @@ -821,7 +842,7 @@ mod tests { verify_raw_header(nak_pdu.pdu_header(), &buf); let mut current_idx = nak_pdu.pdu_header().header_len(); assert_eq!(current_idx + 9 + 16, nak_pdu.len_written()); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); @@ -1034,7 +1055,7 @@ mod tests { verify_raw_header(&pdu_header, &buf); let mut current_idx = pdu_header.header_len(); assert_eq!(current_idx + 9, len_written); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); @@ -1060,7 +1081,7 @@ mod tests { verify_raw_header(&pdu_header, &buf); let mut current_idx = pdu_header.header_len(); assert_eq!(current_idx + 1 + 8 + 2, len_written); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); @@ -1087,7 +1108,7 @@ mod tests { verify_raw_header(&pdu_header, &buf); let mut current_idx = pdu_header.header_len(); assert_eq!(current_idx + 1 + 16, len_written); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); @@ -1131,7 +1152,7 @@ mod tests { verify_raw_header(&pdu_header, &buf); let mut current_idx = pdu_header.header_len(); assert_eq!(current_idx + 1 + 16, len_written); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); @@ -1171,7 +1192,7 @@ mod tests { verify_raw_header(&pdu_header, &buf); let mut current_idx = pdu_header.header_len(); assert_eq!(current_idx + 1 + 8 + num_segments * 8, len_written); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); @@ -1227,7 +1248,7 @@ mod tests { verify_raw_header(&pdu_header, &buf); let mut current_idx = pdu_header.header_len(); assert_eq!(current_idx + 1 + 16 + num_segments * 16, len_written); - assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); + assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8); current_idx += 1; let start_of_scope = u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); diff --git a/src/cfdp/tlv/mod.rs b/src/cfdp/tlv/mod.rs index dce0a15..bf90b9d 100644 --- a/src/cfdp/tlv/mod.rs +++ b/src/cfdp/tlv/mod.rs @@ -22,6 +22,7 @@ pub mod msg_to_user; /// Minimum length of a type-length-value structure, including type and length fields. pub const MIN_TLV_LEN: usize = 2; +/// Trait for generic TLV structures. pub trait GenericTlv { /// TLV type field. fn tlv_type_field(&self) -> TlvTypeField; @@ -109,21 +110,30 @@ pub enum TlvType { EntityId = 0x06, } +/// TLV type field variants. +/// +/// This allows specifying custom variants as well. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TlvTypeField { + /// Standard TLV types. Standard(TlvType), + /// Custom TLV type. Custom(u8), } +/// Filestore action codes as specified in the standard. #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum FilestoreActionCode { + /// Create file. CreateFile = 0b0000, + /// Delete file. DeleteFile = 0b0001, + /// Rename file. RenameFile = 0b0010, /// This operation appends one file to another. The first specified name will form the first /// part of the new file and the name of the new file. This function can be used to get @@ -132,9 +142,13 @@ pub enum FilestoreActionCode { /// This operation replaces the content of the first specified file with the content of /// the secondly specified file. ReplaceFile = 0b0100, + /// Create directory. CreateDirectory = 0b0101, + /// Remove directory. RemoveDirectory = 0b0110, + /// Deny file. DenyFile = 0b0111, + /// Deny directory. DenyDirectory = 0b1000, } @@ -175,8 +189,10 @@ pub struct Tlv<'data> { } impl<'data> Tlv<'data> { + /// Minimum length of a TLV structure, including type and length fields. pub const MIN_LEN: usize = MIN_TLV_LEN; + /// Generic constructor for a TLV structure. pub fn new(tlv_type: TlvType, data: &[u8]) -> Result, TlvLvDataTooLargeError> { Ok(Tlv { tlv_type_field: TlvTypeField::Standard(tlv_type), @@ -184,6 +200,7 @@ impl<'data> Tlv<'data> { }) } + /// Constructor for a TLV with a custom type field. pub fn new_with_custom_type( tlv_type: u8, data: &[u8], @@ -225,6 +242,7 @@ impl<'data> Tlv<'data> { self.lv.raw_data() } + /// Converts to an owned TLV variant, allocating memory for the value field. #[cfg(feature = "alloc")] pub fn to_owned(&self) -> TlvOwned { TlvOwned { @@ -268,6 +286,7 @@ impl GenericTlv for Tlv<'_> { } } +/// Component of the TLV module which require [alloc] support. #[cfg(feature = "alloc")] pub mod alloc_mod { use super::*; @@ -283,6 +302,7 @@ pub mod alloc_mod { } impl TlvOwned { + /// Generic constructor. pub fn new(tlv_type: TlvType, data: &[u8]) -> Self { Self { tlv_type_field: TlvTypeField::Standard(tlv_type), @@ -290,6 +310,7 @@ pub mod alloc_mod { } } + /// Generic constructor with a custom TLV type. pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Self { Self { tlv_type_field: TlvTypeField::Custom(tlv_type), @@ -305,6 +326,7 @@ pub mod alloc_mod { } } + /// Write to a byte slice. pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result { generic_len_check_data_serialization(buf, self.data.len(), MIN_TLV_LEN)?; buf[0] = self.tlv_type_field.into(); @@ -318,6 +340,7 @@ pub mod alloc_mod { self.data.len() + 2 } + /// Convert to [Tlv] pub fn as_tlv(&self) -> Tlv<'_> { Tlv { tlv_type_field: self.tlv_type_field, @@ -366,6 +389,7 @@ pub mod alloc_mod { } } +/// Entity ID TLV. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -374,6 +398,7 @@ pub struct EntityIdTlv { } impl EntityIdTlv { + /// Constructor. #[inline] pub fn new(entity_id: UnsignedByteField) -> Self { Self { entity_id } @@ -389,21 +414,25 @@ impl EntityIdTlv { Ok(()) } + /// Entity ID. #[inline] pub fn entity_id(&self) -> &UnsignedByteField { &self.entity_id } + /// Length of the value field. #[inline] pub fn len_value(&self) -> usize { self.entity_id.size() } + /// Full length of the TLV, including type and length fields. #[inline] pub fn len_full(&self) -> usize { 2 + self.entity_id.size() } + /// Create from a raw bytestream. pub fn from_bytes(buf: &[u8]) -> Result { Self::len_check(buf)?; verify_tlv_type(buf[0], TlvType::EntityId)?; @@ -495,6 +524,7 @@ impl TryFrom> for EntityIdTlv { } } +/// Does the [FilestoreActionCode] have a second filename? #[inline] pub fn fs_request_has_second_filename(action_code: FilestoreActionCode) -> bool { if action_code == FilestoreActionCode::RenameFile @@ -528,6 +558,7 @@ impl FilestoreTlvBase<'_, '_> { } } +/// Filestore request TLV. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct FilestoreRequestTlv<'first_name, 'second_name> { @@ -536,14 +567,17 @@ pub struct FilestoreRequestTlv<'first_name, 'second_name> { } impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> { + /// Constructor for file creation. pub fn new_create_file(file_name: Lv<'first_name>) -> Result { Self::new(FilestoreActionCode::CreateFile, file_name, None) } + /// Constructor for file deletion. pub fn new_delete_file(file_name: Lv<'first_name>) -> Result { Self::new(FilestoreActionCode::DeleteFile, file_name, None) } + /// Constructor for file renaming. pub fn new_rename_file( source_name: Lv<'first_name>, target_name: Lv<'second_name>, @@ -583,18 +617,22 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> { ) } + /// Constructor for directory creation. pub fn new_create_directory(dir_name: Lv<'first_name>) -> Result { Self::new(FilestoreActionCode::CreateDirectory, dir_name, None) } + /// Constructor for directory removal. pub fn new_remove_directory(dir_name: Lv<'first_name>) -> Result { Self::new(FilestoreActionCode::RemoveDirectory, dir_name, None) } + /// Constructor for file denial. pub fn new_deny_file(file_name: Lv<'first_name>) -> Result { Self::new(FilestoreActionCode::DenyFile, file_name, None) } + /// Constructor for directory denial. pub fn new_deny_directory(dir_name: Lv<'first_name>) -> Result { Self::new(FilestoreActionCode::DenyDirectory, dir_name, None) } @@ -628,31 +666,37 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> { }) } + /// Action code. #[inline] pub fn action_code(&self) -> FilestoreActionCode { self.base.action_code } + /// First name as [Lv]. #[inline] pub fn first_name(&self) -> Lv<'first_name> { self.base.first_name } + /// First name as optional [Lv]. #[inline] pub fn second_name(&self) -> Option> { self.base.second_name } + /// Length of the value field. #[inline] pub fn len_value(&self) -> usize { self.base.base_len_value() } + /// Full TLV length. #[inline] pub fn len_full(&self) -> usize { 2 + self.len_value() } + /// Construct from a raw bytestream. pub fn from_bytes<'longest: 'first_name + 'second_name>( buf: &'longest [u8], ) -> Result { @@ -737,6 +781,7 @@ impl GenericTlv for FilestoreRequestTlv<'_, '_> { } } +/// Filestore response TLV. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -768,6 +813,8 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco Lv::new_empty(), ) } + + /// Generic constructor. pub fn new( action_code: FilestoreActionCode, status_code: u8, @@ -796,6 +843,7 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco }) } + /// Check whether this response has a second filename. pub fn has_second_filename(action_code: FilestoreActionCode) -> bool { if action_code == FilestoreActionCode::RenameFile || action_code == FilestoreActionCode::AppendFile @@ -806,36 +854,43 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco false } + /// Action code. #[inline] pub fn action_code(&self) -> FilestoreActionCode { self.base.action_code } + /// Status code. #[inline] pub fn status_code(&self) -> u8 { self.status_code } + /// First name as [Lv]. #[inline] pub fn first_name(&self) -> Lv<'first_name> { self.base.first_name } + /// Optional second name as [Lv]. #[inline] pub fn second_name(&self) -> Option> { self.base.second_name } + /// Length of the value field. #[inline] pub fn len_value(&self) -> usize { self.base.base_len_value() + self.filestore_message.len_full() } + /// Full length of the TLV. #[inline] pub fn len_full(&self) -> usize { 2 + self.len_value() } + /// Construct from a raw bytestream. pub fn from_bytes<'buf: 'first_name + 'second_name + 'fs_msg>( buf: &'buf [u8], ) -> Result { diff --git a/src/cfdp/tlv/msg_to_user.rs b/src/cfdp/tlv/msg_to_user.rs index f3fe04a..a643ac1 100644 --- a/src/cfdp/tlv/msg_to_user.rs +++ b/src/cfdp/tlv/msg_to_user.rs @@ -8,8 +8,10 @@ use crate::{ }; use delegate::delegate; +/// Message To User TLV structure. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct MsgToUserTlv<'data> { + /// Wrapped generic TLV structure. pub tlv: Tlv<'data>, } @@ -23,7 +25,9 @@ impl<'data> MsgToUserTlv<'data> { delegate! { to self.tlv { + /// Value field of the TLV. pub fn value(&self) -> &[u8]; + /// Helper method to retrieve the length of the value. Simply calls the [slice::len] method of /// [Self::value] pub fn len_value(&self) -> usize; @@ -37,14 +41,16 @@ impl<'data> MsgToUserTlv<'data> { } } + /// Is this a standard TLV? #[inline] pub fn is_standard_tlv(&self) -> bool { true } + /// TLV type field. #[inline] - pub fn tlv_type(&self) -> Option { - Some(TlvType::MsgToUser) + pub fn tlv_type(&self) -> TlvType { + TlvType::MsgToUser } /// Check whether this message is a reserved CFDP message like a Proxy Operation Message. @@ -85,11 +91,13 @@ impl<'data> MsgToUserTlv<'data> { Ok(msg_to_user) } + /// Convert to a generic [Tlv]. #[inline] pub fn to_tlv(&self) -> Tlv<'data> { self.tlv } + /// Convert to an [TlvOwned]. #[cfg(feature = "alloc")] pub fn to_owned(&self) -> TlvOwned { self.tlv.to_owned() @@ -102,6 +110,7 @@ impl<'data> MsgToUserTlv<'data> { delegate!( to self.tlv { + /// Write the TLV to a byte buffer. pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result; } ); @@ -142,7 +151,7 @@ mod tests { assert!(msg_to_user.is_ok()); let msg_to_user = msg_to_user.unwrap(); assert!(msg_to_user.is_standard_tlv()); - assert_eq!(msg_to_user.tlv_type().unwrap(), TlvType::MsgToUser); + assert_eq!(msg_to_user.tlv_type(), TlvType::MsgToUser); assert_eq!( msg_to_user.tlv_type_field(), TlvTypeField::Standard(TlvType::MsgToUser) diff --git a/src/lib.rs b/src/lib.rs index e4ba944..f410729 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,7 @@ pub const MAX_SEQ_COUNT: u14 = u14::MAX; pub enum ChecksumType { /// Check the default CRC16-CCITT checksum. WithCrc16, - /// Packet has a CRC16 which should be ignored. + /// Packet has a CRC16 which should be ignored. /// /// It is either not generated for packet creation or ignored when reading a packet. WithCrc16ButIgnored, diff --git a/src/time/mod.rs b/src/time/mod.rs index fb3d373..43bf4ee 100644 --- a/src/time/mod.rs +++ b/src/time/mod.rs @@ -1,4 +1,5 @@ //! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) +#![warn(missing_docs)] use crate::ByteConversionError; #[cfg(feature = "chrono")] use chrono::{TimeZone, Utc}; -- 2.43.0