Added support for layered formats
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details

+ Formats can include sub-formats of previously defined formats
+ This search is done post config building so any format in the global config can be used
+ Clippy + fmt
main
Joey Hines 2022-06-19 14:08:37 -06:00
parent f2cec4c89e
commit 3274386ccf
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
5 changed files with 147 additions and 35 deletions

View File

@ -37,6 +37,8 @@ Members:
* `Bytes`: Collection of bytes with no termination * `Bytes`: Collection of bytes with no termination
* `max_len`: max number of bytes * `max_len`: max number of bytes
* `endianess`: byte ordering of the field * `endianess`: byte ordering of the field
* `Format`: Allows formats to include other formats
* `format_name`: Format definition to use
#### Example Config #### Example Config
[Example](./formats/example.toml) [Example](./formats/example.toml)

View File

@ -48,4 +48,20 @@ field_type = {type = "String", endianness = "BigEndian", max_len = 55}
# byte field # byte field
[[formats.fields]] [[formats.fields]]
name = "bytes field" name = "bytes field"
field_type = {type = "Bytes", endianness = "BigEndian", max_len = 55} field_type = {type = "Bytes", endianness = "BigEndian", max_len = 5}
# Example of layer formats
[[formats]]
name = "example_layer"
bit_flip = false
# Example sub-format
[[formats.fields]]
name = "example sub-format"
field_type = {type = "Format", format_name = "example"}
# UInt after subfield
[[formats.fields]]
name = "uint field"
print_type = {print = "Base", base=16}
field_type = {type = "UInt", bit_width = 16, endianness = "LittleEndian"}

View File

@ -8,6 +8,7 @@ use serde::Deserialize;
use crate::byte_stream::{bit_mask, ByteStream, ByteStreamError}; use crate::byte_stream::{bit_mask, ByteStream, ByteStreamError};
use crate::formatter::printers::PrintType; use crate::formatter::printers::PrintType;
use crate::FormatConfig;
pub trait ByteOrderOperations: ByteOrder { pub trait ByteOrderOperations: ByteOrder {
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8>; fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8>;
@ -109,6 +110,7 @@ pub enum FormatError {
#[allow(dead_code)] #[allow(dead_code)]
NotSupported, NotSupported,
StringParseError(FromUtf8Error), StringParseError(FromUtf8Error),
FormatNotFound(String),
} }
impl From<ByteStreamError> for FormatError { impl From<ByteStreamError> for FormatError {
@ -129,6 +131,9 @@ impl Display for FormatError {
FormatError::ByteSteamError(e) => writeln!(f, "Byte stream error: {}", e), FormatError::ByteSteamError(e) => writeln!(f, "Byte stream error: {}", e),
FormatError::NotSupported => write!(f, "Field type not supported"), FormatError::NotSupported => write!(f, "Field type not supported"),
FormatError::StringParseError(e) => write!(f, "String parse error: {}", e), FormatError::StringParseError(e) => write!(f, "String parse error: {}", e),
FormatError::FormatNotFound(format) => {
write!(f, "Unable to find sub-format: {}", format)
}
} }
} }
} }
@ -160,6 +165,8 @@ pub enum FieldType {
max_len: usize, max_len: usize,
endianness: Endianness, endianness: Endianness,
}, },
/// Insert a predefine format here
Format { format_name: String },
} }
#[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq, Copy)] #[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq, Copy)]
@ -298,6 +305,7 @@ impl Field {
&self, &self,
byte_stream: &mut ByteStream, byte_stream: &mut ByteStream,
bit_ndx: usize, bit_ndx: usize,
config: &FormatConfig,
) -> Result<(String, usize), FormatError> { ) -> Result<(String, usize), FormatError> {
let global_bit_flip = byte_stream.get_reverse_bits(); let global_bit_flip = byte_stream.get_reverse_bits();
@ -305,16 +313,16 @@ impl Field {
byte_stream.set_reverse_bits(bit_flip); byte_stream.set_reverse_bits(bit_flip);
} }
let fmt = match self.field_type { let fmt = match &self.field_type {
FieldType::UInt { FieldType::UInt {
bit_width, bit_width,
endianness, endianness,
} => match endianness { } => match endianness {
Endianness::LittleEndian => { Endianness::LittleEndian => {
self.format_uint::<LittleEndian>(byte_stream, bit_ndx, bit_width) self.format_uint::<LittleEndian>(byte_stream, bit_ndx, *bit_width)
} }
Endianness::BigEndian => { Endianness::BigEndian => {
self.format_uint::<BigEndian>(byte_stream, bit_ndx, bit_width) self.format_uint::<BigEndian>(byte_stream, bit_ndx, *bit_width)
} }
}, },
FieldType::Int { FieldType::Int {
@ -322,10 +330,10 @@ impl Field {
endianness, endianness,
} => match endianness { } => match endianness {
Endianness::LittleEndian => { Endianness::LittleEndian => {
self.format_int::<LittleEndian>(byte_stream, bit_ndx, bit_width) self.format_int::<LittleEndian>(byte_stream, bit_ndx, *bit_width)
} }
Endianness::BigEndian => { Endianness::BigEndian => {
self.format_int::<BigEndian>(byte_stream, bit_ndx, bit_width) self.format_int::<BigEndian>(byte_stream, bit_ndx, *bit_width)
} }
}, },
FieldType::Float { endianness } => match endianness { FieldType::Float { endianness } => match endianness {
@ -343,10 +351,10 @@ impl Field {
endianness, endianness,
} => match endianness { } => match endianness {
Endianness::LittleEndian => { Endianness::LittleEndian => {
self.format_string::<LittleEndian>(byte_stream, bit_ndx, max_len) self.format_string::<LittleEndian>(byte_stream, bit_ndx, *max_len)
} }
Endianness::BigEndian => { Endianness::BigEndian => {
self.format_string::<LittleEndian>(byte_stream, bit_ndx, max_len) self.format_string::<LittleEndian>(byte_stream, bit_ndx, *max_len)
} }
}, },
FieldType::Bytes { FieldType::Bytes {
@ -354,12 +362,22 @@ impl Field {
endianness, endianness,
} => match endianness { } => match endianness {
Endianness::LittleEndian => { Endianness::LittleEndian => {
self.format_bytes::<LittleEndian>(byte_stream, bit_ndx, max_len) self.format_bytes::<LittleEndian>(byte_stream, bit_ndx, *max_len)
} }
Endianness::BigEndian => { Endianness::BigEndian => {
self.format_bytes::<LittleEndian>(byte_stream, bit_ndx, max_len) self.format_bytes::<LittleEndian>(byte_stream, bit_ndx, *max_len)
} }
}, },
FieldType::Format { format_name } => {
let format_config = config.get_format(format_name).unwrap();
let (mut format_str, post_bit_ndx) = format_config
.format_byte_stream(byte_stream, bit_ndx, config)
.map_err(|_| FormatError::FormatNotFound(format_name.to_string()))?;
format_str.insert(0, '\n');
Ok((format_str, post_bit_ndx - bit_ndx))
}
}?; }?;
byte_stream.set_reverse_bits(global_bit_flip); byte_stream.set_reverse_bits(global_bit_flip);
@ -379,15 +397,18 @@ pub struct Format {
} }
impl Format { impl Format {
pub fn format_data(&self, data: &[u8]) -> Result<String, FormatError> { pub fn format_byte_stream(
&self,
byte_stream: &mut ByteStream,
mut bit_ndx: usize,
config: &FormatConfig,
) -> Result<(String, usize), FormatError> {
let mut format_str = String::new(); let mut format_str = String::new();
let mut byte_stream = ByteStream::from(data); let old_reverse = byte_stream.get_reverse_bits();
let mut bit_ndx: usize = 0;
byte_stream.set_reverse_bits(self.bit_flip); byte_stream.set_reverse_bits(self.bit_flip);
for field in &self.fields { for field in &self.fields {
match field.format_data(&mut byte_stream, bit_ndx) { match field.format_data(byte_stream, bit_ndx, config) {
Ok((data_str, bit_width)) => { Ok((data_str, bit_width)) => {
bit_ndx += bit_width; bit_ndx += bit_width;
writeln!(format_str, "{}: {}", field.name, data_str).unwrap(); writeln!(format_str, "{}: {}", field.name, data_str).unwrap();
@ -402,6 +423,16 @@ impl Format {
} }
} }
byte_stream.set_reverse_bits(old_reverse);
Ok((format_str, bit_ndx))
}
pub fn format_data(&self, data: &[u8], config: &FormatConfig) -> Result<String, FormatError> {
let mut byte_stream = ByteStream::from(data);
let (format_str, _) = self.format_byte_stream(&mut byte_stream, 0, config)?;
Ok(format_str) Ok(format_str)
} }
} }
@ -410,6 +441,7 @@ impl Format {
mod tests { mod tests {
use crate::byte_stream::ByteStream; use crate::byte_stream::ByteStream;
use crate::formatter::format::{Endianness, Field, FieldType}; use crate::formatter::format::{Endianness, Field, FieldType};
use crate::{Format, FormatConfig};
#[test] #[test]
fn test_format_int_4_bits() { fn test_format_int_4_bits() {
@ -429,8 +461,12 @@ mod tests {
byte_vec.push((-i) as u8); byte_vec.push((-i) as u8);
let mut byte_stream = ByteStream::from(byte_vec); let mut byte_stream = ByteStream::from(byte_vec);
let (pos_output, width1) = field.format_data(&mut byte_stream, 4).unwrap(); let (pos_output, width1) = field
let (neg_output, width2) = field.format_data(&mut byte_stream, 12).unwrap(); .format_data(&mut byte_stream, 4, &FormatConfig::default())
.unwrap();
let (neg_output, width2) = field
.format_data(&mut byte_stream, 12, &FormatConfig::default())
.unwrap();
assert_eq!(width1, 4); assert_eq!(width1, 4);
assert_eq!(width2, 4); assert_eq!(width2, 4);
@ -452,7 +488,9 @@ mod tests {
print_type: Default::default(), print_type: Default::default(),
}; };
let mut byte_stream = ByteStream::from(vec![0x1B]); let mut byte_stream = ByteStream::from(vec![0x1B]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 5); assert_eq!(width, 5);
assert_eq!(output, "-5") assert_eq!(output, "-5")
@ -471,7 +509,9 @@ mod tests {
}; };
let mut byte_stream = ByteStream::from(vec![0xFC, 0xA5]); let mut byte_stream = ByteStream::from(vec![0xFC, 0xA5]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 16); assert_eq!(width, 16);
assert_eq!(output, "-23044") assert_eq!(output, "-23044")
@ -490,7 +530,9 @@ mod tests {
}; };
let mut byte_stream = ByteStream::from(vec![0x0C, 0xF5, 0xA0]); let mut byte_stream = ByteStream::from(vec![0x0C, 0xF5, 0xA0]);
let (output, width) = field.format_data(&mut byte_stream, 4).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 4, &FormatConfig::default())
.unwrap();
assert_eq!(width, 16); assert_eq!(width, 16);
assert_eq!(output, "-23044") assert_eq!(output, "-23044")
@ -508,7 +550,9 @@ mod tests {
}; };
let mut byte_stream = ByteStream::from(b"\x52\x58\xd2\xc3".to_vec()); let mut byte_stream = ByteStream::from(b"\x52\x58\xd2\xc3".to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 32); assert_eq!(width, 32);
assert_eq!(output, "-420.69") assert_eq!(output, "-420.69")
@ -526,7 +570,9 @@ mod tests {
}; };
let mut byte_stream = ByteStream::from(b"\xD7\xA3\x70\x3D\x0A\x4B\x7A\xC0".to_vec()); let mut byte_stream = ByteStream::from(b"\xD7\xA3\x70\x3D\x0A\x4B\x7A\xC0".to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 64); assert_eq!(width, 64);
assert_eq!(output, "-420.69") assert_eq!(output, "-420.69")
@ -545,7 +591,9 @@ mod tests {
let mut byte_stream = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec()); let mut byte_stream = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec());
assert!(field.format_data(&mut byte_stream, 0).is_err()) assert!(field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.is_err())
} }
#[test] #[test]
@ -566,7 +614,9 @@ mod tests {
let mut byte_stream = ByteStream::from(bytes); let mut byte_stream = ByteStream::from(bytes);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, (test_string.len() + 1) * 8); //extra byte to account for the '\0' char assert_eq!(width, (test_string.len() + 1) * 8); //extra byte to account for the '\0' char
assert_eq!(output, test_string) assert_eq!(output, test_string)
@ -586,7 +636,9 @@ mod tests {
let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD, 0xBE, 0xEF]); let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 16); assert_eq!(width, 16);
assert_eq!(output, "[222, 173]") assert_eq!(output, "[222, 173]")
@ -606,7 +658,9 @@ mod tests {
let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD]); let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD]);
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 16); assert_eq!(width, 16);
assert_eq!(output, "[222, 173]") assert_eq!(output, "[222, 173]")
@ -627,7 +681,9 @@ mod tests {
let src_str = b"Test\x00abcdefg"; let src_str = b"Test\x00abcdefg";
let mut byte_stream = ByteStream::from(src_str.to_vec()); let mut byte_stream = ByteStream::from(src_str.to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 40); assert_eq!(width, 40);
assert_eq!(output, "Test") assert_eq!(output, "Test")
@ -648,9 +704,47 @@ mod tests {
let src_str = b"Test\x00abcdefg"; let src_str = b"Test\x00abcdefg";
let mut byte_stream = ByteStream::from(src_str.to_vec()); let mut byte_stream = ByteStream::from(src_str.to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); let (output, width) = field
.format_data(&mut byte_stream, 0, &FormatConfig::default())
.unwrap();
assert_eq!(width, 40); assert_eq!(width, 40);
assert_eq!(output, "Test") assert_eq!(output, "Test")
} }
#[test]
fn test_sub_field() {
let field = Field {
name: "test".to_string(),
field_type: FieldType::Format {
format_name: "sub_format".to_string(),
},
print_type: Default::default(),
bit_flip: None,
};
let config = FormatConfig {
formats: vec![Format {
name: "sub_format".to_string(),
bit_flip: false,
fields: vec![Field {
name: "test".to_string(),
field_type: FieldType::String {
max_len: 25,
endianness: Endianness::LittleEndian,
},
print_type: Default::default(),
bit_flip: None,
}],
}],
};
let src_str = b"Test\x00abcdefg";
let mut byte_stream = ByteStream::from(src_str.to_vec());
let (output, width) = field.format_data(&mut byte_stream, 0, &config).unwrap();
assert_eq!(width, 40);
assert_eq!(output, "test: Test\n")
}
} }

View File

@ -286,7 +286,7 @@ mod test {
let format = get_ccsds_format(); let format = get_ccsds_format();
let data = ccsds_packet.to_bytes(); let data = ccsds_packet.to_bytes();
let output = format.format_data(&data).unwrap(); let output = format.format_data(&data, &FormatConfig::default()).unwrap();
assert_eq!(ccsds_packet.print(), output) assert_eq!(ccsds_packet.print(), output)
} }
@ -308,7 +308,7 @@ mod test {
}; };
let data = ccsds_packet.to_bytes(); let data = ccsds_packet.to_bytes();
let output = format.format_data(&data).unwrap(); let output = format.format_data(&data, &FormatConfig::default()).unwrap();
assert_eq!(ccsds_packet.print(), output) assert_eq!(ccsds_packet.print(), output)
} }

View File

@ -55,7 +55,7 @@ pub struct Args {
data: Vec<String>, data: Vec<String>,
} }
fn init() -> Result<(Vec<u8>, Format), FormatyError> { fn init() -> Result<(Vec<u8>, Format, FormatConfig), FormatyError> {
let args: Args = Args::from_args(); let args: Args = Args::from_args();
let config = FormatConfig::new(&args.config, &args.global_config)?; let config = FormatConfig::new(&args.config, &args.global_config)?;
@ -70,19 +70,19 @@ fn init() -> Result<(Vec<u8>, Format), FormatyError> {
args.input_type.parse_arg_input(args.data, args.base)? args.input_type.parse_arg_input(args.data, args.base)?
}; };
Ok((data, format)) Ok((data, format, config))
} }
fn main() { fn main() {
let (data, format) = match init() { let (data, format, config) = match init() {
Ok((data, format)) => (data, format), Ok((data, format, config)) => (data, format, config),
Err(e) => { Err(e) => {
println!("Error initializing: {}", e); println!("Error initializing: {}", e);
return; return;
} }
}; };
match format.format_data(&data) { match format.format_data(&data, &config) {
Ok(data) => println!("{}", data), Ok(data) => println!("{}", data),
Err(_) => print!("Unable to parse data"), Err(_) => print!("Unable to parse data"),
} }