use alloc::vec::Vec;
use crate::DataType;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TypedOutput {
data_type: DataType,
width: usize,
content: Vec<u8>,
string_lens: Vec<u32>,
}
impl TypedOutput {
pub(crate) fn new(data_type: DataType) -> Self {
Self {
data_type,
width: usize::from(matches!(data_type, DataType::Serial | DataType::String)),
content: Vec::new(),
string_lens: Vec::new(),
}
}
pub(crate) fn append(&mut self, width: usize, bytes: &[u8], lens: &[u32]) -> Result<(), ()> {
if self.content.is_empty() && self.string_lens.is_empty() && self.width == 0 {
self.width = width;
}
if width != self.width && !bytes.is_empty() {
return Err(());
}
self.content.extend_from_slice(bytes);
self.string_lens.extend_from_slice(lens);
Ok(())
}
pub(crate) fn has_no_content(&self) -> bool {
self.content.is_empty() && self.string_lens.is_empty()
}
pub(crate) fn adopt(&mut self, width: usize, bytes: Vec<u8>, lens: Vec<u32>) -> Result<(), ()> {
if self.width == 0 {
self.width = width;
}
if width != self.width && !bytes.is_empty() {
return Err(());
}
self.content = bytes;
self.string_lens = lens;
Ok(())
}
#[must_use]
pub const fn data_type(&self) -> DataType {
self.data_type
}
#[must_use]
pub const fn width(&self) -> usize {
self.width
}
#[must_use]
pub fn content(&self) -> &[u8] {
&self.content
}
#[must_use]
pub fn string_lens(&self) -> &[u32] {
&self.string_lens
}
#[must_use]
pub fn len(&self) -> usize {
match self.data_type {
DataType::String => self.string_lens.len(),
_ if self.width == 0 => 0,
_ => self.content.len() / self.width,
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn into_content(self) -> Vec<u8> {
self.content
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn widths_are_fixed_by_the_first_chunk() {
let mut numbers = TypedOutput::new(DataType::Numeric);
assert_eq!(
(numbers.width(), numbers.len(), numbers.is_empty()),
(0, 0, true)
);
numbers.append(4, &[1, 0, 0, 0], &[]).unwrap();
assert_eq!(numbers.append(2, &[1, 0], &[]), Err(()));
numbers.append(2, &[], &[]).unwrap();
assert_eq!((numbers.width(), numbers.len()), (4, 1));
let mut strings = TypedOutput::new(DataType::String);
strings.append(1, b"abc", &[1, 2]).unwrap();
assert_eq!((strings.len(), strings.string_lens()), (2, &[1u32, 2][..]));
assert_eq!(strings.clone().into_content(), b"abc");
assert_eq!(strings.content(), b"abc");
}
#[test]
fn adopting_takes_buffers_with_append_width_rules() {
let mut strings = TypedOutput::new(DataType::String);
assert!(strings.has_no_content());
strings
.adopt(1, b"abc".to_vec(), alloc::vec![1, 2])
.unwrap();
assert!(!strings.has_no_content());
assert_eq!((strings.width(), strings.content()), (1, &b"abc"[..]));
assert_eq!(strings.string_lens(), &[1u32, 2]);
let mut numbers = TypedOutput::new(DataType::Numeric);
numbers.append(4, &[], &[]).unwrap();
assert!(numbers.has_no_content());
assert_eq!(numbers.adopt(2, alloc::vec![1, 0], alloc::vec![]), Err(()));
assert!(
numbers.has_no_content(),
"a refused adoption changes nothing"
);
numbers
.adopt(4, alloc::vec![1, 0, 0, 0], alloc::vec![])
.unwrap();
assert_eq!((numbers.width(), numbers.len()), (4, 1));
}
}