use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::io::{BufRead, BufReader, Error, Read}; use std::num::ParseIntError; use std::path::Path; use std::str::FromStr; use std::string::FromUtf8Error; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum InputTypes { Array, String, Binary, } impl FromStr for InputTypes { type Err = String; fn from_str(s: &str) -> Result { let s = s.to_lowercase(); match s.as_str() { "array" | "a" => Ok(Self::Array), "string" | "s" => Ok(Self::String), "binary" | "b" => Ok(Self::Binary), _ => Err(format!("Invalid input type '{}'", s)), } } } impl InputTypes { fn parse_byte_default(element: &str) -> Result { if element.starts_with("0x") || element.starts_with("0X") { u8::from_str_radix(&element[2..], 16) } else if element.starts_with("0b") || element.starts_with("0B") { u8::from_str_radix(&element[2..], 2) } else if element.starts_with('h') || element.starts_with('H') { u8::from_str_radix(&element[1..], 16) } else if element.len() > 1 && element.starts_with('0') { u8::from_str_radix(&element[1..], 8) } else { element.parse() } } fn parse_byte_base(element: &str, base: u32) -> Result { u8::from_str_radix(element, base) } fn parse_array(elements: Vec, base: Option) -> Result, ParseIntError> { elements .iter() .map(|element| { if let Some(base) = base { Self::parse_byte_base(element, base) } else { Self::parse_byte_default(element) } }) .collect() } pub fn parse_arg_input( &self, src: Vec, base: Option, ) -> Result, ByteArrayParseErr> { if src.is_empty() { return Err(ByteArrayParseErr::EmptySrcArray); } let str_arr = if src.len() == 1 { src[0] .replace(',', " ") .replace('[', "") .replace(']', "") .split_whitespace() .map(|s| s.to_string()) .collect() } else { src }; let data = match self { InputTypes::Array => Self::parse_array(str_arr, base)?, InputTypes::String => str_arr.join(" ").as_bytes().to_vec(), InputTypes::Binary => return Err(ByteArrayParseErr::UnsupportedFormat), }; Ok(data) } #[allow(clippy::unused_io_amount)] fn parse_data_from_file( &self, mut buf_reader: Box, base: Option, ) -> Result, ByteArrayParseErr> { let mut data: Vec = Vec::new(); let mut done = false; while !done { let mut buf: Vec = 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; } } 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, ) -> Result, 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) -> Result, ByteArrayParseErr> { let reader = Box::new(BufReader::new(std::io::stdin())); self.parse_data_from_file(reader, base) } } #[derive(Debug)] pub enum ByteArrayParseErr { EmptySrcArray, ParseIntError(ParseIntError), UnsupportedFormat, FileError(std::io::Error), ParseStringError(FromUtf8Error), } impl From for ByteArrayParseErr { fn from(e: ParseIntError) -> Self { ByteArrayParseErr::ParseIntError(e) } } impl From for ByteArrayParseErr { fn from(e: Error) -> Self { Self::FileError(e) } } impl From for ByteArrayParseErr { fn from(e: FromUtf8Error) -> Self { Self::ParseStringError(e) } } 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), 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) } } #[cfg(test)] mod tests { use crate::parser::InputTypes; #[test] fn parse_base_10_array() { let array = vec!["[0, 1, 2, 3, 4]".to_string()]; let out = InputTypes::Array.parse_arg_input(array, None).unwrap(); assert_eq!(out, vec![0, 1, 2, 3, 4]) } #[test] fn parse_base_2_array() { let array = vec!["[0b0, 0b1, 0b10, 0b11, 0b100]".to_string()]; let out = InputTypes::Array.parse_arg_input(array, None).unwrap(); assert_eq!(out, vec![0, 1, 2, 3, 4]) } #[test] fn parse_base_8_array() { let array = vec!["[00, 01, 02, 03, 04]".to_string()]; let out = InputTypes::Array.parse_arg_input(array, None).unwrap(); assert_eq!(out, vec![0, 1, 2, 3, 4]) } #[test] fn parse_base_16_array() { let array = vec!["[0x0, 0x1, 0x2, h3, H4]".to_string()]; let out = InputTypes::Array.parse_arg_input(array, None).unwrap(); assert_eq!(out, vec![0, 1, 2, 3, 4]) } #[test] fn parse_base_mixed() { let array = vec!["[00 0x1, 2, h3, H4]".to_string()]; let out = InputTypes::Array.parse_arg_input(array, None).unwrap(); assert_eq!(out, vec![0, 1, 2, 3, 4]) } #[test] fn parse_no_braces_multi_args() { let array = vec![ "0".to_string(), "1".to_string(), "2".to_string(), "3".to_string(), "4".to_string(), ]; let out = InputTypes::Array.parse_arg_input(array, None).unwrap(); assert_eq!(out, vec![0, 1, 2, 3, 4]) } #[test] fn parse_no_braces_one_arg() { let array = vec!["0 1 2 3 4".to_string()]; let out = InputTypes::Array.parse_arg_input(array, None).unwrap(); assert_eq!(out, vec![0, 1, 2, 3, 4]) } #[test] fn parse_string() { let string = "Hello World!"; let out = InputTypes::String .parse_arg_input(vec![string.to_string()], None) .unwrap(); assert_eq!(string.as_bytes(), out) } }