Move backslash escaping to separate crate

This commit is contained in:
Ryan Geary 2020-06-17 20:50:49 -04:00
parent 767b9303f2
commit 2ee77aa848
5 changed files with 14 additions and 138 deletions

7
Cargo.lock generated
View file

@ -29,6 +29,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "backslash"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35a89ea09f2c7f3c81711c0db7d389d86a9d66fa15a7067e6fd6dbef863ef786"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.2.1"
@ -39,6 +45,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
name = "choose" name = "choose"
version = "1.2.0" version = "1.2.0"
dependencies = [ dependencies = [
"backslash",
"lazy_static", "lazy_static",
"regex", "regex",
"structopt", "structopt",

View file

@ -19,3 +19,4 @@ exclude = [
structopt = "0.3" structopt = "0.3"
regex = "1" regex = "1"
lazy_static = "1" lazy_static = "1"
backslash = "0"

View file

@ -1,137 +1 @@
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'));
}
}
}

View file

@ -2,7 +2,6 @@ use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
use crate::choice::Choice; use crate::choice::Choice;
use crate::escape;
use crate::parse; use crate::parse;
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
@ -38,7 +37,7 @@ pub struct Opt {
pub one_indexed: bool, pub one_indexed: bool,
/// Specify output field separator /// Specify output field separator
#[structopt(short, long, parse(from_str = escape::process_escapes))] #[structopt(short, long, parse(from_str = parse::output_field_separator))]
pub output_field_separator: Option<String>, 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 /// Fields to print. Either a, a:b, a..b, or a..=b, where a and b are integers. The beginning

View file

@ -1,3 +1,4 @@
use backslash::escape_ascii;
use regex::Regex; use regex::Regex;
use crate::choice::{Choice, ChoiceKind}; use crate::choice::{Choice, ChoiceKind};
@ -60,6 +61,10 @@ pub fn choice(src: &str) -> Result<Choice, ParseError> {
return Ok(Choice::new(start, end, kind)); return Ok(Choice::new(start, end, kind));
} }
pub fn output_field_separator(src: &str) -> String {
escape_ascii(src).unwrap()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::parse; use crate::parse;