easydoc_writer/util/
xml_insert.rs1#[must_use]
24pub fn insert_after_nth(xml: &str, pattern: &str, n: usize, content: &str) -> String {
25 let mut offset = 0usize;
26 let mut remaining = xml;
27 let mut count = 0usize;
28
29 while let Some(pos) = remaining.find(pattern) {
30 if count == n {
31 let insert_at = offset + pos + pattern.len();
32 let mut result = String::with_capacity(xml.len() + content.len());
33 result.push_str(&xml[..insert_at]);
34 result.push_str(content);
35 result.push_str(&xml[insert_at..]);
36 return result;
37 }
38 count += 1;
39 let advance = pos + pattern.len();
40 offset += advance;
41 remaining = &remaining[advance..];
42 }
43
44 xml.to_string()
45}
46
47#[must_use]
65pub fn insert_many_after_nth(xml: &str, pattern: &str, contents: &[String]) -> String {
66 if contents.is_empty() {
67 return xml.to_owned();
68 }
69
70 let mut result =
71 String::with_capacity(xml.len() + contents.iter().map(String::len).sum::<usize>());
72 let mut search_from = 0usize;
73 let mut occurrence = 0usize;
74
75 while occurrence < contents.len() {
76 let Some(rel) = xml[search_from..].find(pattern) else {
77 result.push_str(&xml[search_from..]);
79 return result;
80 };
81 let abs = search_from + rel;
82 result.push_str(&xml[search_from..abs + pattern.len()]);
83 result.push_str(&contents[occurrence]);
84 search_from = abs + pattern.len();
85 occurrence += 1;
86 }
87
88 result.push_str(&xml[search_from..]);
89 result
90}
91
92#[must_use]
102pub fn insert_no_wrap(xml: &str, cell_index: usize) -> String {
103 if xml.matches("<w:tcW").count() > cell_index {
105 return insert_after_nth(xml, "<w:tcW", cell_index, "<w:noWrap/>");
106 }
107 if xml.matches("<w:tcPr").count() > cell_index {
109 return insert_after_nth(xml, "<w:tcPr", cell_index, "<w:noWrap/>");
110 }
111 xml.to_string()
112}
113
114#[must_use]
121pub fn insert_num_fmt(xml: &str, cell_index: usize, format: &str) -> String {
122 let pattern = "<w:pPr><w:rPr";
123 if xml.matches(pattern).count() > cell_index {
124 let num_fmt = format!("<w:numFmt w:val=\"{format}\"/>");
125 return insert_after_nth(xml, pattern, cell_index, &num_fmt);
126 }
127 xml.to_string()
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn insert_after_nth_first() {
136 let out = insert_after_nth("AAxBBxAA", "AA", 0, "!");
137 assert_eq!(out, "AA!xBBxAA");
138 }
139
140 #[test]
141 fn insert_after_nth_second() {
142 let out = insert_after_nth("AAxBBxAA", "AA", 1, "!");
144 assert_eq!(out, "AAxBBxAA!");
145 }
146
147 #[test]
148 fn insert_after_nth_not_found() {
149 let out = insert_after_nth("xBBx", "AA", 0, "!");
150 assert_eq!(out, "xBBx");
151 }
152
153 #[test]
154 fn insert_after_nth_out_of_bounds() {
155 let out = insert_after_nth("AA", "AA", 5, "!");
156 assert_eq!(out, "AA");
157 }
158
159 #[test]
160 fn insert_many_basic() {
161 let out = insert_many_after_nth("<a/><b/><a/><b/><a/>", "<b/>", &["!".into(), "?".into()]);
162 assert_eq!(out, "<a/><b/>!<a/><b/>?<a/>");
163 }
164
165 #[test]
166 fn insert_many_empty_contents_returns_original() {
167 let xml = "<a/><b/><a/>";
168 let out = insert_many_after_nth(xml, "<b/>", &[]);
169 assert_eq!(out, xml);
170 }
171
172 #[test]
173 fn insert_many_more_contents_than_matches() {
174 let out = insert_many_after_nth("<a/><b/>", "<b/>", &["!".into(), "?".into()]);
176 assert_eq!(out, "<a/><b/>!");
177 }
178
179 #[test]
180 fn insert_many_no_match_returns_original() {
181 let xml = "<a/><a/>";
182 let out = insert_many_after_nth(xml, "<b/>", &["!".into()]);
183 assert_eq!(out, xml);
184 }
185
186 #[test]
187 fn insert_many_preserves_remaining_tail() {
188 let out = insert_many_after_nth("<a/><b/>TAIL", "<b/>", &["!".into()]);
190 assert_eq!(out, "<a/><b/>!TAIL");
191 }
192}