add filestore response abstraction
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
This commit is contained in:
parent
4945ea804d
commit
dc2b97b848
@ -190,7 +190,7 @@ impl<'fs_responses> FinishedPdu<'fs_responses> {
|
|||||||
}
|
}
|
||||||
} else if tlv_type == TlvType::EntityId {
|
} else if tlv_type == TlvType::EntityId {
|
||||||
// At least one FS response is included.
|
// 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]);
|
fs_responses = Some(&buf[start_of_fs_responses..current_idx]);
|
||||||
}
|
}
|
||||||
fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
|
fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
|
||||||
@ -402,6 +402,27 @@ mod tests {
|
|||||||
assert_eq!(finished_pdu, read_back);
|
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]
|
#[test]
|
||||||
fn test_with_crc() {
|
fn test_with_crc() {
|
||||||
let finished_pdu = generic_finished_pdu(
|
let finished_pdu = generic_finished_pdu(
|
||||||
@ -467,4 +488,7 @@ mod tests {
|
|||||||
entity_id_tlv
|
entity_id_tlv
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialization_with_fs_responses() {}
|
||||||
}
|
}
|
||||||
|
@ -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)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct FilestoreRequestTlv<'first_name, 'second_name> {
|
pub struct FilestoreRequestTlv<'first_name, 'second_name> {
|
||||||
action_code: FilestoreActionCode,
|
base: FilestoreTlvBase<'first_name, 'second_name>,
|
||||||
#[cfg_attr(feature = "serde", serde(borrow))]
|
|
||||||
first_name: Lv<'first_name>,
|
|
||||||
#[cfg_attr(feature = "serde", serde(borrow))]
|
|
||||||
second_name: Option<Lv<'second_name>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'first_name, 'second_name> FilestoreRequestTlv<'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> {
|
pub fn new_create_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
|
||||||
Self::new(FilestoreActionCode::CreateFile, first_name, None)
|
Self::new(FilestoreActionCode::CreateFile, file_name, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_delete_file(first_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
|
pub fn new_delete_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
|
||||||
Self::new(FilestoreActionCode::DeleteFile, first_name, None)
|
Self::new(FilestoreActionCode::DeleteFile, file_name, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_rename_file(
|
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
|
/// only one is passed. It will also returns [None] if the cumulative length of the first
|
||||||
/// name and the second name exceeds 255 bytes.
|
/// 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(
|
pub fn new(
|
||||||
action_code: FilestoreActionCode,
|
action_code: FilestoreActionCode,
|
||||||
first_name: Lv<'first_name>,
|
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));
|
return Err(TlvLvError::InvalidValueLength(base_value_len));
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
action_code,
|
base: FilestoreTlvBase {
|
||||||
first_name,
|
action_code,
|
||||||
second_name,
|
first_name,
|
||||||
|
second_name,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,20 +455,20 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn action_code(&self) -> FilestoreActionCode {
|
pub fn action_code(&self) -> FilestoreActionCode {
|
||||||
self.action_code
|
self.base.action_code
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_name(&self) -> Lv<'first_name> {
|
pub fn first_name(&self) -> Lv<'first_name> {
|
||||||
self.first_name
|
self.base.first_name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn second_name(&self) -> Option<Lv<'second_name>> {
|
pub fn second_name(&self) -> Option<Lv<'second_name>> {
|
||||||
self.second_name
|
self.base.second_name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len_value(&self) -> usize {
|
pub fn len_value(&self) -> usize {
|
||||||
let mut len = 1 + self.first_name.len_full();
|
let mut len = 1 + self.base.first_name.len_full();
|
||||||
if let Some(second_name) = self.second_name {
|
if let Some(second_name) = self.base.second_name {
|
||||||
len += second_name.len_full();
|
len += second_name.len_full();
|
||||||
}
|
}
|
||||||
len
|
len
|
||||||
@ -497,9 +505,11 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
|
|||||||
second_name = Some(Lv::from_bytes(&buf[current_idx..])?);
|
second_name = Some(Lv::from_bytes(&buf[current_idx..])?);
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
action_code,
|
base: FilestoreTlvBase {
|
||||||
first_name,
|
action_code,
|
||||||
second_name,
|
first_name,
|
||||||
|
second_name,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -514,14 +524,14 @@ impl WritableTlv for FilestoreRequestTlv<'_, '_> {
|
|||||||
}
|
}
|
||||||
buf[0] = TlvType::FilestoreRequest as u8;
|
buf[0] = TlvType::FilestoreRequest as u8;
|
||||||
buf[1] = self.len_value() 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;
|
let mut current_idx = 3;
|
||||||
// Length checks were already performed.
|
// Length checks were already performed.
|
||||||
self.first_name.write_to_be_bytes_no_len_check(
|
self.base.first_name.write_to_be_bytes_no_len_check(
|
||||||
&mut buf[current_idx..current_idx + self.first_name.len_full()],
|
&mut buf[current_idx..current_idx + self.base.first_name.len_full()],
|
||||||
);
|
);
|
||||||
current_idx += self.first_name.len_full();
|
current_idx += self.base.first_name.len_full();
|
||||||
if let Some(second_name) = self.second_name {
|
if let Some(second_name) = self.base.second_name {
|
||||||
second_name.write_to_be_bytes_no_len_check(
|
second_name.write_to_be_bytes_no_len_check(
|
||||||
&mut buf[current_idx..current_idx + second_name.len_full()],
|
&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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -903,6 +1098,21 @@ mod tests {
|
|||||||
assert_eq!(req_conv_back, req);
|
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]
|
#[test]
|
||||||
fn test_entity_it_tlv_to_tlv() {
|
fn test_entity_it_tlv_to_tlv() {
|
||||||
let entity_id = UbfU16::new(0x0102);
|
let entity_id = UbfU16::new(0x0102);
|
||||||
|
Loading…
Reference in New Issue
Block a user