From aeda08c5b6b3965fe3c3ea0091d52e99c16b83ba Mon Sep 17 00:00:00 2001 From: Kazuya Takei Date: Wed, 10 Apr 2024 02:41:03 +0900 Subject: [PATCH] feat: Implement replace by regex --- Cargo.lock | 5 ++-- Cargo.toml | 1 + src/workspace.rs | 25 ++++++++++++----- src/writer.rs | 28 ++++++++++++++++--- .../return-0/multi-line-regex/after/.age.toml | 12 ++++++++ .../multi-line-regex/after/example.txt | 4 +++ .../multi-line-regex/before/.age.toml | 12 ++++++++ .../multi-line-regex/before/example.txt | 4 +++ tests/test_update.py | 2 ++ 9 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 tests/return-0/multi-line-regex/after/.age.toml create mode 100644 tests/return-0/multi-line-regex/after/example.txt create mode 100644 tests/return-0/multi-line-regex/before/.age.toml create mode 100644 tests/return-0/multi-line-regex/before/example.txt diff --git a/Cargo.lock b/Cargo.lock index 9327b9a..f391f36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,7 @@ dependencies = [ "clap", "env_logger", "log", + "regex", "semver", "serde", "tera", @@ -657,9 +658,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 8ae2233..292ce6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ chrono = "0.4.35" clap = { version = "4.5.2", features = ["derive"] } env_logger = "0.11.3" log = "0.4.21" +regex = "1.10.4" semver = { version = "1.0.22", features = ["serde"] } serde = { version = "1.0.197", features = ["derive"] } tera = { version = "1.19.1", features = ["builtins"] } diff --git a/src/workspace.rs b/src/workspace.rs index 66e69f3..505a883 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -1,3 +1,4 @@ +use core::panic; use std::path::PathBuf; use anyhow::{anyhow, Result}; @@ -6,7 +7,7 @@ use log::{debug, warn}; use semver::Version; use crate::config::{resolve_config, Config, ConfigDocument}; -use crate::writer::{WriteRule, Writer}; +use crate::writer::{SearchPattern, WriteRule, Writer}; /** * CLI workspace. @@ -53,15 +54,25 @@ impl Workspace { fn init_writer(&self, ctx: &Context) -> Writer { let mut writer = Writer::new(&ctx.for_tera()); for f in &self.config.files { - if f.search.is_none() { - warn!("Search is required untl."); + if f.search.is_none() && f.regex.is_none() { + warn!("Required either 'search' or 'regex'."); continue; } - let rule = WriteRule { - search: f.search.as_ref().unwrap().to_string(), - replace: f.replace.to_string(), + let search = 'pattern: { + if f.search.is_some() { + break 'pattern SearchPattern::String(f.search.as_ref().unwrap().to_string()); + } else if f.regex.is_some() { + break 'pattern SearchPattern::Regex(f.regex.as_ref().unwrap().to_string()); + } + panic!() }; - writer.add_target(&f.path, rule) + writer.add_target( + &f.path, + WriteRule { + search, + replace: f.replace.to_string(), + }, + ); } writer } diff --git a/src/writer.rs b/src/writer.rs index bf0b12a..15aa8df 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -4,6 +4,7 @@ use std::io::prelude::*; use std::path::{Path, PathBuf}; use anyhow::Result; +use regex::Regex; use tera::{Context, Tera}; /** @@ -76,14 +77,33 @@ impl WriteTarget { */ pub struct WriteRule { /** Search target that is rendered by Tera. */ - pub search: String, + pub search: SearchPattern, /** Replacement content that is rendered by Tera. */ pub replace: String, } +pub enum SearchPattern { + String(String), + Regex(String), +} + impl WriteRule { fn update(&self, target: String, context: &Context) -> String { - let search = Tera::one_off(&self.search.to_string(), context, true).unwrap(); + match &self.search { + SearchPattern::String(s) => self.update_string(target, context, s), + SearchPattern::Regex(s) => self.update_regex(target, context, s), + } + } + + fn update_regex(&self, target: String, context: &Context, search: &String) -> String { + let search = Tera::one_off(search, context, true).unwrap(); + let search = Regex::new(&search).unwrap(); + let replace = Tera::one_off(&self.replace.to_string(), context, true).unwrap(); + search.replace(&target, replace).to_string() + } + + fn update_string(&self, target: String, context: &Context, search: &String) -> String { + let search = Tera::one_off(&search, context, true).unwrap(); let replace = Tera::one_off(&self.replace.to_string(), context, true).unwrap(); let lines = search.split('\n').count(); @@ -132,14 +152,14 @@ mod tests { writer.add_target( &filepath, WriteRule { - search: String::from("target-1"), + search: SearchPattern::String(String::from("target-1")), replace: String::from("target-2"), }, ); writer.add_target( &filepath, WriteRule { - search: String::from("target-2"), + search: SearchPattern::String(String::from("target-2")), replace: String::from("replace-2"), }, ); diff --git a/tests/return-0/multi-line-regex/after/.age.toml b/tests/return-0/multi-line-regex/after/.age.toml new file mode 100644 index 0000000..ffa9dd9 --- /dev/null +++ b/tests/return-0/multi-line-regex/after/.age.toml @@ -0,0 +1,12 @@ +current_version = "0.2.0" + +[[files]] +path = "example.txt" +regex = """ +version = '{{current_version}}' +(.+) +""" +replace = """ +version = '{{new_version}}' +world +""" diff --git a/tests/return-0/multi-line-regex/after/example.txt b/tests/return-0/multi-line-regex/after/example.txt new file mode 100644 index 0000000..6d379b6 --- /dev/null +++ b/tests/return-0/multi-line-regex/after/example.txt @@ -0,0 +1,4 @@ +version = '0.2.0' +world + +This line is kept. diff --git a/tests/return-0/multi-line-regex/before/.age.toml b/tests/return-0/multi-line-regex/before/.age.toml new file mode 100644 index 0000000..bb0a44d --- /dev/null +++ b/tests/return-0/multi-line-regex/before/.age.toml @@ -0,0 +1,12 @@ +current_version = "0.1.0" + +[[files]] +path = "example.txt" +regex = """ +version = '{{current_version}}' +(.+) +""" +replace = """ +version = '{{new_version}}' +world +""" diff --git a/tests/return-0/multi-line-regex/before/example.txt b/tests/return-0/multi-line-regex/before/example.txt new file mode 100644 index 0000000..648a597 --- /dev/null +++ b/tests/return-0/multi-line-regex/before/example.txt @@ -0,0 +1,4 @@ +version = '0.1.0' +hello + +This line is kept. diff --git a/tests/test_update.py b/tests/test_update.py index b189a3a..4d87225 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -36,6 +36,8 @@ def test_valid_env(cmd, env_path: Path, tmp_path: Path): """Run test caese on env having valid files.""" shutil.copytree(env_path / "before", tmp_path, dirs_exist_ok=True) proc: CompletedProcess = cmd("update", "0.2.0") + print(proc.stdout) + print(proc.stderr) assert proc.returncode == 0 diff = run(["diff", "--recursive", str(tmp_path), str(env_path / "after")]) assert diff.returncode == 0