add filestore response abstraction
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit

This commit is contained in:
Robin Müller 2023-12-05 16:29:30 +01:00
parent 4945ea804d
commit dc2b97b848
2 changed files with 261 additions and 27 deletions

View File

@ -190,7 +190,7 @@ impl<'fs_responses> FinishedPdu<'fs_responses> {
}
} else if tlv_type == TlvType::EntityId {
// At least one FS response is included.
if current_idx > full_len_without_crc {
if current_idx > start_of_fs_responses {
fs_responses = Some(&buf[start_of_fs_responses..current_idx]);
}
fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
@ -402,6 +402,27 @@ mod tests {
assert_eq!(finished_pdu, read_back);
}
#[test]
fn test_serialization_buf_too_small() {
let finished_pdu = generic_finished_pdu(
CrcFlag::NoCrc,
LargeFileFlag::Normal,
DeliveryCode::Complete,
FileStatus::Retained,
);
let mut buf: [u8; 8] = [0; 8];
let error = finished_pdu.write_to_bytes(&mut buf);
assert!(error.is_err());
if let PduError::ByteConversion(ByteConversionError::ToSliceTooSmall { found, expected }) =
error.unwrap_err()
{
assert_eq!(found, 8);
assert_eq!(expected, 9);
} else {
panic!("expected to_slice_too_small error");
}
}
#[test]
fn test_with_crc() {
let finished_pdu = generic_finished_pdu(
@ -467,4 +488,7 @@ mod tests {
entity_id_tlv
)
}
#[test]
fn test_deserialization_with_fs_responses() {}
}

View File

@ -335,23 +335,29 @@ impl<'data> TryFrom<Tlv<'data>> for EntityIdTlv {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct FilestoreTlvBase<'first_name, 'second_name> {
pub action_code: FilestoreActionCode,
#[cfg_attr(feature = "serde", serde(borrow))]
pub first_name: Lv<'first_name>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub second_name: Option<Lv<'second_name>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FilestoreRequestTlv<'first_name, 'second_name> {
action_code: FilestoreActionCode,
#[cfg_attr(feature = "serde", serde(borrow))]
first_name: Lv<'first_name>,
#[cfg_attr(feature = "serde", serde(borrow))]
second_name: Option<Lv<'second_name>>,
base: FilestoreTlvBase<'first_name, 'second_name>,
}
impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
pub fn new_create_file(first_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::CreateFile, first_name, None)
pub fn new_create_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::CreateFile, file_name, None)
}
pub fn new_delete_file(first_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DeleteFile, first_name, None)
pub fn new_delete_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DeleteFile, file_name, None)
}
pub fn new_rename_file(
@ -413,7 +419,7 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
/// only one is passed. It will also returns [None] if the cumulative length of the first
/// name and the second name exceeds 255 bytes.
///
/// This is the case for the rename, append and replace filestore request.
/// Two file paths are required for the rename, append and replace filestore request.
pub fn new(
action_code: FilestoreActionCode,
first_name: Lv<'first_name>,
@ -430,9 +436,11 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
return Err(TlvLvError::InvalidValueLength(base_value_len));
}
Ok(Self {
base: FilestoreTlvBase {
action_code,
first_name,
second_name,
},
})
}
@ -447,20 +455,20 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
}
pub fn action_code(&self) -> FilestoreActionCode {
self.action_code
self.base.action_code
}
pub fn first_name(&self) -> Lv<'first_name> {
self.first_name
self.base.first_name
}
pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.second_name
self.base.second_name
}
pub fn len_value(&self) -> usize {
let mut len = 1 + self.first_name.len_full();
if let Some(second_name) = self.second_name {
let mut len = 1 + self.base.first_name.len_full();
if let Some(second_name) = self.base.second_name {
len += second_name.len_full();
}
len
@ -497,9 +505,11 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
second_name = Some(Lv::from_bytes(&buf[current_idx..])?);
}
Ok(Self {
base: FilestoreTlvBase {
action_code,
first_name,
second_name,
},
})
}
}
@ -514,14 +524,14 @@ impl WritableTlv for FilestoreRequestTlv<'_, '_> {
}
buf[0] = TlvType::FilestoreRequest as u8;
buf[1] = self.len_value() as u8;
buf[2] = (self.action_code as u8) << 4;
buf[2] = (self.base.action_code as u8) << 4;
let mut current_idx = 3;
// Length checks were already performed.
self.first_name.write_to_be_bytes_no_len_check(
&mut buf[current_idx..current_idx + self.first_name.len_full()],
self.base.first_name.write_to_be_bytes_no_len_check(
&mut buf[current_idx..current_idx + self.base.first_name.len_full()],
);
current_idx += self.first_name.len_full();
if let Some(second_name) = self.second_name {
current_idx += self.base.first_name.len_full();
if let Some(second_name) = self.base.second_name {
second_name.write_to_be_bytes_no_len_check(
&mut buf[current_idx..current_idx + second_name.len_full()],
);
@ -541,6 +551,191 @@ impl GenericTlv for FilestoreRequestTlv<'_, '_> {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FilestoreResponseTlv<'first_name, 'second_name, 'fs_msg> {
base: FilestoreTlvBase<'first_name, 'second_name>,
status_code: u8,
filestore_message: Lv<'fs_msg>,
}
impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'second_name, 'fs_msg> {
/// This function will return [None] if the respective action code requires two names but
/// only one is passed. It will also returns [None] if the cumulative length of the first
/// name and the second name exceeds 255 bytes.
///
/// Two file paths are required for the rename, append and replace filestore request.
pub fn new_no_filestore_message(
action_code: FilestoreActionCode,
status_code: u8,
first_name: Lv<'first_name>,
second_name: Option<Lv<'second_name>>,
) -> Result<Self, TlvLvError> {
Self::new(
action_code,
status_code,
first_name,
second_name,
Lv::new_empty(),
)
}
pub fn new(
action_code: FilestoreActionCode,
status_code: u8,
first_name: Lv<'first_name>,
second_name: Option<Lv<'second_name>>,
filestore_message: Lv<'fs_msg>,
) -> Result<Self, TlvLvError> {
let mut base_value_len = first_name.len_full();
if Self::has_second_filename(action_code) {
if second_name.is_none() {
return Err(TlvLvError::SecondNameMissing);
}
base_value_len += second_name.as_ref().unwrap().len_full();
}
if base_value_len > u8::MAX as usize {
return Err(TlvLvError::InvalidValueLength(base_value_len));
}
Ok(Self {
base: FilestoreTlvBase {
action_code,
first_name,
second_name,
},
status_code,
filestore_message,
})
}
pub fn has_second_filename(action_code: FilestoreActionCode) -> bool {
if action_code == FilestoreActionCode::RenameFile
|| action_code == FilestoreActionCode::AppendFile
|| action_code == FilestoreActionCode::ReplaceFile
{
return true;
}
false
}
pub fn action_code(&self) -> FilestoreActionCode {
self.base.action_code
}
pub fn first_name(&self) -> Lv<'first_name> {
self.base.first_name
}
pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.base.second_name
}
pub fn len_value(&self) -> usize {
let mut len = 1 + self.base.first_name.len_full();
if let Some(second_name) = self.base.second_name {
len += second_name.len_full();
}
len += self.filestore_message.len_full();
len
}
pub fn len_full(&self) -> usize {
2 + self.len_value()
}
pub fn from_bytes<'buf: 'first_name + 'second_name + 'fs_msg>(
buf: &'buf [u8],
) -> Result<Self, TlvLvError> {
if buf.len() < 2 {
return Err(ByteConversionError::FromSliceTooSmall {
found: buf.len(),
expected: 2,
}
.into());
}
verify_tlv_type(buf[0], TlvType::FilestoreRequest)?;
let len = buf[1] as usize;
let mut current_idx = 2;
let len_check = |current_idx: &mut usize, add_len: usize| -> Result<(), TlvLvError> {
if *current_idx + add_len > buf.len() {
return Err(ByteConversionError::FromSliceTooSmall {
found: buf.len(),
expected: *current_idx,
}
.into());
}
Ok(())
};
len_check(&mut current_idx, len)?;
let action_code = FilestoreActionCode::try_from((buf[2] >> 4) & 0b1111)
.map_err(|_| TlvLvError::InvalidFilestoreActionCode((buf[2] >> 4) & 0b1111))?;
let status_code = buf[2] & 0b1111;
current_idx += 1;
let first_name = Lv::from_bytes(&buf[current_idx..])?;
len_check(&mut current_idx, first_name.len_full())?;
current_idx += first_name.len_full();
let mut second_name = None;
if Self::has_second_filename(action_code) {
if current_idx >= 2 + len {
return Err(TlvLvError::SecondNameMissing);
}
let second_name_lv = Lv::from_bytes(&buf[current_idx..])?;
current_idx += second_name_lv.len_full();
second_name = Some(second_name_lv);
}
let filestore_message = Lv::from_bytes(&buf[current_idx..])?;
len_check(&mut current_idx, filestore_message.len_full())?;
Ok(Self {
base: FilestoreTlvBase {
action_code,
first_name,
second_name,
},
status_code,
filestore_message,
})
}
}
impl WritableTlv for FilestoreResponseTlv<'_, '_, '_> {
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
if buf.len() < self.len_full() {
return Err(ByteConversionError::ToSliceTooSmall {
found: buf.len(),
expected: self.len_full(),
});
}
buf[0] = TlvType::FilestoreRequest as u8;
buf[1] = self.len_value() as u8;
buf[2] = ((self.base.action_code as u8) << 4) | (self.status_code & 0b1111);
let mut current_idx = 3;
// Length checks were already performed.
self.base.first_name.write_to_be_bytes_no_len_check(
&mut buf[current_idx..current_idx + self.base.first_name.len_full()],
);
current_idx += self.base.first_name.len_full();
if let Some(second_name) = self.base.second_name {
current_idx += second_name.write_to_be_bytes_no_len_check(
&mut buf[current_idx..current_idx + second_name.len_full()],
);
}
current_idx += self.filestore_message.write_to_be_bytes_no_len_check(
&mut buf[current_idx..current_idx + self.filestore_message.len_full()],
);
Ok(current_idx)
}
fn len_written(&self) -> usize {
self.len_full()
}
}
impl GenericTlv for FilestoreResponseTlv<'_, '_, '_> {
fn tlv_type_field(&self) -> TlvTypeField {
TlvTypeField::Standard(TlvType::FilestoreResponse)
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -903,6 +1098,21 @@ mod tests {
assert_eq!(req_conv_back, req);
}
#[test]
fn test_fs_response_serialization() {
let lv_0 = Lv::new_from_str(TLV_TEST_STR_0).unwrap();
let response = FilestoreResponseTlv::new_no_filestore_message(
FilestoreActionCode::CreateFile,
0b0001,
lv_0,
None,
)
.expect("creating response failed");
let mut buf: [u8; 32] = [0; 32];
let written_len = response.write_to_bytes(&mut buf).unwrap();
assert_eq!(written_len, 2 + 1 + lv_0.len_full() + 1);
}
#[test]
fn test_entity_it_tlv_to_tlv() {
let entity_id = UbfU16::new(0x0102);