summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Geary <rtgnj42@gmail.com>2020-06-17 19:06:28 -0400
committerRyan Geary <rtgnj42@gmail.com>2020-06-17 19:24:05 -0400
commit767b9303f218ae1c15f5dfbcc7b62c72983e5efb (patch)
tree8e0cbc9cac9898c35d97662b48fde9de24c81471
parent0e422090d33da2f320a77fd128554635ba7aacba (diff)
Add backslash escape parsing (#26)
-rw-r--r--src/choice.rs9
-rw-r--r--src/escape.rs137
-rw-r--r--src/main.rs1
-rw-r--r--src/opt.rs3
-rw-r--r--src/parse.rs4
5 files changed, 149 insertions, 5 deletions
diff --git a/src/choice.rs b/src/choice.rs
index 5e6e679..d7447fe 100644
--- a/src/choice.rs
+++ b/src/choice.rs
@@ -1115,6 +1115,15 @@ mod tests {
fn print_neg_4_to_2_one_indexed() {
test_fn(vec!["choose", "-4:2", "--one-indexed"], "a b c d", "a b");
}
+
+ #[test]
+ fn print_2_to_4_newline_ofs() {
+ test_fn(
+ vec!["choose", "2:4", "-o", r#"\n"#],
+ "a b c d e f",
+ "c\nd\ne",
+ );
+ }
}
mod is_reverse_range_tests {
diff --git a/src/escape.rs b/src/escape.rs
new file mode 100644
index 0000000..9098746
--- /dev/null
+++ b/src/escape.rs
@@ -0,0 +1,137 @@
+pub fn process_escapes(input: &str) -> String {
+ if input.len() < 1 {
+ return String::from(input);
+ }
+
+ let mut v = Vec::from(input);
+ for i in 0..(v.len() - 1) {
+ if v[i] == '\\' as u8 && is_escapable(v[i + 1] as char) {
+ v.remove(i);
+ v[i] = char_to_escape_sequence(v[i] as char) as u8;
+ }
+ }
+ String::from_utf8(v).unwrap()
+}
+
+fn char_to_escape_sequence(chr: char) -> char {
+ match chr {
+ 'n' => '\n',
+ 't' => '\t',
+ 'r' => '\r',
+ '\\' => '\\',
+ '0' => '\0',
+ _ => chr,
+ }
+}
+
+fn is_escapable(chr: char) -> bool {
+ match chr {
+ 'n' | 't' | 'r' | '\\' | '0' => true,
+ _ => false,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ mod test_process_escapes {
+ use super::*;
+
+ #[test]
+ fn test_newline() {
+ assert_eq!(
+ String::from("hello\nworld"),
+ process_escapes(r#"hello\nworld"#)
+ );
+ }
+
+ #[test]
+ fn test_carriage_return() {
+ assert_eq!(
+ String::from("hello\rworld"),
+ process_escapes(r#"hello\rworld"#)
+ );
+ }
+
+ #[test]
+ fn test_tab() {
+ assert_eq!(
+ String::from("hello\tworld"),
+ process_escapes(r#"hello\tworld"#)
+ );
+ }
+
+ #[test]
+ fn test_backslash() {
+ assert_eq!(
+ String::from("hello\\world"),
+ process_escapes(r#"hello\\world"#)
+ );
+ }
+
+ #[test]
+ fn test_null() {
+ assert_eq!(
+ String::from("hello\0world"),
+ process_escapes(r#"hello\0world"#)
+ );
+ }
+ }
+
+ mod test_char_to_escape_sequence {
+ use super::*;
+ #[test]
+ fn test_escape_n() {
+ assert_eq!('\n', char_to_escape_sequence('n'));
+ }
+
+ #[test]
+ fn test_escape_t() {
+ assert_eq!('\t', char_to_escape_sequence('t'));
+ }
+
+ #[test]
+ fn test_escape_r() {
+ assert_eq!('\r', char_to_escape_sequence('r'));
+ }
+
+ #[test]
+ fn test_escape_backslash() {
+ assert_eq!('\\', char_to_escape_sequence('\\'));
+ }
+
+ #[test]
+ fn test_escape_0() {
+ assert_eq!('\0', char_to_escape_sequence('0'));
+ }
+ }
+
+ mod is_escapable_tests {
+ use super::*;
+
+ #[test]
+ fn test_escape_n() {
+ assert!(is_escapable('n'));
+ }
+
+ #[test]
+ fn test_escape_t() {
+ assert!(is_escapable('t'));
+ }
+
+ #[test]
+ fn test_escape_r() {
+ assert!(is_escapable('r'));
+ }
+
+ #[test]
+ fn test_escape_backslash() {
+ assert!(is_escapable('\\'));
+ }
+
+ #[test]
+ fn test_escape_0() {
+ assert!(is_escapable('0'));
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 86bd108..4a8f1f1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,6 +9,7 @@ extern crate lazy_static;
mod choice;
mod config;
mod errors;
+mod escape;
mod opt;
mod parse;
mod parse_error;
diff --git a/src/opt.rs b/src/opt.rs
index 94f1236..1eb08ee 100644
--- a/src/opt.rs
+++ b/src/opt.rs
@@ -2,6 +2,7 @@ use std::path::PathBuf;
use structopt::StructOpt;
use crate::choice::Choice;
+use crate::escape;
use crate::parse;
#[derive(Debug, StructOpt)]
@@ -37,7 +38,7 @@ pub struct Opt {
pub one_indexed: bool,
/// Specify output field separator
- #[structopt(short, long, parse(from_str = parse::output_field_separator))]
+ #[structopt(short, long, parse(from_str = escape::process_escapes))]
pub output_field_separator: Option<String>,
/// Fields to print. Either a, a:b, a..b, or a..=b, where a and b are integers. The beginning
diff --git a/src/parse.rs b/src/parse.rs
index 7ff93cd..17b5bfa 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -60,10 +60,6 @@ pub fn choice(src: &str) -> Result<Choice, ParseError> {
return Ok(Choice::new(start, end, kind));
}
-pub fn output_field_separator(src: &str) -> String {
- String::from(src)
-}
-
#[cfg(test)]
mod tests {
use crate::parse;