Unescape replacement expression

This commit is contained in:
Greg 2019-01-02 22:52:09 -05:00
parent 9a8e8a5c2f
commit 20228b6fe3
5 changed files with 42 additions and 19 deletions

7
Cargo.lock generated
View file

@ -247,6 +247,7 @@ dependencies = [
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"unescape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -315,6 +316,11 @@ name = "ucd-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unescape"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-segmentation"
version = "1.2.1"
@ -408,6 +414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
"checksum unescape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e"
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"

View file

@ -17,6 +17,7 @@ structopt = "0.2.14"
atomic-write = "0.2.0"
rayon = "1.0.3"
regex-syntax = "0.6.4"
unescape = "0.1.0"
[profile.release]
opt-level = 3

View file

@ -48,7 +48,7 @@ pub(crate) fn run() -> Result<()> {
let source = Source::from(args.files);
let replacer = Replacer::new(
args.find,
&args.replace_with,
args.replace_with,
args.literal_mode,
args.flags
)?;

View file

@ -23,16 +23,16 @@ impl Source {
}
}
pub(crate) struct Replacer<'a> {
pub(crate) struct Replacer {
regex: Regex,
replace_with: &'a str,
replace_with: String,
is_literal: bool,
}
impl<'a> Replacer<'a> {
impl Replacer {
pub(crate) fn new(
look_for: String,
replace_with: &'a str,
replace_with: String,
is_literal: bool,
flags: Option<String>,
) -> Result<Self> {
@ -45,15 +45,21 @@ impl<'a> Replacer<'a> {
let mut regex = regex::RegexBuilder::new(&look_for);
regex.case_insensitive(
!is_literal && !utils::regex_case_sensitive(&look_for)
!is_literal && !utils::regex_case_sensitive(&look_for),
);
if let (Some(flags), false) = (flags, is_literal) {
for c in flags.chars() {
match c {
'c' => { regex.case_insensitive(false); },
'i' => { regex.case_insensitive(true); },
'm' => { regex.multi_line(true); },
'c' => {
regex.case_insensitive(false);
},
'i' => {
regex.case_insensitive(true);
},
'm' => {
regex.multi_line(true);
},
_ => {},
};
}
@ -61,7 +67,8 @@ impl<'a> Replacer<'a> {
return Ok(Replacer {
regex: regex.build()?,
replace_with,
replace_with: utils::unescape(&replace_with)
.unwrap_or_else(|| replace_with),
is_literal,
});
}
@ -74,13 +81,13 @@ impl<'a> Replacer<'a> {
if self.is_literal {
self.regex
.replace_all(&content, regex::NoExpand(self.replace_with))
.replace_all(&content, regex::NoExpand(&self.replace_with))
.to_string()
.into()
}
else {
self.regex
.replace_all(&content, self.replace_with)
.replace_all(&content, &*self.replace_with)
.to_string()
.into()
}
@ -143,44 +150,48 @@ mod tests {
#[test]
fn default_global() -> Result<()> {
let r = Replacer::new("a".to_string(), "b", false, None)?;
let r = Replacer::new("a".into(), "b".into(), false, None)?;
assert_eq!(r.replace("aaa"), "bbb");
Ok(())
}
#[test]
fn escaped_char_preservation() -> Result<()> {
let r = Replacer::new("a".to_string(), "b", false, None)?;
let r = Replacer::new("a".into(), "b".into(), false, None)?;
assert_eq!(r.replace("a\\n"), "b\\n");
Ok(())
}
#[test]
fn smart_case_insensitive() -> Result<()> {
let r = Replacer::new("abc".to_string(), "x", false, None)?;
let r = Replacer::new("abc".into(), "x".into(), false, None)?;
assert_eq!(r.replace("abcABCAbcabC"), "xxxx");
Ok(())
}
#[test]
fn smart_case_sensitive() -> Result<()> {
let r = Replacer::new("Abc".to_string(), "x", false, None)?;
let r = Replacer::new("Abc".into(), "x".into(), false, None)?;
assert_eq!(r.replace("abcABCAbcabC"), "abcABCxabC");
Ok(())
}
#[test]
fn no_smart_case_literals() -> Result<()> {
let r = Replacer::new("abc".to_string(), "x", true, None)?;
let r = Replacer::new("abc".into(), "x".into(), true, None)?;
assert_eq!(r.replace("abcABCAbcabC"), "xABCAbcabC");
Ok(())
}
#[test]
fn sanity_check_literal_replacements() -> Result<()> {
let r = Replacer::new("((special[]))".to_string(), "x", true, None)?;
let r = Replacer::new(
"((special[]))".to_string(),
"x".to_string(),
true,
None,
)?;
assert_eq!(r.replace("((special[]))y"), "xy");
Ok(())
}
}

View file

@ -1,5 +1,9 @@
pub(crate) type Result<T> = std::result::Result<T, crate::Error>;
pub(crate) fn unescape(s: &str) -> Option<String> {
unescape::unescape(s)
}
pub(crate) fn regex_case_sensitive(pattern: &str) -> bool {
use regex_syntax::Parser;
Parser::new()