Added file input + stdin support
+ Files/stdin input can be binary or ascii + Handles piping from other programs + Misc test fixes + Updated readme + clippy + fmtmain
parent
287ba32ea7
commit
f2cec4c89e
|
@ -64,16 +64,18 @@ Formaty 0.1.0
|
||||||
Arbitrary Binary Data Formatting
|
Arbitrary Binary Data Formatting
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
formaty [OPTIONS] <format> [data]...
|
formaty [FLAGS] [OPTIONS] <format> [data]...
|
||||||
|
|
||||||
FLAGS:
|
FLAGS:
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
|
-s, --stdin Input data from stdin
|
||||||
-V, --version Prints version information
|
-V, --version Prints version information
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
-b, --base <base> Base of the input values
|
-b, --base <base> Base of the input values
|
||||||
-c, --config <config> Path to the format config [env: FORMATY_CONFIG=]
|
-c, --config <config> Path to the format config [env: FORMATY_CONFIG=]
|
||||||
-g, --global_config <global-config> Path to the global config directory [env: FORMATY_GLOBAL_CONFIG=]
|
-g, --global_config <global-config> Path to the global config directory [env: FORMATY_GLOBAL_CONFIG=]
|
||||||
|
-f, --file <input-file> Input data from file
|
||||||
-i, --input_type <input-type> Input data type [default: array]
|
-i, --input_type <input-type> Input data type [default: array]
|
||||||
|
|
||||||
ARGS:
|
ARGS:
|
||||||
|
|
|
@ -43,7 +43,6 @@ field_type = {type = "Float", endianness = "BigEndian"}
|
||||||
# string field. Printed as a byte array
|
# string field. Printed as a byte array
|
||||||
[[formats.fields]]
|
[[formats.fields]]
|
||||||
name = "string field"
|
name = "string field"
|
||||||
print_type = {print = "ByteArray"}
|
|
||||||
field_type = {type = "String", endianness = "BigEndian", max_len = 55}
|
field_type = {type = "String", endianness = "BigEndian", max_len = 55}
|
||||||
|
|
||||||
# byte field
|
# byte field
|
||||||
|
|
|
@ -611,4 +611,46 @@ mod tests {
|
||||||
assert_eq!(width, 16);
|
assert_eq!(width, 16);
|
||||||
assert_eq!(output, "[222, 173]")
|
assert_eq!(output, "[222, 173]")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_str_big_endian() {
|
||||||
|
let field = Field {
|
||||||
|
field_type: FieldType::String {
|
||||||
|
max_len: 64,
|
||||||
|
endianness: Endianness::BigEndian,
|
||||||
|
},
|
||||||
|
name: "test".to_string(),
|
||||||
|
bit_flip: None,
|
||||||
|
print_type: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
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).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(width, 40);
|
||||||
|
assert_eq!(output, "Test")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_str_little_endian() {
|
||||||
|
let field = Field {
|
||||||
|
field_type: FieldType::String {
|
||||||
|
max_len: 64,
|
||||||
|
endianness: Endianness::LittleEndian,
|
||||||
|
},
|
||||||
|
name: "test".to_string(),
|
||||||
|
bit_flip: None,
|
||||||
|
print_type: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
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).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(width, 40);
|
||||||
|
assert_eq!(output, "Test")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub enum FormatConfigError {
|
||||||
file_name: String,
|
file_name: String,
|
||||||
err: toml::de::Error,
|
err: toml::de::Error,
|
||||||
},
|
},
|
||||||
Utf8Error(std::str::Utf8Error),
|
Utf8Error(str::Utf8Error),
|
||||||
FormatNotFound(String),
|
FormatNotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ impl From<std::io::Error> for FormatConfigError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::str::Utf8Error> for FormatConfigError {
|
impl From<str::Utf8Error> for FormatConfigError {
|
||||||
fn from(e: std::str::Utf8Error) -> Self {
|
fn from(e: str::Utf8Error) -> Self {
|
||||||
Self::Utf8Error(e)
|
Self::Utf8Error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ccsds_format() -> Format {
|
fn get_ccsds_format() -> Format {
|
||||||
let format_config = FormatConfig::new(&None).unwrap();
|
let format_config = FormatConfig::new(&None, &None).unwrap();
|
||||||
|
|
||||||
format_config.get_format("ccsds").unwrap()
|
format_config.get_format("ccsds").unwrap()
|
||||||
}
|
}
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -42,6 +42,12 @@ pub struct Args {
|
||||||
#[structopt(help = "Base of the input values", short = "b", long = "base")]
|
#[structopt(help = "Base of the input values", short = "b", long = "base")]
|
||||||
base: Option<u32>,
|
base: Option<u32>,
|
||||||
|
|
||||||
|
#[structopt(help = "Input data from file", short = "f", long = "file")]
|
||||||
|
input_file: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[structopt(help = "Input data from stdin", short = "s", long = "stdin")]
|
||||||
|
input_stdin: bool,
|
||||||
|
|
||||||
#[structopt(help = "Format to parse data as")]
|
#[structopt(help = "Format to parse data as")]
|
||||||
format: String,
|
format: String,
|
||||||
|
|
||||||
|
@ -56,7 +62,14 @@ fn init() -> Result<(Vec<u8>, Format), FormatyError> {
|
||||||
|
|
||||||
let format = config.get_format(&args.format)?;
|
let format = config.get_format(&args.format)?;
|
||||||
|
|
||||||
let data = args.input_type.parse_input(args.data, args.base)?;
|
let data = if args.input_stdin {
|
||||||
|
args.input_type.parse_stdin(args.base)?
|
||||||
|
} else if let Some(input_file) = args.input_file {
|
||||||
|
args.input_type.parse_file(&input_file, args.base)?
|
||||||
|
} else {
|
||||||
|
args.input_type.parse_arg_input(args.data, args.base)?
|
||||||
|
};
|
||||||
|
|
||||||
Ok((data, format))
|
Ok((data, format))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::io::{BufRead, BufReader, Error, Read};
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum InputTypes {
|
pub enum InputTypes {
|
||||||
Array,
|
Array,
|
||||||
String,
|
String,
|
||||||
|
Binary,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for InputTypes {
|
impl FromStr for InputTypes {
|
||||||
|
@ -18,6 +22,7 @@ impl FromStr for InputTypes {
|
||||||
match s.as_str() {
|
match s.as_str() {
|
||||||
"array" | "a" => Ok(Self::Array),
|
"array" | "a" => Ok(Self::Array),
|
||||||
"string" | "s" => Ok(Self::String),
|
"string" | "s" => Ok(Self::String),
|
||||||
|
"binary" | "b" => Ok(Self::Binary),
|
||||||
_ => Err(format!("Invalid input type '{}'", s)),
|
_ => Err(format!("Invalid input type '{}'", s)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +60,7 @@ impl InputTypes {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_input(
|
pub fn parse_arg_input(
|
||||||
&self,
|
&self,
|
||||||
src: Vec<String>,
|
src: Vec<String>,
|
||||||
base: Option<u32>,
|
base: Option<u32>,
|
||||||
|
@ -79,16 +84,67 @@ impl InputTypes {
|
||||||
let data = match self {
|
let data = match self {
|
||||||
InputTypes::Array => Self::parse_array(str_arr, base)?,
|
InputTypes::Array => Self::parse_array(str_arr, base)?,
|
||||||
InputTypes::String => str_arr.join(" ").as_bytes().to_vec(),
|
InputTypes::String => str_arr.join(" ").as_bytes().to_vec(),
|
||||||
|
InputTypes::Binary => return Err(ByteArrayParseErr::UnsupportedFormat),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_io_amount)]
|
||||||
|
fn parse_data_from_file(
|
||||||
|
&self,
|
||||||
|
mut buf_reader: Box<dyn BufRead>,
|
||||||
|
base: Option<u32>,
|
||||||
|
) -> Result<Vec<u8>, ByteArrayParseErr> {
|
||||||
|
let mut data: Vec<u8> = Vec::new();
|
||||||
|
let mut done = false;
|
||||||
|
|
||||||
|
while !done {
|
||||||
|
let mut buf: Vec<u8> = vec![0; 2048];
|
||||||
|
let len = buf_reader.read(&mut buf)?;
|
||||||
|
|
||||||
|
if len < buf.len() {
|
||||||
|
buf.truncate(len);
|
||||||
|
data.append(&mut buf);
|
||||||
|
done = true;
|
||||||
|
} else if len > 0 {
|
||||||
|
data.append(&mut buf);
|
||||||
|
} else {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
match self {
|
||||||
|
InputTypes::Array | InputTypes::String => {
|
||||||
|
let str_data = String::from_utf8(data)?;
|
||||||
|
self.parse_arg_input(vec![str_data], base)
|
||||||
|
}
|
||||||
|
InputTypes::Binary => Ok(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_file(
|
||||||
|
&self,
|
||||||
|
file_path: &Path,
|
||||||
|
base: Option<u32>,
|
||||||
|
) -> Result<Vec<u8>, ByteArrayParseErr> {
|
||||||
|
let reader = Box::new(BufReader::new(std::fs::File::open(file_path)?));
|
||||||
|
self.parse_data_from_file(reader, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_stdin(&self, base: Option<u32>) -> Result<Vec<u8>, ByteArrayParseErr> {
|
||||||
|
let reader = Box::new(BufReader::new(std::io::stdin()));
|
||||||
|
self.parse_data_from_file(reader, base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ByteArrayParseErr {
|
pub enum ByteArrayParseErr {
|
||||||
EmptySrcArray,
|
EmptySrcArray,
|
||||||
ParseIntError(ParseIntError),
|
ParseIntError(ParseIntError),
|
||||||
|
UnsupportedFormat,
|
||||||
|
FileError(std::io::Error),
|
||||||
|
ParseStringError(FromUtf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseIntError> for ByteArrayParseErr {
|
impl From<ParseIntError> for ByteArrayParseErr {
|
||||||
|
@ -97,11 +153,28 @@ impl From<ParseIntError> for ByteArrayParseErr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for ByteArrayParseErr {
|
||||||
|
fn from(e: Error) -> Self {
|
||||||
|
Self::FileError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FromUtf8Error> for ByteArrayParseErr {
|
||||||
|
fn from(e: FromUtf8Error) -> Self {
|
||||||
|
Self::ParseStringError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for ByteArrayParseErr {
|
impl Display for ByteArrayParseErr {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
let err_msg = match self {
|
let err_msg = match self {
|
||||||
ByteArrayParseErr::EmptySrcArray => "Provided array is empty!".to_string(),
|
ByteArrayParseErr::EmptySrcArray => "Provided array is empty!".to_string(),
|
||||||
ByteArrayParseErr::ParseIntError(e) => format!("Failed to parse as int: {}", e),
|
ByteArrayParseErr::ParseIntError(e) => format!("Failed to parse as int: {}", e),
|
||||||
|
ByteArrayParseErr::UnsupportedFormat => {
|
||||||
|
"Format is not support with the supplied input type".to_string()
|
||||||
|
}
|
||||||
|
ByteArrayParseErr::FileError(e) => format!("Unable to parse file: {}", e),
|
||||||
|
ByteArrayParseErr::ParseStringError(e) => format!("Unable to parse string: {}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "{}", err_msg)
|
write!(f, "{}", err_msg)
|
||||||
|
@ -116,7 +189,7 @@ mod tests {
|
||||||
fn parse_base_10_array() {
|
fn parse_base_10_array() {
|
||||||
let array = vec!["[0, 1, 2, 3, 4]".to_string()];
|
let array = vec!["[0, 1, 2, 3, 4]".to_string()];
|
||||||
|
|
||||||
let out = InputTypes::Array.parse_input(array, None).unwrap();
|
let out = InputTypes::Array.parse_arg_input(array, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
||||||
}
|
}
|
||||||
|
@ -125,7 +198,7 @@ mod tests {
|
||||||
fn parse_base_2_array() {
|
fn parse_base_2_array() {
|
||||||
let array = vec!["[0b0, 0b1, 0b10, 0b11, 0b100]".to_string()];
|
let array = vec!["[0b0, 0b1, 0b10, 0b11, 0b100]".to_string()];
|
||||||
|
|
||||||
let out = InputTypes::Array.parse_input(array, None).unwrap();
|
let out = InputTypes::Array.parse_arg_input(array, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
||||||
}
|
}
|
||||||
|
@ -134,7 +207,7 @@ mod tests {
|
||||||
fn parse_base_8_array() {
|
fn parse_base_8_array() {
|
||||||
let array = vec!["[00, 01, 02, 03, 04]".to_string()];
|
let array = vec!["[00, 01, 02, 03, 04]".to_string()];
|
||||||
|
|
||||||
let out = InputTypes::Array.parse_input(array, None).unwrap();
|
let out = InputTypes::Array.parse_arg_input(array, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
||||||
}
|
}
|
||||||
|
@ -143,7 +216,7 @@ mod tests {
|
||||||
fn parse_base_16_array() {
|
fn parse_base_16_array() {
|
||||||
let array = vec!["[0x0, 0x1, 0x2, h3, H4]".to_string()];
|
let array = vec!["[0x0, 0x1, 0x2, h3, H4]".to_string()];
|
||||||
|
|
||||||
let out = InputTypes::Array.parse_input(array, None).unwrap();
|
let out = InputTypes::Array.parse_arg_input(array, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
||||||
}
|
}
|
||||||
|
@ -152,7 +225,7 @@ mod tests {
|
||||||
fn parse_base_mixed() {
|
fn parse_base_mixed() {
|
||||||
let array = vec!["[00 0x1, 2, h3, H4]".to_string()];
|
let array = vec!["[00 0x1, 2, h3, H4]".to_string()];
|
||||||
|
|
||||||
let out = InputTypes::Array.parse_input(array, None).unwrap();
|
let out = InputTypes::Array.parse_arg_input(array, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
||||||
}
|
}
|
||||||
|
@ -166,7 +239,7 @@ mod tests {
|
||||||
"3".to_string(),
|
"3".to_string(),
|
||||||
"4".to_string(),
|
"4".to_string(),
|
||||||
];
|
];
|
||||||
let out = InputTypes::Array.parse_input(array, None).unwrap();
|
let out = InputTypes::Array.parse_arg_input(array, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
||||||
}
|
}
|
||||||
|
@ -174,7 +247,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_no_braces_one_arg() {
|
fn parse_no_braces_one_arg() {
|
||||||
let array = vec!["0 1 2 3 4".to_string()];
|
let array = vec!["0 1 2 3 4".to_string()];
|
||||||
let out = InputTypes::Array.parse_input(array, None).unwrap();
|
let out = InputTypes::Array.parse_arg_input(array, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
assert_eq!(out, vec![0, 1, 2, 3, 4])
|
||||||
}
|
}
|
||||||
|
@ -184,7 +257,7 @@ mod tests {
|
||||||
let string = "Hello World!";
|
let string = "Hello World!";
|
||||||
|
|
||||||
let out = InputTypes::String
|
let out = InputTypes::String
|
||||||
.parse_input(vec![string.to_string()], None)
|
.parse_arg_input(vec![string.to_string()], None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(string.as_bytes(), out)
|
assert_eq!(string.as_bytes(), out)
|
||||||
|
|
Loading…
Reference in New Issue