From 70a3c260e6ac360e6e4970888c25ac09986fdc99 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sun, 10 Apr 2022 10:42:05 -0600 Subject: [PATCH] Improved docs + a bunch of fixes + Better error handling + Line numbers in toml errors should now match the file + Errors during parsing show where in the bit stream they failed + Fixed some issues with string, float, and double parsing reporting the wrong bit width + Updated docs and added the example.toml config + clipppy + fmt --- README.md | 36 ++++++ formats/example.toml | 42 +++++++ src/byte_stream/mod.rs | 8 +- src/formatter/format.rs | 242 ++++++++++++++++---------------------- src/formatter/mod.rs | 44 ++++--- src/formatter/printers.rs | 75 ++++++++++++ src/main.rs | 25 ++-- src/parser/mod.rs | 6 +- 8 files changed, 309 insertions(+), 169 deletions(-) create mode 100644 formats/example.toml diff --git a/README.md b/README.md index 1699927..4bab36e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,42 @@ A simple configurable binary data parser. Data structures are described using TO All formats in [formats](./formats) are included in the `formaty` binary. See [formats.md](./formats/formats.md) for more info. +### Configuration + +#### Format +A format is a collection of fields that describe the structure of data, so it can be parsed + +Members: +* `name` - name of the format, used as the argument when selecting the format to parse the data as +* `bit_flip` - should individual bytes have their bits flipped within, individual fields can override this +* `fields` - 1 or more fields to describe the structure of the data + +#### Field +A field is a collection of bits that make up some sort of data. Formaty supports `Int`/`Uint` of arbitrary size, +`Float`, `Double`, `Strings` and `Bytes`. This data may not be byte aligned. + +Members: +* `name` - the name of the field, used while displaying data +* `field_type` - the type of the field and associated data +* `print_type` - how to print the data, if not provided it uses the default print for a type +* `bit_flip` - overrides the global bit flip field, defaults to false if not provided + +#### Field Types +* `UInt`/`Int`: standard integer types. Unsigned or signed respectively. Can be 1 or more bits. + * `bit_width`: number of bits composing the integer + * `endianess`: byte ordering of the field +* `Float`/`Double`: Standard float point types of 32-bit or 64-bit width + * `endianess`: byte ordering of the field +* `String`: Collection of bytes ended by a null character '\0' + * `max_len`: max length of the data + * `endianess`: byte ordering of the field +* `Bytes`: Collection of bytes with no termination + * `max_len`: max number of bytes + * `endianess`: byte ordering of the field + +#### Example Config +[Example](./formats/example.toml) + ## Example ```bash ./formaty ccsds "[0xe0, 0xa1, 0xc0, 0x00, 0x00, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05]" diff --git a/formats/example.toml b/formats/example.toml new file mode 100644 index 0000000..6a4e2bc --- /dev/null +++ b/formats/example.toml @@ -0,0 +1,42 @@ +# Example fields to show off configuration +# Example data to use "[0x00, 0x55, 0xff, 0x43, 0xd2 0x99 0x90, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x57, 0x6f, 0x72, 0x6c, 0x64]" + +[[formats]] +# Format name +name = "example" +# Global bit flip +bit_flip = false + +# signed int field, big endian +[[formats.fields]] +name = "int field" +field_type = {type = "Int", bit_width = 16, endianness = "BigEndian"} + +# unsigned int field, little endian. also is printed as hex +[[formats.fields]] +name = "uint field" +print_type = {print = "Base", base=16} +field_type = {type = "UInt", bit_width = 4, endianness = "LittleEndian"} + +# unsigned int field, little endian. also is printed as hex +[[formats.fields]] +name = "uint field" +print_type = {print = "Base", base=16} +field_type = {type = "UInt", bit_width = 4, endianness = "LittleEndian"} + +# single precesion float field. +[[formats.fields]] +name = "float field" +bit_flip = false +field_type = {type = "Float", endianness = "BigEndian"} + +# string field. Printed as a byte array +[[formats.fields]] +name = "string field" +print_type = {print = "ByteArray"} +field_type = {type = "String", endianness = "BigEndian", max_len = 55} + +# byte field +[[formats.fields]] +name = "bytes field" +field_type = {type = "Bytes", endianness = "BigEndian", max_len = 55} \ No newline at end of file diff --git a/src/byte_stream/mod.rs b/src/byte_stream/mod.rs index 2f08f55..5dd37af 100644 --- a/src/byte_stream/mod.rs +++ b/src/byte_stream/mod.rs @@ -1,4 +1,4 @@ -use crate::formatter::format::LastByte; +use crate::formatter::format::ByteOrderOperations; use std::fmt::{Display, Formatter}; #[derive(Clone, Debug)] @@ -43,7 +43,7 @@ impl ByteStream { self.reverse_bits } - pub fn get_bytes( + pub fn get_bytes( &self, bit_ndx: usize, bit_count: usize, @@ -80,6 +80,10 @@ impl ByteStream { byte_stream.pop(); } + if byte_count > byte_stream.len() { + T::pad_bytes(&mut byte_stream, bytes_needed) + } + if bits_in_last_byte != 0 { *byte_stream.last_mut().unwrap() &= bit_mask(bits_in_last_byte); } diff --git a/src/formatter/format.rs b/src/formatter/format.rs index de9d858..b6a8b98 100644 --- a/src/formatter/format.rs +++ b/src/formatter/format.rs @@ -1,13 +1,15 @@ -use crate::byte_stream::{bit_mask, ByteStream, ByteStreamError}; -use crate::formatter::printers::{base_notation, print_bytes_as_array}; -use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; -use num_bigint::{BigInt, BigUint}; -use serde::Deserialize; use std::fmt::{Display, Formatter, Write}; use std::io::Cursor; use std::string::FromUtf8Error; -pub trait LastByte: ByteOrder { +use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt}; +use num_bigint::{BigInt, BigUint}; +use serde::Deserialize; + +use crate::byte_stream::{bit_mask, ByteStream, ByteStreamError}; +use crate::formatter::printers::PrintType; + +pub trait ByteOrderOperations: ByteOrder { fn last_byte(buf: &mut Vec) -> Option<&mut u8>; fn big_int(buf: &[u8]) -> BigInt; @@ -17,9 +19,11 @@ pub trait LastByte: ByteOrder { fn big_u_int_to_bytes(big_int: BigUint) -> Vec; fn big_int_to_bytes(big_int: BigInt) -> Vec; + + fn pad_bytes(buf: &mut Vec, size: usize); } -impl LastByte for BigEndian { +impl ByteOrderOperations for BigEndian { fn last_byte(buf: &mut Vec) -> Option<&mut u8> { buf.first_mut() } @@ -39,9 +43,18 @@ impl LastByte for BigEndian { fn big_int_to_bytes(big_int: BigInt) -> Vec { big_int.to_signed_bytes_be() } + + fn pad_bytes(buf: &mut Vec, size: usize) { + if size <= buf.len() { + return; + } + + let pad_to = size - buf.len(); + buf.splice(0..0, vec![0_u8; pad_to].iter().cloned()); + } } -impl LastByte for LittleEndian { +impl ByteOrderOperations for LittleEndian { fn last_byte(buf: &mut Vec) -> Option<&mut u8> { buf.last_mut() } @@ -61,6 +74,17 @@ impl LastByte for LittleEndian { fn big_int_to_bytes(big_int: BigInt) -> Vec { big_int.to_signed_bytes_le() } + + fn pad_bytes(buf: &mut Vec, size: usize) { + if size <= buf.len() { + return; + } + + let pad_to = size - buf.len(); + + let mut pad = vec![0_u8; pad_to]; + buf.append(&mut pad); + } } #[derive(Debug, Clone)] @@ -122,75 +146,6 @@ pub enum FieldType { }, } -#[derive(Debug, Deserialize, Clone)] -#[serde(tag = "print")] -pub enum PrintType { - Base { base: u32 }, - ByteArray, - Default, -} - -impl Default for PrintType { - fn default() -> Self { - PrintType::Default - } -} - -impl PrintType { - pub fn print_big_int(&self, big_int: BigInt) -> String { - match self { - PrintType::Base { base: b } => { - format!("{}{}", base_notation(*b), big_int.to_str_radix(*b)) - } - PrintType::ByteArray => print_bytes_as_array(&T::big_int_to_bytes(big_int)), - PrintType::Default => big_int.to_string(), - } - } - - pub fn print_big_u_int(&self, big_int: BigUint) -> String { - match self { - PrintType::Base { base: b } => { - format!("{}{}", base_notation(*b), big_int.to_str_radix(*b)) - } - PrintType::ByteArray => print_bytes_as_array(&T::big_u_int_to_bytes(big_int)), - PrintType::Default => big_int.to_string(), - } - } - - pub fn print_float(&self, float: f32) -> String { - match self { - PrintType::ByteArray => { - let mut bytes = Vec::with_capacity(4); - bytes.write_f32::(float).unwrap(); - print_bytes_as_array(&bytes) - } - _ => float.to_string(), - } - } - - pub fn print_double(&self, double: f64) -> String { - match self { - PrintType::ByteArray => { - let mut bytes = Vec::with_capacity(8); - bytes.write_f64::(double).unwrap(); - print_bytes_as_array(&bytes) - } - _ => double.to_string(), - } - } - - pub fn print_bytes(&self, bytes: &[u8]) -> String { - print_bytes_as_array(bytes) - } - - pub fn print_string(&self, s: &str) -> String { - match self { - PrintType::ByteArray => print_bytes_as_array(s.as_bytes()), - _ => s.to_string(), - } - } -} - #[derive(Debug, Deserialize, Clone, PartialOrd, PartialEq, Copy)] pub enum Endianness { LittleEndian, @@ -211,7 +166,7 @@ pub struct Field { } impl Field { - fn format_int( + fn format_int( &self, byte_stream: &ByteStream, bit_ndx: usize, @@ -236,7 +191,7 @@ impl Field { } } - fn format_uint( + fn format_uint( &self, byte_stream: &ByteStream, bit_ndx: usize, @@ -248,7 +203,7 @@ impl Field { Ok((self.print_type.print_big_u_int::(big_int), bit_width)) } - fn format_float( + fn format_float( &self, byte_stream: &ByteStream, bit_ndx: usize, @@ -257,10 +212,13 @@ impl Field { let mut cursor = Cursor::new(bytes); let float = cursor.read_f32::().unwrap(); - Ok((self.print_type.print_float::(float), 4)) + Ok(( + self.print_type.print_float::(float), + std::mem::size_of::() * 8, + )) } - fn format_double( + fn format_double( &self, byte_stream: &ByteStream, bit_ndx: usize, @@ -269,35 +227,37 @@ impl Field { let mut cursor = Cursor::new(bytes); let double = cursor.read_f64::().unwrap(); - Ok((self.print_type.print_double::(double), 4)) + Ok(( + self.print_type.print_double::(double), + std::mem::size_of::() * 8, + )) } - fn format_string( + fn format_string( &self, byte_stream: &ByteStream, - mut bit_ndx: usize, + bit_ndx: usize, max_byte_len: usize, ) -> Result<(String, usize), FormatError> { let mut string_bytes = Vec::new(); + let mut bit_count = 0; for _ in 0..max_byte_len { - let byte = byte_stream.get_bytes::(bit_ndx, 8)?[0]; + let byte = byte_stream.get_bytes::(bit_ndx + bit_count, 8)?[0]; + bit_count += 8; if byte == 0 { break; } string_bytes.push(byte); - bit_ndx += 8; } - let byte_count = string_bytes.len(); - let s = String::from_utf8(string_bytes)?; - Ok((self.print_type.print_string(&s), byte_count)) + Ok((self.print_type.print_string(&s), bit_count)) } - fn format_bytes( + fn format_bytes( &self, byte_stream: &ByteStream, bit_ndx: usize, @@ -415,7 +375,10 @@ impl Format { writeln!(format_str, "{}: {}", field.name, data_str).unwrap(); } Err(e) => { - println!("Error formatting field: \"{}\": {}", field.name, e); + println!( + "Error formatting field (bit_ndx = {}): \"{}\": {}", + bit_ndx, field.name, e + ); return Err(e); } } @@ -439,7 +402,7 @@ mod tests { }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; for i in 0i8..7i8 { @@ -448,8 +411,11 @@ mod tests { byte_vec.push((-i) as u8); let mut byte_stream = ByteStream::from(byte_vec); - let (pos_output, _) = field.format_data(&mut byte_stream, 0).unwrap(); - let (neg_output, _) = field.format_data(&mut byte_stream, 8).unwrap(); + let (pos_output, width1) = field.format_data(&mut byte_stream, 0).unwrap(); + let (neg_output, width2) = field.format_data(&mut byte_stream, 8).unwrap(); + + assert_eq!(width1, 4); + assert_eq!(width2, 4); assert_eq!(pos_output, i.to_string()); assert_eq!(neg_output, (-i).to_string()); @@ -465,11 +431,12 @@ mod tests { }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; let mut byte_stream = ByteStream::from(vec![0x1B]); - let (output, _) = field.format_data(&mut byte_stream, 0).unwrap(); + let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); + assert_eq!(width, 5); assert_eq!(output, "-5") } @@ -482,12 +449,13 @@ mod tests { }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; let mut byte_stream = ByteStream::from(vec![0xFC, 0xA5]); - let (output, _) = field.format_data(&mut byte_stream, 0).unwrap(); + let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); + assert_eq!(width, 16); assert_eq!(output, "-23044") } @@ -500,12 +468,13 @@ mod tests { }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; let mut byte_stream = ByteStream::from(vec![0xC0, 0x5F, 0x0A]); - let (output, _) = field.format_data(&mut byte_stream, 4).unwrap(); + let (output, width) = field.format_data(&mut byte_stream, 4).unwrap(); + assert_eq!(width, 16); assert_eq!(output, "-23044") } @@ -517,12 +486,13 @@ mod tests { }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; let mut byte_stream = ByteStream::from(b"\x52\x58\xd2\xc3".to_vec()); - let (output, _) = field.format_data(&mut byte_stream, 0).unwrap(); + let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); + assert_eq!(width, 32); assert_eq!(output, "-420.69") } @@ -534,12 +504,13 @@ mod tests { }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; let mut byte_stream = ByteStream::from(b"\xD7\xA3\x70\x3D\x0A\x4B\x7A\xC0".to_vec()); - let (output, _) = field.format_data(&mut byte_stream, 0).unwrap(); + let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); + assert_eq!(width, 64); assert_eq!(output, "-420.69") } @@ -551,7 +522,7 @@ mod tests { }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; let mut byte_stream = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec()); @@ -561,70 +532,65 @@ mod tests { #[test] fn test_format_string() { + let test_string = "Hello World!"; let field = Field { - field_type: FieldType::String { max_len: 16, endianness: Endianness::LittleEndian }, + field_type: FieldType::String { + max_len: 16, + endianness: Endianness::LittleEndian, + }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; - let mut byte_stream = ByteStream::from(b"Hello World!\0".to_vec()); + let mut bytes = test_string.as_bytes().to_vec(); + bytes.push('\0' as u8); - let (output, _) = field.format_data(&mut byte_stream, 0).unwrap(); + let mut byte_stream = ByteStream::from(bytes); - assert_eq!(output, "Hello World!".to_string()) + let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); + + assert_eq!(width, (test_string.len() + 1) * 8); //extra byte to account for the '\0' char + assert_eq!(output, test_string) } #[test] fn test_format_bytes() { let field = Field { - field_type: FieldType::Bytes { max_len: 2, endianness: Endianness::LittleEndian }, + field_type: FieldType::Bytes { + max_len: 2, + endianness: Endianness::LittleEndian, + }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD, 0xBE, 0xEF]); - let (output, _) = field.format_data(&mut byte_stream, 0).unwrap(); + let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); + assert_eq!(width, 16); 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, endianness: Endianness::LittleEndian }, - name: "test".to_string(), - bit_flip: None, - print_type: Default::default() - }; - - let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD]); - - let (output, _) = field.format_data(&mut byte_stream, 0).unwrap(); - - assert_eq!(output, "[222, 173]") - } - - #[test] - fn test_ccsds_apid_issue() { - let field = Field { - field_type: FieldType::UInt { - bit_width: 11, + field_type: FieldType::Bytes { + max_len: 64, endianness: Endianness::LittleEndian, }, name: "test".to_string(), bit_flip: None, - print_type: Default::default() + print_type: Default::default(), }; - let mut byte_stream = ByteStream::from(vec![ - 0xe0, 0xa1, 0xc0, 0x00, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - ]); + let mut byte_stream = ByteStream::from(vec![0xDE, 0xAD]); - let (output, _) = field.format_data(&mut byte_stream, 5).unwrap(); + let (output, width) = field.format_data(&mut byte_stream, 0).unwrap(); - assert_eq!(output, "1295") + assert_eq!(width, 16); + assert_eq!(output, "[222, 173]") } } diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs index a24c9a3..427f66c 100644 --- a/src/formatter/mod.rs +++ b/src/formatter/mod.rs @@ -15,7 +15,10 @@ use std::str; #[allow(clippy::enum_variant_names)] pub enum FormatConfigError { IOError(std::io::Error), - TomlError(toml::de::Error), + TomlError { + file_name: String, + err: toml::de::Error, + }, Utf8Error(std::str::Utf8Error), } @@ -27,12 +30,6 @@ impl From for FormatConfigError { } } -impl From for FormatConfigError { - fn from(e: toml::de::Error) -> Self { - Self::TomlError(e) - } -} - impl From for FormatConfigError { fn from(e: std::str::Utf8Error) -> Self { Self::Utf8Error(e) @@ -43,7 +40,9 @@ impl Display for FormatConfigError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let err_msg = match self { FormatConfigError::IOError(e) => e.to_string(), - FormatConfigError::TomlError(e) => e.to_string(), + FormatConfigError::TomlError { file_name, err } => { + format!("Error parsing {}: {}", file_name, err) + } FormatConfigError::Utf8Error(e) => e.to_string(), }; @@ -56,19 +55,26 @@ impl Display for FormatConfigError { #[include = "*.toml"] struct BuiltInFormats; -#[derive(Debug, Deserialize, Clone)] +#[derive(Debug, Deserialize, Clone, Default)] pub struct FormatConfig { pub formats: Vec, } impl FormatConfig { pub fn new(config_path: &Option) -> Result { - let mut contents = String::new(); + let mut config_str = String::new(); - if let Some(config_path) = config_path { + let mut config: FormatConfig = if let Some(config_path) = config_path { let mut config = File::open(config_path)?; - config.read_to_string(&mut contents)?; - } + config.read_to_string(&mut config_str)?; + + toml::from_str(&config_str).map_err(|e| FormatConfigError::TomlError { + file_name: config_path.to_str().unwrap().to_string(), + err: e, + })? + } else { + FormatConfig::default() + }; for format_file_path in BuiltInFormats::iter() { if format_file_path.ends_with("md") { @@ -77,9 +83,17 @@ impl FormatConfig { let format_file = BuiltInFormats::get(&format_file_path).unwrap(); - contents.push_str(str::from_utf8(&format_file.data).unwrap()); + let config_str = str::from_utf8(&format_file.data).unwrap(); + + let mut built_in: FormatConfig = + toml::from_str(config_str).map_err(|e| FormatConfigError::TomlError { + file_name: format_file_path.to_string(), + err: e, + })?; + + config.formats.append(&mut built_in.formats); } - Ok(toml::from_str(&contents)?) + Ok(config) } } diff --git a/src/formatter/printers.rs b/src/formatter/printers.rs index c253741..b0b7350 100644 --- a/src/formatter/printers.rs +++ b/src/formatter/printers.rs @@ -1,3 +1,9 @@ +use byteorder::WriteBytesExt; +use num_bigint::{BigInt, BigUint}; +use serde::Deserialize; + +use crate::formatter::format::ByteOrderOperations; + pub fn print_bytes_as_array(data: &[u8]) -> String { format!("{:?}", data) } @@ -11,3 +17,72 @@ pub fn base_notation(b: u32) -> String { _ => format!("Base({}) ", b), } } + +#[derive(Debug, Deserialize, Clone)] +#[serde(tag = "print")] +pub enum PrintType { + Base { base: u32 }, + ByteArray, + Default, +} + +impl Default for PrintType { + fn default() -> Self { + PrintType::Default + } +} + +impl PrintType { + pub fn print_big_int(&self, big_int: BigInt) -> String { + match self { + PrintType::Base { base: b } => { + format!("{}{}", base_notation(*b), big_int.to_str_radix(*b)) + } + PrintType::ByteArray => print_bytes_as_array(&T::big_int_to_bytes(big_int)), + PrintType::Default => big_int.to_string(), + } + } + + pub fn print_big_u_int(&self, big_int: BigUint) -> String { + match self { + PrintType::Base { base: b } => { + format!("{}{}", base_notation(*b), big_int.to_str_radix(*b)) + } + PrintType::ByteArray => print_bytes_as_array(&T::big_u_int_to_bytes(big_int)), + PrintType::Default => big_int.to_string(), + } + } + + pub fn print_float(&self, float: f32) -> String { + match self { + PrintType::ByteArray => { + let mut bytes = Vec::with_capacity(4); + bytes.write_f32::(float).unwrap(); + print_bytes_as_array(&bytes) + } + _ => float.to_string(), + } + } + + pub fn print_double(&self, double: f64) -> String { + match self { + PrintType::ByteArray => { + let mut bytes = Vec::with_capacity(8); + bytes.write_f64::(double).unwrap(); + print_bytes_as_array(&bytes) + } + _ => double.to_string(), + } + } + + pub fn print_bytes(&self, bytes: &[u8]) -> String { + print_bytes_as_array(bytes) + } + + pub fn print_string(&self, s: &str) -> String { + match self { + PrintType::ByteArray => print_bytes_as_array(s.as_bytes()), + _ => s.to_string(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 1e2865c..0542378 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use crate::error::FormatyError; +use crate::formatter::format::Format; use crate::parser::parse_bytes_from_input_arg; use formatter::FormatConfig; use std::path::PathBuf; @@ -27,7 +28,7 @@ pub struct Args { data: Vec, } -fn formaty() -> Result<(), FormatyError> { +fn init() -> Result<(Vec, Format), FormatyError> { let args: Args = Args::from_args(); let config = FormatConfig::new(&args.config)?; @@ -36,22 +37,24 @@ fn formaty() -> Result<(), FormatyError> { .formats .iter() .find(|f| f.name == args.format) - .ok_or_else(|| FormatyError::FormatNotFound(args.format.to_string()))?; + .ok_or_else(|| FormatyError::FormatNotFound(args.format.to_string()))? + .clone(); let data = parse_bytes_from_input_arg(args.data).unwrap(); - - let data = format.format_data(&data)?; - - println!("{}", data); - - Ok(()) + Ok((data, format)) } fn main() { - match formaty() { - Ok(_) => {} + let (data, format) = match init() { + Ok((data, format)) => (data, format), Err(e) => { - println!("Error formatting data: {}", e.to_string()) + println!("Error initializing: {}", e); + return; } + }; + + match format.format_data(&data) { + Ok(data) => println!("{}", data), + Err(_) => print!("Unable to parse data"), } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d2b5e21..e9d522e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -50,9 +50,9 @@ pub fn parse_bytes_from_input_arg(src: Vec) -> Result, ByteArray let str_arr = if src.len() == 1 { src[0] - .replace(",", " ") - .replace("[", "") - .replace("]", "") + .replace(',', " ") + .replace('[', "") + .replace(']', "") .split_whitespace() .map(|s| s.to_string()) .collect()