diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d5bc5..97202b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] +- Bumped `thiserror` to v2 +- Bumped `spacepackets` to v0.13 +- The source and destination handlers can now be used without the `std` feature and only require + the `alloc` feature. + # [v0.1.0] 2024-09-11 Initial release diff --git a/Cargo.toml b/Cargo.toml index 8225fa2..6cb2745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,18 +18,19 @@ name = "cfdp" [dependencies] crc = "3" smallvec = "1" -derive-new = "0.6" +derive-new = ">=0.6, <=0.7" [dependencies.spacepackets] -version = "0.12" +version = "0.13" default-features = false +path = "../spacepackets" [dependencies.thiserror] -version = "1" -optional = true +version = "2" +default-features = false [dependencies.hashbrown] -version = "0.14" +version = ">=0.14, <=0.15" optional = true [dependencies.serde] @@ -44,7 +45,7 @@ optional = true default = ["std"] std = [ "alloc", - "thiserror", + "thiserror/std", "spacepackets/std" ] alloc = [ @@ -58,7 +59,7 @@ defmt = ["dep:defmt", "spacepackets/defmt"] tempfile = "3" rand = "0.8" log = "0.4" -fern = "0.6" +fern = "0.7" chrono = "0.4" clap = { version = "4", features = ["derive"] } diff --git a/README.md b/README.md index 0479568..4c4855a 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,13 @@ The underlying base packet library used to generate the packets to be sent is th # Features The goal of this library is to be flexible enough to support the use-cases of both on-board -software and of ground software. It has support to make integration on [std] systems as simple -as possible, but also has sufficient abstraction to allow for integration on`no_std` environments. -Currently, the handlers still require the [std] feature until -[thiserror supports `error_in_core`](https://github.com/dtolnay/thiserror/pull/304). +software and of ground software. It has support to make integration on `std` systems as simple +as possible, but also has sufficient abstraction to allow for integration on`no_std` environments +and can be used on these systems as well as long as the `alloc` feature is activated. -It is recommended to activate the `alloc` feature at the very least to allow using the primary -components provided by this crate. These components will only allocate memory at initialization -time and thus are still viable for systems where run-time allocation is prohibited. +Please note even though the `alloc` feature is required for the core handlers, these components +will only allocate memory at initialization time and thus are still viable for systems where +run-time allocation is prohibited. ## Default features diff --git a/src/dest.rs b/src/dest.rs index 8b7d48e..0b6c31c 100644 --- a/src/dest.rs +++ b/src/dest.rs @@ -55,7 +55,6 @@ use spacepackets::{ }, util::{UnsignedByteField, UnsignedEnum}, }; -use thiserror::Error; #[derive(Debug)] struct FileProperties { @@ -189,7 +188,7 @@ impl TransactionParams { } } -#[derive(Debug, Error)] +#[derive(Debug, thiserror::Error)] pub enum DestError { /// File directive expected, but none specified #[error("expected file directive")] @@ -215,6 +214,7 @@ pub enum DestError { #[error("pdu error {0}")] Pdu(#[from] PduError), #[error("io error {0}")] + #[cfg(feature = "std")] Io(#[from] std::io::Error), #[error("file store error {0}")] Filestore(#[from] FilestoreError), diff --git a/src/filestore.rs b/src/filestore.rs index 9af7022..7709c9c 100644 --- a/src/filestore.rs +++ b/src/filestore.rs @@ -1,103 +1,38 @@ -use alloc::string::{String, ToString}; -use core::fmt::Display; use spacepackets::cfdp::ChecksumType; use spacepackets::ByteConversionError; #[cfg(feature = "std")] -use std::error::Error; -use std::path::Path; -#[cfg(feature = "std")] pub use std_mod::*; -#[derive(Debug, Clone)] +#[derive(Debug, thiserror::Error)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum FilestoreError { + #[error("file does not exist")] FileDoesNotExist, + #[error("file already exists")] FileAlreadyExists, + #[error("directory does not exist")] DirDoesNotExist, + #[error("permission error")] Permission, + #[error("is not a file")] IsNotFile, + #[error("is not a directory")] IsNotDirectory, - ByteConversion(ByteConversionError), - Io { - raw_errno: Option, - string: String, - }, + #[error("byte conversion: {0}")] + ByteConversion(#[from] ByteConversionError), + #[error("IO error: {0})")] + #[cfg(feature = "std")] + Io(#[from] std::io::Error), + #[error("checksum type not implemented: {0:?}")] ChecksumTypeNotImplemented(ChecksumType), + #[error("utf8 error")] Utf8Error, + #[error("other error")] Other, } -impl From for FilestoreError { - fn from(value: ByteConversionError) -> Self { - Self::ByteConversion(value) - } -} - -impl Display for FilestoreError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - FilestoreError::FileDoesNotExist => { - write!(f, "file does not exist") - } - FilestoreError::FileAlreadyExists => { - write!(f, "file already exists") - } - FilestoreError::DirDoesNotExist => { - write!(f, "directory does not exist") - } - FilestoreError::Permission => { - write!(f, "permission error") - } - FilestoreError::IsNotFile => { - write!(f, "is not a file") - } - FilestoreError::IsNotDirectory => { - write!(f, "is not a directory") - } - FilestoreError::ByteConversion(e) => { - write!(f, "filestore error: {e}") - } - FilestoreError::Io { raw_errno, string } => { - write!( - f, - "filestore generic IO error with raw errno {:?}: {}", - raw_errno, string - ) - } - FilestoreError::ChecksumTypeNotImplemented(checksum_type) => { - write!(f, "checksum {:?} not implemented", checksum_type) - } - FilestoreError::Utf8Error => { - write!(f, "utf8 error") - } - FilestoreError::Other => { - write!(f, "some filestore error occured") - } - } - } -} - -impl Error for FilestoreError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - FilestoreError::ByteConversion(e) => Some(e), - _ => None, - } - } -} - -#[cfg(feature = "std")] -impl From for FilestoreError { - fn from(value: std::io::Error) -> Self { - Self::Io { - raw_errno: value.raw_os_error(), - string: value.to_string(), - } - } -} - pub trait VirtualFilestore { fn create_file(&self, file_path: &str) -> Result<(), FilestoreError>; @@ -122,14 +57,7 @@ pub trait VirtualFilestore { fn filename_from_full_path(path: &str) -> Option<&str> where - Self: Sized, - { - // Convert the path string to a Path - let path = Path::new(path); - - // Extract the file name using the file_name() method - path.file_name().and_then(|name| name.to_str()) - } + Self: Sized; fn is_file(&self, path: &str) -> Result; @@ -193,7 +121,7 @@ pub mod std_mod { use super::*; use std::{ fs::{self, File, OpenOptions}, - io::{BufReader, Read, Seek, SeekFrom, Write}, + io::{BufReader, Read, Seek, SeekFrom, Write}, path::Path, }; #[derive(Default)] @@ -241,10 +169,7 @@ pub mod std_mod { } fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError> { - fs::create_dir(dir_path).map_err(|e| FilestoreError::Io { - raw_errno: e.raw_os_error(), - string: e.to_string(), - })?; + fs::create_dir(dir_path)?; Ok(()) } @@ -358,6 +283,17 @@ pub mod std_mod { _ => Err(FilestoreError::ChecksumTypeNotImplemented(checksum_type)), } } + + fn filename_from_full_path(path: &str) -> Option<&str> + where + Self: Sized, + { + // Convert the path string to a Path + let path = Path::new(path); + + // Extract the file name using the file_name() method + path.file_name().and_then(|name| name.to_str()) + } } impl NativeFilestore { @@ -393,7 +329,7 @@ pub mod std_mod { #[cfg(test)] mod tests { - use std::{fs, path::Path, println}; + use std::{fs, path::Path, println, string::ToString}; use super::*; use alloc::format; diff --git a/src/lib.rs b/src/lib.rs index b9bd698..d475b57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,11 +14,12 @@ //! The goal of this library is to be flexible enough to support the use-cases of both on-board //! software and of ground software. It has support to make integration on [std] systems as simple //! as possible, but also has sufficient abstraction to allow for integration on `no_std` -//! environments. Currently, the handlers still require the [std] feature until -//! [thiserror supports `error_in_core`](https://github.com/dtolnay/thiserror/pull/304). -//! It is recommended to activate the `alloc` feature at the very least to allow using the primary -//! components provided by this crate. These components will only allocate memory at initialization -//! time and thus are still viable for systems where run-time allocation is prohibited. +//! environments and can be used on these systems as well as long as the [alloc] feature is used +//! as well. +//! +//! Please note even though the [alloc] feature is required for the core handlers, these components +//! will only allocate memory at initialization time and thus are still viable for systems where +//! run-time allocation is prohibited. //! //! The core of this library are the [crate::dest::DestinationHandler] and the //! [crate::source::SourceHandler] components which model the CFDP destination and source entity @@ -70,12 +71,11 @@ extern crate alloc; #[cfg(any(feature = "std", test))] extern crate std; -#[cfg(feature = "std")] -pub mod dest; #[cfg(feature = "alloc")] +pub mod dest; pub mod filestore; pub mod request; -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] pub mod source; pub mod time; pub mod user; @@ -83,8 +83,6 @@ pub mod user; use crate::time::CountdownProvider; use core::{cell::RefCell, fmt::Debug, hash::Hash}; use crc::{Crc, CRC_32_ISCSI, CRC_32_ISO_HDLC}; -#[cfg(feature = "std")] -use hashbrown::HashMap; #[cfg(feature = "alloc")] pub use alloc_mod::*; @@ -288,12 +286,12 @@ pub trait RemoteEntityConfigProvider { fn remove_config(&mut self, remote_id: u64) -> bool; } -/// This is a thin wrapper around a [HashMap] to store remote entity configurations. +/// This is a thin wrapper around a [hashbrown::HashMap] to store remote entity configurations. /// It implements the full [RemoteEntityConfigProvider] trait. -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] #[derive(Default, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct StdRemoteEntityConfigProvider(pub HashMap); +pub struct StdRemoteEntityConfigProvider(pub hashbrown::HashMap); #[cfg(feature = "std")] impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider { @@ -879,7 +877,7 @@ impl<'raw> PduRawWithInfo<'raw> { }); } if pdu_header.pdu_datafield_len() < 1 { - return Err(PduError::FormatError); + return Err(PduError::Format); } // Route depending on PDU type and directive type if applicable. Retrieve directive type // from the raw stream for better performance (with sanity and directive code check). @@ -1548,7 +1546,7 @@ pub(crate) mod tests { #[test] fn transaction_id_hashable_usable_as_map_key() { - let mut map = HashMap::new(); + let mut map = hashbrown::HashMap::new(); let transaction_id_0 = TransactionId::new( UnsignedByteFieldU8::new(1).into(), UnsignedByteFieldU8::new(2).into(), diff --git a/src/source.rs b/src/source.rs index fbd1299..dbced99 100644 --- a/src/source.rs +++ b/src/source.rs @@ -748,25 +748,6 @@ impl< } fn notice_of_completion(&mut self, cfdp_user: &mut impl CfdpUser) { - /* - def _notice_of_completion(self): - if self.cfg.indication_cfg.transaction_finished_indication_required: - assert self._params.transaction_id is not None - # This happens for unacknowledged file copy operation with no closure. - if self._params.finished_params is None: - self._params.finished_params = FinishedParams( - condition_code=ConditionCode.NO_ERROR, - delivery_code=DeliveryCode.DATA_COMPLETE, - file_status=FileStatus.FILE_STATUS_UNREPORTED, - ) - indication_params = TransactionFinishedParams( - transaction_id=self._params.transaction_id, - finished_params=self._params.finished_params, - ) - self.user.transaction_finished_indication(indication_params) - # Transaction finished - self.reset() - */ let tstate = self.tstate.as_ref().unwrap(); if self.local_cfg.indication_cfg.transaction_finished { // The first case happens for unacknowledged file copy operation with no closure.