formaty/src/formatter/format.rs

311 lines
9.6 KiB
Rust

use crate::byte_stream::{ByteStream, bit_mask, ByteStreamError};
use serde::Deserialize;
use std::fmt::{Write, Display, Formatter};
use num_bigint::{BigUint, BigInt};
use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt};
use std::string::FromUtf8Error;
use crate::formatter::printers::print_bytes_as_array;
#[derive(Debug, Clone)]
pub enum FormatError {
ByteSteamError(ByteStreamError),
#[allow(dead_code)]
NotSupported,
StringParseError(FromUtf8Error),
}
impl From<ByteStreamError> for FormatError {
fn from(e: ByteStreamError) -> Self {
FormatError::ByteSteamError(e)
}
}
impl From<FromUtf8Error> for FormatError {
fn from(e: FromUtf8Error) -> Self {
FormatError::StringParseError(e)
}
}
impl Display for FormatError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FormatError::ByteSteamError(e) => writeln!(f, "Byte stream error: {}", e),
FormatError::NotSupported => write!(f, "Field type not supported"),
FormatError::StringParseError(e) => write!(f, "String parse error: {}", e)
}
}
}
#[derive(Debug, Deserialize, Clone)]
#[serde(tag = "type")]
pub enum FieldType {
/// Unsigned Int
UInt { bit_width: usize },
/// Unsigned Int
Int { bit_width: usize },
/// Single Precession Float
Float,
/// Double Precession Float
Double,
/// Null Terminated String Field
String { max_len: usize },
/// Fixed Byte Length Field
Bytes { max_len: usize },
}
#[derive(Debug, Deserialize, Clone)]
pub struct Field {
/// Field Name
pub name: String,
/// Field Type
pub field_type: FieldType,
}
impl Field {
fn format_int(byte_stream: &ByteStream, bit_ndx: usize, bit_width: usize) -> Result<(String, usize), FormatError> {
let mut bytes = byte_stream.get_bytes(bit_ndx, bit_width)?;
if let Some(last_byte) = bytes.last_mut() {
let last_bit = ((bit_width - 1) % 8) as u8;
let sign_bit = (*last_byte >> last_bit) & 0x1 == 1;
if sign_bit {
// Sign extend
*last_byte |= !bit_mask(last_bit + 1)
}
let big_int = BigInt::from_signed_bytes_le(&bytes);
Ok((big_int.to_string(), bit_width))
}
else {
Err(ByteStreamError::OutOfRange.into())
}
}
fn format_uint(byte_stream: &ByteStream, bit_ndx: usize, bit_width: usize) -> Result<(String, usize), FormatError> {
let bytes = byte_stream.get_bytes(bit_ndx, bit_width)?;
let big_int = BigUint::from_bytes_le(&bytes);
Ok((big_int.to_string(), bit_width))
}
fn format_float(byte_stream: &ByteStream, bit_ndx: usize) -> Result<(String, usize), FormatError> {
let bytes = byte_stream.get_bytes(bit_ndx, 32)?;
let mut cursor = Cursor::new(bytes);
Ok((cursor.read_f32::<LittleEndian>().unwrap().to_string(), 4))
}
fn format_double(byte_stream: &ByteStream, bit_ndx: usize) -> Result<(String, usize), FormatError> {
let bytes = byte_stream.get_bytes(bit_ndx, 64)?;
let mut cursor = Cursor::new(bytes);
Ok((cursor.read_f64::<LittleEndian>().unwrap().to_string(), 4))
}
fn format_string(byte_stream: &ByteStream, mut bit_ndx: usize, max_byte_len: usize) -> Result<(String, usize), FormatError> {
let mut string_bytes = Vec::new();
for _ in 0..max_byte_len {
let byte = byte_stream.get_bytes(bit_ndx, 8)?[0];
if byte == 0 {
break;
}
string_bytes.push(byte);
bit_ndx += 8;
}
let byte_count = string_bytes.len();
Ok((String::from_utf8(string_bytes)?, byte_count))
}
fn format_bytes(byte_stream: &ByteStream, bit_ndx: usize, max_byte_len: usize) -> Result<(String, usize), FormatError> {
let data_remaining = byte_stream.len() - bit_ndx/8;
let width = if max_byte_len < data_remaining {
max_byte_len*8
}
else {
data_remaining*8
};
let data = byte_stream.get_bytes(bit_ndx, width)?;
Ok((print_bytes_as_array(&data), width))
}
fn format_data(&self, byte_stream: &ByteStream, bit_ndx: usize) -> Result<(String, usize), FormatError> {
match self.field_type {
FieldType::UInt { bit_width } => Self::format_uint(byte_stream, bit_ndx, bit_width),
FieldType::Int { bit_width } => Self::format_int(byte_stream, bit_ndx, bit_width),
FieldType::Float => Self::format_float(byte_stream, bit_ndx),
FieldType::Double => Self::format_double(byte_stream, bit_ndx),
FieldType::String {max_len} => Self::format_string(byte_stream, bit_ndx, max_len),
FieldType::Bytes {max_len} => Self::format_bytes(byte_stream, bit_ndx, max_len)
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct Format {
/// Format Name
pub name: String,
/// Elements of the format
pub fields: Vec<Field>,
}
impl Format {
pub fn format_data(&self, data: &[u8]) -> Result<String, FormatError> {
let mut format_str = String::new();
let byte_stream = ByteStream::from(data);
let mut bit_ndx: usize = 0;
for field in &self.fields {
match field.format_data(&byte_stream, bit_ndx) {
Ok((data_str, bit_width)) => {
bit_ndx += bit_width;
writeln!(format_str, "{}: {}", field.name, data_str).unwrap();
}
Err(e) => {
println!("Error formatting field: \"{}\": {}", field.name, e);
return Err(e);
}
}
}
Ok(format_str)
}
}
#[cfg(test)]
mod tests {
use crate::formatter::format::{FieldType, Field};
use crate::byte_stream::ByteStream;
#[test]
fn test_format_int_4_bits() {
let field = Field {field_type: FieldType::Int {bit_width: 4,}, name: "test".to_string()};
for i in 0i8..7i8 {
let mut byte_vec = Vec::new();
byte_vec.push(i as u8);
byte_vec.push((-i) as u8);
let byte_steam = ByteStream::from(byte_vec);
let (pos_output, _) = field.format_data(&byte_steam, 0).unwrap();
let (neg_output, _) = field.format_data(&byte_steam, 8).unwrap();
assert_eq!(pos_output, i.to_string());
assert_eq!(neg_output, (-i).to_string());
}
}
#[test]
fn test_format_int_5_bits() {
let field = Field {field_type: FieldType::Int {bit_width: 5,}, name: "test".to_string()};
let byte_steam = ByteStream::from(vec![0x1B]);
let (output, _) = field.format_data(&byte_steam, 0).unwrap();
assert_eq!(output, "-5")
}
#[test]
fn test_format_int_16_bits() {
let field = Field {field_type: FieldType::Int {bit_width: 16,}, name: "test".to_string()};
let byte_steam = ByteStream::from(vec![0xFC, 0xA5]);
let (output, _) = field.format_data(&byte_steam, 0).unwrap();
assert_eq!(output, "-23044")
}
#[test]
fn test_format_int_16_bits_not_aligned() {
let field = Field {field_type: FieldType::Int {bit_width: 16,}, name: "test".to_string()};
let byte_steam = ByteStream::from(vec![0xC0, 0x5F, 0x0A]);
let (output, _) = field.format_data(&byte_steam, 4).unwrap();
assert_eq!(output, "-23044")
}
#[test]
fn test_format_float() {
let field = Field {field_type: FieldType::Float, name: "test".to_string()};
let byte_steam = ByteStream::from(b"\x52\x58\xd2\xc3".to_vec());
let (output, _) = field.format_data(&byte_steam, 0).unwrap();
assert_eq!(output, "-420.69")
}
#[test]
fn test_format_double() {
let field = Field {field_type: FieldType::Double, name: "test".to_string()};
let byte_steam = ByteStream::from(b"\xD7\xA3\x70\x3D\x0A\x4B\x7A\xC0".to_vec());
let (output, _) = field.format_data(&byte_steam, 0).unwrap();
assert_eq!(output, "-420.69")
}
#[test]
fn test_format_float_err() {
let field = Field {field_type: FieldType::Double, name: "test".to_string()};
let byte_steam = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec());
assert!(field.format_data(&byte_steam, 0).is_err())
}
#[test]
fn test_format_string() {
let field = Field {field_type: FieldType::String {max_len: 16}, name: "test".to_string()};
let byte_steam = ByteStream::from(b"Hello World!\0".to_vec());
let (output, _) = field.format_data(&byte_steam, 0).unwrap();
assert_eq!(output, "Hello World!".to_string())
}
#[test]
fn test_format_bytes() {
let field = Field {field_type: FieldType::Bytes {max_len: 2}, name: "test".to_string()};
let byte_steam = ByteStream::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
let (output, _) = field.format_data(&byte_steam, 0).unwrap();
assert_eq!(output, "[222, 173]")
}
#[test]
fn test_format_bytes_max_len_bigger_than_data() {
let field = Field {field_type: FieldType::Bytes {max_len: 64}, name: "test".to_string()};
let byte_steam = ByteStream::from(vec![0xDE, 0xAD]);
let (output, _) = field.format_data(&byte_steam, 0).unwrap();
assert_eq!(output, "[222, 173]")
}
#[test]
fn test_ccsds_apid_issue() {
let field = Field {field_type: FieldType::UInt {bit_width: 11}, name: "test".to_string()};
let byte_steam = ByteStream::from(vec![0xe0, 0xa1, 0xc0, 0x00, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05]);
let (output, _) = field.format_data(&byte_steam, 5).unwrap();
assert_eq!(output, "1295")
}
}