diff options
Diffstat (limited to 'clippy_dev/src/lib.rs')
| -rw-r--r-- | clippy_dev/src/lib.rs | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index d4b74f55717..1c303d180d2 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -101,6 +101,88 @@ fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> { .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) } +/// Replace a region in a file delimited by two lines matching regexes. +/// +/// `path` is the relative path to the file on which you want to perform the replacement. +/// +/// See `replace_region_in_text` for documentation of the other options. +pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_start: bool, replacements: F) where F: Fn() -> Vec<String> { + let mut f = fs::File::open(path).expect(&format!("File not found: {}", path)); + let mut contents = String::new(); + f.read_to_string(&mut contents).expect("Something went wrong reading the file"); + let replaced = replace_region_in_text(&contents, start, end, replace_start, replacements); + + let mut f = fs::File::create(path).expect(&format!("File not found: {}", path)); + f.write_all(replaced.as_bytes()).expect("Unable to write file"); + // Ensure we write the changes with a trailing newline so that + // the file has the proper line endings. + f.write(b"\n").expect("Unable to write file"); +} + +/// Replace a region in a text delimited by two lines matching regexes. +/// +/// * `text` is the input text on which you want to perform the replacement +/// * `start` is a `&str` that describes the delimiter line before the region you want to replace. +/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. +/// * `end` is a `&str` that describes the delimiter line until where the replacement should +/// happen. As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. +/// * If `replace_start` is true, the `start` delimiter line is replaced as well. +/// The `end` delimiter line is never replaced. +/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text. +/// +/// If you want to perform the replacement on files instead of already parsed text, +/// use `replace_region_in_file`. +/// +/// # Example +/// +/// ``` +/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; +/// let result = clippy_dev::replace_region_in_text( +/// the_text, +/// r#"replace_start"#, +/// r#"replace_end"#, +/// false, +/// || { +/// vec!["a different".to_string(), "text".to_string()] +/// } +/// ); +/// assert_eq!("replace_start\na different\ntext\nreplace_end", result); +/// ``` +pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> String where F: Fn() -> Vec<String> { + let lines = text.lines(); + let mut in_old_region = false; + let mut found = false; + let mut new_lines = vec![]; + let start = Regex::new(start).unwrap(); + let end = Regex::new(end).unwrap(); + + for line in lines { + if in_old_region { + if end.is_match(&line) { + in_old_region = false; + new_lines.extend(replacements()); + new_lines.push(line.to_string()); + } + } else if start.is_match(&line) { + if !replace_start { + new_lines.push(line.to_string()); + } + in_old_region = true; + found = true; + } else { + new_lines.push(line.to_string()); + } + } + + if !found { + // This happens if the provided regex in `clippy_dev/src/main.rs` is not found in the + // given text or file. Most likely this is an error on the programmer's side and the Regex + // is incorrect. + println!("regex {:?} not found. You may have to update it.", start); + } + new_lines.join("\n") +} + #[test] fn test_parse_contents() { let result: Vec<Lint> = parse_contents( @@ -141,6 +223,43 @@ declare_deprecated_lint! { } #[test] +fn test_replace_region() { + let text = r#" +abc +123 +789 +def +ghi"#; + let expected = r#" +abc +hello world +def +ghi"#; + let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || { + vec!["hello world".to_string()] + }); + assert_eq!(expected, result); +} + +#[test] +fn test_replace_region_with_start() { + let text = r#" +abc +123 +789 +def +ghi"#; + let expected = r#" +hello world +def +ghi"#; + let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || { + vec!["hello world".to_string()] + }); + assert_eq!(expected, result); +} + +#[test] fn test_usable_lints() { let lints = vec