diff --git a/shell.nix b/shell.nix index 870a039..1d073be 100644 --- a/shell.nix +++ b/shell.nix @@ -5,6 +5,7 @@ pkgs.mkShell { cargo rustc rustfmt + gdb ]; RUST_BACKTRACE = 1; diff --git a/src/bin/common/argument_parsing.rs b/src/bin/common/argument_parsing.rs new file mode 100644 index 0000000..1e13095 --- /dev/null +++ b/src/bin/common/argument_parsing.rs @@ -0,0 +1,41 @@ +pub struct Arguments { + pub hexadecimal: bool, + pub mode: u8, + pub program_name: String, + pub reverse_characters: bool, + pub value_to_convert: String, +} + +pub fn parse_direction(args: &mut impl Iterator, opts: &Arguments, current_arg: String) -> Result{ + let mut rvalue = opts.mode; + let direction = match args.next() { + None => return Err(format!("argument for {} was not provided", current_arg)), + Some(e) => e, + }; + match direction.as_str() { + "row" => { rvalue &= !(1 << 1) ; rvalue &= !(1 << 2); } + "row-reverse" => { rvalue |= 1 << 1 ; rvalue &= !(1 << 2); } + "column" => { rvalue &= !(1 << 1) ; rvalue |= 1 << 2 ; } + "column-reverse" => { rvalue |= 1 << 1 ; rvalue |= 1 << 2 ; } + _ => return Err( + format!("invalid direction \"{}\" provided for {}", direction, current_arg) + ) + }; + return Ok(rvalue); +} + +pub fn parse_wrap(args: &mut impl Iterator, opts: &Arguments, current_arg: String) -> Result{ + let mut rvalue = opts.mode; + let wrap = match args.next() { + None => return Err(format!("argument for {} was not provided", current_arg)), + Some(e) => e, + }; + match wrap.as_str() { + "normal" => rvalue &= !(1 << 0), + "reverse" => rvalue |= 1 << 0, + _ => return Err( + format!("invalid wrap \"{}\" provided for {}", wrap, current_arg) + ) + }; + return Ok(rvalue); +} diff --git a/src/bin/common/mod.rs b/src/bin/common/mod.rs new file mode 100644 index 0000000..55989fb --- /dev/null +++ b/src/bin/common/mod.rs @@ -0,0 +1 @@ +pub mod argument_parsing; diff --git a/src/bin/convert_from_baillie.rs b/src/bin/convert_from_baillie.rs new file mode 100644 index 0000000..5e9d353 --- /dev/null +++ b/src/bin/convert_from_baillie.rs @@ -0,0 +1,105 @@ +use std::{env, process::exit}; + +use binary_braillie::binary_braillie_to_bytes; + +mod common; +use common::argument_parsing; + +fn main() { + let args = parse_args(); + if args.value_to_convert.len() <= 0 { + print!("{}: No input specified!", args.program_name) + } + let decoded_ = binary_braillie_to_bytes( + if !args.reverse_characters { + args.value_to_convert + } else { + args.value_to_convert.chars().rev().collect() + }, + args.mode.try_into().unwrap() + ); + + let decoded = match decoded_ { + Ok(e) => e, + Err(()) => { + print!("{}: Invalid input provided", args.program_name); + exit(128); + } + }; + + if args.hexadecimal { + print!("{}", hex::encode(decoded)); + } else { + print!("{}", match String::from_utf8(decoded) { + Ok(e) => e, + Err(e) => { + print!("{}: decoded string is invalid: {}", args.program_name, e.utf8_error()); + exit(130); + } + }); + } +} + +fn parse_args() -> argument_parsing::Arguments { + let mut args = env::args().peekable(); + let mut rvalue = argument_parsing::Arguments { + hexadecimal: false, + mode: 0, + program_name: args.next().unwrap_or(String::from("t2br")), + reverse_characters: false, + value_to_convert: String::new() + }; + let program_name = &rvalue.program_name; + + while args.peek() != None { + let arg = args.next().unwrap(); + if arg == "--help" + || arg == "-h" + || arg == "-?" + { + print_help(rvalue.program_name); + exit(1); + } + else if arg == "--hex" { + rvalue.hexadecimal = true; + } + else if arg == "--direction" { + match argument_parsing::parse_direction(&mut args, &rvalue, arg) { + Ok(e) => rvalue.mode = e, + Err(e) => { + print!("{}: {}", program_name, e); + exit(64); + } + } + } + else if arg == "--wrap" { + match argument_parsing::parse_wrap(&mut args, &rvalue, arg) { + Ok(e) => rvalue.mode = e, + Err(e) => { + print!("{}: {}", program_name, e); + exit(65); + } + } + } + else if arg == "-r" + || arg == "--reverse" { + rvalue.reverse_characters = true; + } + else { + rvalue.value_to_convert.push_str(&arg); + } + } + return rvalue; +} + +fn print_help(program_name: String) { + print!("Usage: {program_name} [options] \n"); + print!("Convert binary represented in braillie characters to readable text\n\n"); + print!(" --hex convert to hexadecimal instead of text\n\n"); + print!(" --direction [mode] direction of bits per character\n"); + print!(" possible values: row, row-reverse, column, column-reverse\n\n"); + print!(" --wrap [mode] direction of bits wrapping per character\n"); + print!(" possible values: normal, reverse\n\n"); + print!(" -r, --reverse reverse outputted characters\n\n"); + print!(" -h, -?, --help display this help and exit"); +} diff --git a/src/bin/convert_to_braillie.rs b/src/bin/convert_to_braillie.rs index 4922e17..170677b 100644 --- a/src/bin/convert_to_braillie.rs +++ b/src/bin/convert_to_braillie.rs @@ -3,6 +3,9 @@ use std::process::exit; use binary_braillie::to_binary_braillie; +mod common; +use common::argument_parsing; + fn main() { let args = parse_args(); if args.value_to_convert.len() <= 0 { @@ -24,17 +27,11 @@ fn main() { print!("{}", to_binary_braillie(&value, value.len(), args.mode.try_into().unwrap())) } -struct Arguments { - pub hexadecimal: bool, - pub mode: u8, - pub program_name: String, - pub reverse_characters: bool, - pub value_to_convert: String, -} -fn parse_args() -> Arguments { + +fn parse_args() -> argument_parsing::Arguments { let mut args = env::args().peekable(); - let mut rvalue = Arguments { + let mut rvalue = argument_parsing::Arguments { hexadecimal: false, mode: 0, program_name: args.next().unwrap_or(String::from("t2br")), @@ -57,31 +54,19 @@ fn parse_args() -> Arguments { rvalue.hexadecimal = true; } else if arg == "--direction" { - let direction = args.next().unwrap_or_else(|| { - println!("{}: argument for {} was not provided", program_name, arg); - exit(64); - }); - match direction.as_str() { - "row" => { rvalue.mode &= !(1 << 1) ; rvalue.mode &= !(1 << 2); } - "row-reverse" => { rvalue.mode |= 1 << 1 ; rvalue.mode &= !(1 << 2); } - "column" => { rvalue.mode &= !(1 << 1) ; rvalue.mode |= 1 << 2 ; } - "column-reverse" => { rvalue.mode |= 1 << 1 ; rvalue.mode |= 1 << 2 ; } - _ => { - println!("{}: invalid direction \"{}\" provided", program_name, direction); - exit(65); + match argument_parsing::parse_direction(&mut args, &rvalue, arg) { + Ok(e) => rvalue.mode = e, + Err(e) => { + print!("{}: {}", program_name, e); + exit(64); } } } else if arg == "--wrap" { - let wrap = args.next().unwrap_or_else(|| { - println!("{}: argument for {} was not provided", program_name, arg); - exit(64); - }); - match wrap.as_str() { - "normal" => rvalue.mode &= !(1 << 0), - "reverse" => rvalue.mode |= 1 << 0, - _ => { - println!("{}: invalid wrap \"{}\" provided", program_name, wrap); + match argument_parsing::parse_wrap(&mut args, &rvalue, arg) { + Ok(e) => rvalue.mode = e, + Err(e) => { + print!("{}: {}", program_name, e); exit(65); } } diff --git a/src/lib.rs b/src/lib.rs index 25149a1..23c9c9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,9 +81,49 @@ pub fn to_binary_braillie_str(input: impl AsRef, mode: EncodeOrientation) - return to_binary_braillie(input.as_ref().as_bytes(), input.as_ref().len(), mode); } +pub fn binary_braillie_char_to_index(input: char, mode: EncodeOrientation) -> Result{ + if !(0x2800 <= input as u32 && input as u32 <= 0x28ff) { return Err(()) } + let mapping = MAPPINGS[mode as usize]; + let _index: u8 = (input as u32 - 0x2800) as u8; + return Ok( + ((_index >> mapping[7]) & 1) << 0 + | ((_index >> mapping[6]) & 1) << 1 + | ((_index >> mapping[5]) & 1) << 2 + | ((_index >> mapping[4]) & 1) << 3 + | ((_index >> mapping[3]) & 1) << 4 + | ((_index >> mapping[2]) & 1) << 5 + | ((_index >> mapping[1]) & 1) << 6 + | ((_index >> mapping[0]) & 1) << 7 + ); +} + +pub fn binary_braillie_to_bytes( + input: impl AsRef, + mode: EncodeOrientation +) -> Result, ()> { + let mut rvalue: Vec = Vec::with_capacity(input.as_ref().chars().count()); + for char in input.as_ref().chars() { + rvalue.push(match binary_braillie_char_to_index(char, mode) { + Ok(e) => e, + Err(e) => return Err(e), + }) + } + return Ok(rvalue); +} + +pub fn binary_braillie_to_str(input: impl AsRef, mode: EncodeOrientation) -> Result { + let bytes = match binary_braillie_to_bytes(input, mode) { + Ok(e) => e, + Err(e) => return Err(e) + }; + return match String::from_utf8(bytes) { + Ok(e) => Ok(e), + Err(_e) => Err(()) + } +} #[cfg(test)] -mod test_lib { +mod test_lib_to_braillie { use crate::{index_to_braillie_char, to_binary_braillie_str, EncodeOrientation}; #[test] @@ -146,3 +186,79 @@ mod test_lib { ); } } + +#[cfg(test)] +mod test_lib_from_braillie { + use crate::{binary_braillie_char_to_index, EncodeOrientation}; + + #[test] + fn check_characters_out_of_range() { + for i in 0..0x10ffff { + if 0x2800 <= i && i <= 0x28ff { continue; }; + let char = match char::from_u32(i) { + Some(c) => c, + // Continue for invalid characters + None => continue, + }; + binary_braillie_char_to_index(char, EncodeOrientation::Rows) + .expect_err("Expected error for invalid char"); + } + } + + #[test] + fn check_characters_in_range() { + for _char in 0x2800..0x28ff { + let char = char::from_u32(_char).unwrap(); + binary_braillie_char_to_index(char, EncodeOrientation::Rows) + .expect("Expected no error"); + } + } +} + +#[cfg(test)] +mod test_lib_all { + use crate::{ + binary_braillie_char_to_index, + binary_braillie_to_bytes, + index_to_braillie_char, + to_binary_braillie, + EncodeOrientation + }; + + #[test] + fn check_coding_multiple() { + for i in 0x00..0xff { + let mut as_char = index_to_braillie_char( + i, + EncodeOrientation::Rows + ); + for _ in 0..10 { + let as_index = binary_braillie_char_to_index( + as_char, + EncodeOrientation::Rows + ).unwrap(); + assert_eq!(as_index, i); + as_char = index_to_braillie_char(as_index, EncodeOrientation::Rows) + } + } + } + + #[test] + fn check_coding_multiple_bytes() { + for i in 0x00..0xff { + let mut as_str = index_to_braillie_char(i, EncodeOrientation::Rows).to_string(); + for _ in 0..10 { + let as_bytes = binary_braillie_to_bytes( + as_str, + EncodeOrientation::Rows + ).unwrap(); + assert_eq!(i, as_bytes[0]); + as_str = to_binary_braillie( + &as_bytes, + as_bytes.len(), + EncodeOrientation::Rows + ).to_string() + } + } + } +}