mirror of
https://github.com/theryangeary/choose
synced 2024-09-20 06:22:07 +00:00
Add backslash escape parsing (#26)
This commit is contained in:
parent
0e422090d3
commit
448b12bb84
5 changed files with 149 additions and 5 deletions
|
@ -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 {
|
||||
|
|
137
src/escape.rs
Normal file
137
src/escape.rs
Normal file
|
@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ extern crate lazy_static;
|
|||
mod choice;
|
||||
mod config;
|
||||
mod errors;
|
||||
mod escape;
|
||||
mod opt;
|
||||
mod parse;
|
||||
mod parse_error;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue