Skip to content

Commit 28ed457

Browse files
committed
Support Search and Replace in Hyperlinks
1 parent 47398b6 commit 28ed457

File tree

2 files changed

+85
-18
lines changed

2 files changed

+85
-18
lines changed

src/bin/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ enum Commands {
3030

3131
/// Search and replace in document text and tables
3232
Replace(ReplaceArgs),
33+
34+
/// Search and replace hyperlinks in the document
35+
ReplaceLinks(ReplaceArgs)
3336
}
3437

3538
#[derive(Args)]
@@ -86,6 +89,11 @@ fn real_main(args: Cli) -> i32 {
8689
XMLUtil::replace_xml(&temp_dir, &src_file,
8790
&replace_args.regex, &replace_args.replace,
8891
&replace_args.out_file);
92+
},
93+
Commands::ReplaceLinks(replace_args) => {
94+
XMLUtil::replace_attr(&temp_dir, &src_file,
95+
&replace_args.regex, &replace_args.replace,
96+
&replace_args.out_file);
8997
}
9098
}
9199

src/xml_util.rs

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,43 @@ use walkdir::WalkDir;
99

1010
use crate::zip_util::ZipUtil;
1111

12+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13+
enum Mode {
14+
Attribute,
15+
Value
16+
}
17+
1218
pub struct XMLUtil {
1319
}
1420

1521
impl XMLUtil {
1622
pub fn cat(dir: &str, src_file: &str) {
17-
Self::snr_xml(dir, src_file, None, None, None, None);
23+
Self::snr_xml(Mode::Value, dir, src_file, None, None, None, None);
1824
}
1925

2026
pub fn grep_xml(dir: &str, src_file: &str, pattern: &str) {
21-
Self::snr_xml(dir, src_file, None, Some(pattern), None, None);
27+
Self::snr_xml(Mode::Value, dir, src_file, None, Some(pattern), None, None);
2228
}
2329

2430
pub fn replace_xml(dir: &str, src_file: &str, pattern: &str, replace: &str, output_file: &Option<String>) {
25-
let out_file;
31+
let out_file = match output_file {
32+
Some(of) => of.as_str(),
33+
None => src_file
34+
};
2635

27-
if let Some(of) = output_file {
28-
out_file = of.as_str();
29-
} else {
30-
out_file = src_file;
31-
}
36+
Self::snr_xml(Mode::Value, dir, src_file, Some(vec!("word/document.xml")), Some(pattern), Some(replace), Some(out_file));
37+
}
38+
39+
pub fn replace_attr(dir: &str, src_file: &str, pattern: &str, replace: &str, output_file: &Option<String>) {
40+
let out_file = match output_file {
41+
Some(of) => of.as_str(),
42+
None => src_file
43+
};
3244

33-
Self::snr_xml(dir, src_file, Some(vec!("word/document.xml".to_string())), Some(pattern), Some(replace), Some(out_file));
45+
Self::snr_xml(Mode::Attribute, dir, src_file, Some(vec!("word/_rels/document.xml.rels")), Some(pattern), Some(replace), Some(out_file));
3446
}
3547

36-
fn snr_xml(dir: &str, src_file: &str, files: Option<Vec<String>>, pattern: Option<&str>, replace: Option<&str>, output_file: Option<&str>) {
48+
fn snr_xml(mode: Mode, dir: &str, src_file: &str, files: Option<Vec<&str>>, pattern: Option<&str>, replace: Option<&str>, output_file: Option<&str>) {
3749
let mut base_dir = dir.to_owned();
3850
if !dir.ends_with("/") {
3951
base_dir.push('/');
@@ -47,16 +59,20 @@ impl XMLUtil {
4759
}
4860

4961
for entry in WalkDir::new(dir).into_iter().filter_map(|e| e.ok()) {
50-
if entry.file_type().is_file() && entry.file_name().to_string_lossy().ends_with(".xml") {
62+
if entry.file_type().is_file() {
5163
let sub_path = Self::get_sub_path(entry.path(), &base_dir);
5264

5365
if let Some(file_list) = &files {
54-
if !file_list.contains(&sub_path) {
66+
if !file_list.contains(&sub_path.as_str()) {
67+
continue;
68+
}
69+
} else {
70+
if !(sub_path.ends_with(".xml")) {
5571
continue;
5672
}
5773
}
5874

59-
Self::snr_xml_file(entry.path(), &regex, &replace, &base_dir, src_file);
75+
Self::snr_xml_file(mode, entry.path(), &regex, &replace, src_file);
6076
}
6177
}
6278

@@ -65,7 +81,7 @@ impl XMLUtil {
6581
}
6682
}
6783

68-
fn snr_xml_file(path: &Path, regex: &Option<Regex>, replace: &Option<&str>, base_dir: &str, src_file: &str) {
84+
fn snr_xml_file(mode: Mode, path: &Path, regex: &Option<Regex>, replace: &Option<&str>, src_file: &str) {
6985
// detect BOM (Byte Order Mark)
7086
let bom = Self::get_bom(path);
7187
let f = File::open(path).unwrap(); // TODO
@@ -82,20 +98,63 @@ impl XMLUtil {
8298

8399
match dom_res {
84100
Ok(dom) => {
85-
if Self::snr_xml_node(&dom, regex, replace, path, base_dir, src_file) {
101+
let changed = match mode {
102+
Mode::Attribute =>
103+
Self::snr_xml_attribute(&dom, regex, replace, src_file),
104+
Mode::Value =>
105+
Self::snr_xml_node(&dom, regex, replace, src_file)
106+
};
107+
108+
if changed {
86109
std::fs::write(path, dom.to_string()).unwrap();
87110
}
88111
},
89112
Err(e) => println!("Problem with XML file {}: {}", path.display(), e)
90113
}
91114
}
92115

93-
fn snr_xml_node(node: &RefNode, regex: &Option<Regex>, replace: &Option<&str>, path: &Path, base_dir: &str, src_file: &str)
116+
fn snr_xml_attribute(node: &RefNode, regex: &Option<Regex>, replace: &Option<&str>, src_file: &str)
117+
-> bool {
118+
let mut changed = false;
119+
120+
for n in node.child_nodes() {
121+
for (_, mut attr) in n.attributes() {
122+
// let v = av.value();
123+
// println!("Name: {} = {:?}", an, v);
124+
if let Some(v) = attr.value() {
125+
if v.len() == 0 {
126+
continue;
127+
}
128+
129+
match regex {
130+
Some(r) => {
131+
if r.is_match(&v) {
132+
println!("{}: {}={}", src_file, attr.node_name(), v);
133+
if let Some(repl) = replace {
134+
let res = r.replace_all(&v, *repl);
135+
let _ = attr.set_value(&res);
136+
changed = true;
137+
}
138+
}
139+
},
140+
None => {
141+
println!("{}: {}={}", src_file, attr.node_name(), v);
142+
}
143+
}
144+
}
145+
}
146+
changed |= Self::snr_xml_attribute(&n, regex, replace, src_file);
147+
}
148+
149+
changed
150+
}
151+
152+
fn snr_xml_node(node: &RefNode, regex: &Option<Regex>, replace: &Option<&str>, src_file: &str)
94153
-> bool {
95154
let mut changed = false;
96155

97156
for mut n in node.child_nodes() {
98-
if let Option::Some(v) = n.node_value() {
157+
if let Some(v) = n.node_value() {
99158
if v.len() == 0 {
100159
continue;
101160
}
@@ -116,7 +175,7 @@ impl XMLUtil {
116175
}
117176
}
118177
}
119-
changed |= Self::snr_xml_node(&n, regex, replace, path, base_dir, src_file);
178+
changed |= Self::snr_xml_node(&n, regex, replace, src_file);
120179
}
121180

122181
changed

0 commit comments

Comments
 (0)