From 59351fb699baf07b93e9421fc7f9e9dc44433ca8 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sat, 9 Apr 2022 12:34:15 -0600 Subject: [PATCH] Added default formatters + error handling --- Cargo.lock | 132 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +- formats/ccsds.toml | 49 ++++++++++++++++ formats/formats.md | 8 +++ src/error/mod.rs | 47 +++++++++++++++ src/formatter/mod.rs | 73 ++++++++++++++++++++++-- src/main.rs | 34 ++++++----- src/parser/mod.rs | 12 ++++ 8 files changed, 337 insertions(+), 21 deletions(-) create mode 100644 formats/formats.md create mode 100644 src/error/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 478aa0b..e4bf7c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,12 +34,27 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "2.33.3" @@ -55,17 +70,46 @@ dependencies = [ "vec_map", ] +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "formaty" version = "0.1.0" dependencies = [ "byteorder", "num-bigint", + "rust-embed", "serde", "structopt", "toml", ] +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "heck" version = "0.3.3" @@ -126,6 +170,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -168,6 +218,49 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rust-embed" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40377bff8cceee81e28ddb73ac97f5c2856ce5522f0b260b763f434cdfae602" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad22c7226e4829104deab21df575e995bfbc4adfad13a595e387477f238c1aec" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.130" @@ -188,6 +281,19 @@ dependencies = [ "syn", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + [[package]] name = "strsim" version = "0.8.0" @@ -247,6 +353,12 @@ dependencies = [ "serde", ] +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + [[package]] name = "unicode-segmentation" version = "1.8.0" @@ -277,6 +389,17 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "winapi" version = "0.3.9" @@ -293,6 +416,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 2b8de72..0472c16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ structopt = "0.3.23" serde = { version = "1.0", features = ["derive"] } toml = "0.5.8" byteorder = "1.4.3" -num-bigint = "0.4" \ No newline at end of file +num-bigint = "0.4" +rust-embed="6.3.0" \ No newline at end of file diff --git a/formats/ccsds.toml b/formats/ccsds.toml index 5e91140..b8b4047 100644 --- a/formats/ccsds.toml +++ b/formats/ccsds.toml @@ -5,6 +5,7 @@ # Example Packet: # [0xe0, 0xa1, 0xc0, 0x00, 0x00, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05] +# CCSDS packet w/ primary header + payload [[formats]] name = "ccsds" bit_flip = false @@ -47,3 +48,51 @@ field_type = {type = "UInt", bit_width = 16, endianness = "BigEndian"} name = "Data" # Allow payloads up to the max size that can be specfied in "Data Length" field_type = {type = "Bytes", max_len = 65535, endianness = "BigEndian"} + +# CCSDS packet w/ primary header + secondary header + payload +[[formats]] +name = "ccsds_sec" +bit_flip = false + +[[formats.fields]] +name = "Version Number" +bit_flip = true +field_type = {type = "UInt", bit_width = 3, endianness = "BigEndian"} + +[[formats.fields]] +name = "Packet Type" +bit_flip = true +field_type = {type = "UInt", bit_width = 1, endianness = "BigEndian"} + +[[formats.fields]] +name = "Secondary Header Flag" +bit_flip = true +field_type = {type = "UInt", bit_width = 1, endianness = "BigEndian"} + +[[formats.fields]] +name = "APID" +bit_flip = true +field_type = {type = "UInt", bit_width = 11, endianness = "BigEndian"} + +[[formats.fields]] +name = "Sequence Flags" +bit_flip = true +field_type = {type = "UInt", bit_width = 2, endianness = "BigEndian"} + +[[formats.fields]] +name = "Packet Sequence Count" +bit_flip = true +field_type = {type = "UInt", bit_width = 14, endianness = "BigEndian"} + +[[formats.fields]] +name = "Data Length" +field_type = {type = "UInt", bit_width = 16, endianness = "BigEndian"} + +[[formats.fields]] +name = "Secondary header" +field_type = {type = "Bytes", max_len = 8, endianness = "BigEndian"} + +[[formats.fields]] +name = "data" +# allow payloads up to the max size that can be specfied in "data length" +field_type = {type = "Bytes", max_len = 65535, endianness = "BigEndian"} diff --git a/formats/formats.md b/formats/formats.md new file mode 100644 index 0000000..681c729 --- /dev/null +++ b/formats/formats.md @@ -0,0 +1,8 @@ +# Built-In Formats +All formats in this directory are automatically included in the formaty binary. Additional formats can be included +at run time by using the `--config` flag. + +## Formats Included +* [CCSDS](ccsds.toml) - Standard CCSDS command/tlm packets + * `ccsds`: CCSDS packet with primary header + raw payload + * `ccsds_sec`: CCSDS packet with primary header + secondary header + raw payload \ No newline at end of file diff --git a/src/error/mod.rs b/src/error/mod.rs new file mode 100644 index 0000000..88fac4c --- /dev/null +++ b/src/error/mod.rs @@ -0,0 +1,47 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; +use crate::parser::ByteArrayParseErr; +use crate::formatter::format::FormatError; +use crate::formatter::FormatConfigError; + +#[derive(Debug)] +pub enum FormatyError { + ByteArrayParseError(ByteArrayParseErr), + FormatError(FormatError), + FormatConfigError(FormatConfigError), + FormatNotFound(String) + +} + +impl From for FormatyError { + fn from(e: ByteArrayParseErr) -> Self { + Self::ByteArrayParseError(e) + } +} + +impl From for FormatyError { + fn from(e: FormatError) -> Self { + Self::FormatError(e) + } +} + +impl From for FormatyError { + fn from(e: FormatConfigError) -> Self { + Self::FormatConfigError(e) + } +} + +impl Display for FormatyError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let err_msg = match self { + FormatyError::ByteArrayParseError(e) => e.to_string(), + FormatyError::FormatError(e) => e.to_string(), + FormatyError::FormatConfigError(e) => e.to_string(), + FormatyError::FormatNotFound(err_msg) => format!("'{}' config not found.", err_msg) + }; + + write!(f, "{}", err_msg) + } +} + +impl Error for FormatyError {} \ No newline at end of file diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs index 21292b3..c87eb10 100644 --- a/src/formatter/mod.rs +++ b/src/formatter/mod.rs @@ -5,7 +5,56 @@ use crate::formatter::format::Format; use serde::Deserialize; use std::fs::File; use std::io::Read; -use std::path::Path; +use std::path::PathBuf; +use rust_embed::RustEmbed; +use std::str; +use std::str::Utf8Error; +use std::fmt::{Display, Formatter}; +use std::error::Error; + +#[derive(Debug)] +pub enum FormatConfigError { + IOError(std::io::Error), + TomlError(toml::de::Error), + Utf8Error(Utf8Error) +} + +impl Error for FormatConfigError {} + +impl From for FormatConfigError { + fn from(e: std::io::Error) -> Self { + Self::IOError(e) + } +} + +impl From for FormatConfigError { + fn from(e: toml::de::Error) -> Self { + Self::TomlError(e) + } +} + +impl From for FormatConfigError { + fn from(e: Utf8Error) -> Self { + Self::Utf8Error(e) + } +} + +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::Utf8Error(e) => e.to_string() + }; + + write!(f, "Format Config Error: {}", err_msg) + } +} + +#[derive(RustEmbed)] +#[folder = "formats/"] +#[include = "*.toml"] +struct BuiltInFormats; #[derive(Debug, Deserialize, Clone)] pub struct FormatConfig { @@ -13,13 +62,25 @@ pub struct FormatConfig { } impl FormatConfig { - pub fn new(config_path: &Path) -> Result { - let mut config = File::open(config_path)?; - + pub fn new(config_path: &Option) -> Result { let mut contents = String::new(); - config.read_to_string(&mut contents)?; + if let Some(config_path) = config_path { + let mut config = File::open(config_path)?; + config.read_to_string(&mut contents)?; + } - Ok(toml::from_str(&contents).unwrap()) + for format_file_path in BuiltInFormats::iter() { + if format_file_path.ends_with("md") { + continue; + } + + let format_file = BuiltInFormats::get(&format_file_path).unwrap(); + + contents.push_str(str::from_utf8(&format_file.data).unwrap()); + } + + + Ok(toml::from_str(&contents)?) } } diff --git a/src/main.rs b/src/main.rs index cfaf1b3..146abcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,16 +2,18 @@ use crate::parser::parse_bytes_from_input_arg; use formatter::FormatConfig; use std::path::PathBuf; use structopt::StructOpt; +use crate::error::FormatyError; mod byte_stream; mod formatter; mod parser; +mod error; #[derive(Debug, StructOpt)] #[structopt(name = "Formaty", about = "Arbitrary Binary Data Formatting")] pub struct Args { - #[structopt(parse(from_os_str), help = "Path to the format config")] - config: PathBuf, + #[structopt(short = "c", long="config", parse(from_os_str), help = "Path to the format config")] + config: Option, #[structopt(help = "Format to parse data as")] format: String, @@ -20,23 +22,27 @@ pub struct Args { data: Vec, } -fn main() { +fn formaty() -> Result<(), FormatyError> { let args: Args = Args::from_args(); - let config = FormatConfig::new(&args.config).unwrap(); + let config = FormatConfig::new(&args.config)?; - let format = match config.formats.iter().find(|f| f.name == args.format) { - None => { - println!("Format not found in config file"); - return; - } - Some(format) => format, - }; + let format = config.formats.iter().find(|f| f.name == args.format).ok_or(FormatyError::FormatNotFound(args.format.to_string()))?; let data = parse_bytes_from_input_arg(args.data).unwrap(); - match format.format_data(&data) { - Ok(data_str) => println!("{}", data_str), - Err(_) => println!("Error formatting data"), + let data = format.format_data(&data)?; + + println!("{}", data); + + Ok(()) +} + +fn main() { + match formaty() { + Ok(_) => {} + Err(e) => { + println!("Error formatting data: {}", e.to_string()) + } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d7a514d..ba612c5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,4 +1,5 @@ use std::num::ParseIntError; +use std::fmt::{Display, Formatter}; #[derive(Debug, Clone)] pub enum ByteArrayParseErr { @@ -12,6 +13,17 @@ impl From for ByteArrayParseErr { } } +impl Display for ByteArrayParseErr { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let err_msg = match self { + ByteArrayParseErr::EmptySrcArray => "Provided array is empty!".to_string(), + ByteArrayParseErr::ParseIntError(e) => format!("Failed to parse as int: {}", e), + }; + + write!(f, "{}", err_msg) + } +} + fn bytes_from_str_array(src: Vec) -> Result, ByteArrayParseErr> { src.iter() .map(|element| {