rdx_transform/transforms/
strip_target.rs1use rdx_ast::*;
2
3use crate::Transform;
4
5pub struct StripTarget {
36 pub target: String,
38}
39
40fn should_strip(comp: &ComponentNode, current_target: &str) -> bool {
42 match comp.name.as_str() {
44 "WebOnly" => return current_target != "web",
45 "PrintOnly" => return current_target != "print",
46 _ => {}
47 }
48
49 let target_attr = comp.attributes.iter().find_map(|a| {
51 if a.name == "target" {
52 if let AttributeValue::String(s) = &a.value {
53 Some(s.as_str())
54 } else {
55 None
56 }
57 } else {
58 None
59 }
60 });
61
62 match target_attr {
63 Some("all") | None => false,
64 Some(t) => t != current_target,
65 }
66}
67
68impl Transform for StripTarget {
69 fn name(&self) -> &str {
70 "strip-target"
71 }
72
73 fn transform(&self, root: &mut Root, _source: &str) {
74 strip_nodes(&mut root.children, &self.target);
75 }
76}
77
78fn strip_nodes(nodes: &mut Vec<Node>, current_target: &str) {
79 nodes.retain_mut(|node| {
80 if let Node::Component(comp) = &*node
81 && should_strip(comp, current_target)
82 {
83 return false; }
85 true
86 });
87
88 for node in nodes.iter_mut() {
90 if let Some(children) = node.children_mut() {
91 strip_nodes(children, current_target);
92 }
93 }
94}
95
96#[cfg(test)]
101mod tests {
102 use super::*;
103 use rdx_parser::parse;
104
105 #[test]
106 fn web_mode_strips_print_only() {
107 let mut root = parse(
108 "<PrintOnly>\nprint content\n</PrintOnly>\n\
109 <WebOnly>\nweb content\n</WebOnly>\n",
110 );
111 StripTarget {
112 target: "web".into(),
113 }
114 .transform(&mut root, "");
115 assert_eq!(root.children.len(), 1, "Only WebOnly should remain");
116 match &root.children[0] {
117 Node::Component(c) => assert_eq!(c.name, "WebOnly"),
118 other => panic!("Expected WebOnly, got {:?}", other),
119 }
120 }
121
122 #[test]
123 fn print_mode_strips_web_only() {
124 let mut root = parse(
125 "<PrintOnly>\nprint content\n</PrintOnly>\n\
126 <WebOnly>\nweb content\n</WebOnly>\n",
127 );
128 StripTarget {
129 target: "print".into(),
130 }
131 .transform(&mut root, "");
132 assert_eq!(root.children.len(), 1, "Only PrintOnly should remain");
133 match &root.children[0] {
134 Node::Component(c) => assert_eq!(c.name, "PrintOnly"),
135 other => panic!("Expected PrintOnly, got {:?}", other),
136 }
137 }
138
139 #[test]
140 fn target_attribute_web_stripped_in_print() {
141 let mut root = parse("<Note target=\"web\">\ncontent\n</Note>\n");
142 StripTarget {
143 target: "print".into(),
144 }
145 .transform(&mut root, "");
146 assert!(
147 root.children.is_empty(),
148 "web-targeted Note should be removed in print mode"
149 );
150 }
151
152 #[test]
153 fn target_attribute_print_kept_in_print() {
154 let mut root = parse("<Note target=\"print\">\ncontent\n</Note>\n");
155 StripTarget {
156 target: "print".into(),
157 }
158 .transform(&mut root, "");
159 assert_eq!(root.children.len(), 1);
160 }
161
162 #[test]
163 fn target_all_always_kept() {
164 let mut root = parse("<Note target=\"all\">\ncontent\n</Note>\n");
165 StripTarget {
166 target: "web".into(),
167 }
168 .transform(&mut root, "");
169 assert_eq!(root.children.len(), 1);
170 StripTarget {
171 target: "print".into(),
172 }
173 .transform(&mut root, "");
174 assert_eq!(root.children.len(), 1);
175 }
176
177 #[test]
178 fn no_target_attribute_always_kept() {
179 let mut root = parse("<Notice>\ncontent\n</Notice>\n");
180 StripTarget {
181 target: "web".into(),
182 }
183 .transform(&mut root, "");
184 assert_eq!(root.children.len(), 1);
185 }
186
187 #[test]
188 fn nested_strip_works() {
189 let mut root = parse(
191 "<Notice>\n\
192 <PrintOnly>\nprint\n</PrintOnly>\n\
193 Keep this.\n\
194 </Notice>\n",
195 );
196 StripTarget {
197 target: "web".into(),
198 }
199 .transform(&mut root, "");
200 assert_eq!(root.children.len(), 1);
202 match &root.children[0] {
203 Node::Component(c) => {
204 let has_print_only = c
206 .children
207 .iter()
208 .any(|n| matches!(n, Node::Component(inner) if inner.name == "PrintOnly"));
209 assert!(
210 !has_print_only,
211 "PrintOnly should be stripped from nested position"
212 );
213 }
214 other => panic!("Expected Notice, got {:?}", other),
215 }
216 }
217
218 #[test]
219 fn both_kept_when_target_matches() {
220 let mut root = parse(
221 "<WebOnly>\nweb\n</WebOnly>\n\
222 <PrintOnly>\nprint\n</PrintOnly>\n",
223 );
224 StripTarget {
227 target: "pdf".into(),
228 }
229 .transform(&mut root, "");
230 assert!(
231 root.children.is_empty(),
232 "Both WebOnly and PrintOnly should be stripped for unknown target"
233 );
234 }
235}