mediawiki_parser/
transformations.rs

1//! Functions and types for source tree transformations.
2
3use crate::ast::*;
4use crate::error::TransformationError;
5
6/// Transformation result type
7pub type TResult = Result<Element, TransformationError>;
8
9/// Result type for a list of transformed elements.
10pub type TListResult = Result<Vec<Element>, TransformationError>;
11
12/// Signature of an in-place transformation function
13pub type TFuncInplace<S> = Fn(Element, S) -> TResult;
14
15/// Signature of a cloning transformation function
16pub type TFunc<S> = Fn(&Element, &[&Element], S) -> TResult;
17
18/// Apply a given transformation function to a list of elements, without mutating the original.
19pub fn apply_func_clone<S: Copy>(
20    func: &TFunc<S>,
21    content: &[Element],
22    path: &[&Element],
23    settings: S,
24) -> TListResult {
25    let mut result = vec![];
26    for child in content {
27        result.push(func(child, path, settings)?);
28    }
29    Ok(result)
30}
31
32/// Apply a given transformation to every item in a list, consuming this list.
33pub fn apply_func_drain<S: Copy>(
34    func: &TFuncInplace<S>,
35    content: &mut Vec<Element>,
36    settings: S,
37) -> TListResult {
38    let mut result = vec![];
39    for child in content.drain(..) {
40        result.push(func(child, settings)?);
41    }
42    Ok(result)
43}
44
45/// Recursively apply a transformation function `func` to all children of element `root`.
46pub fn recurse_inplace<S: Copy>(func: &TFuncInplace<S>, root: Element, settings: S) -> TResult {
47    recurse_inplace_template(func, root, settings, &apply_func_drain)
48}
49
50/// Recursively apply  a function `content_func` to the children list of a node.
51pub fn recurse_inplace_template<S: Copy>(
52    func: &TFuncInplace<S>,
53    mut root: Element,
54    settings: S,
55    content_func: &Fn(&TFuncInplace<S>, &mut Vec<Element>, S) -> TListResult,
56) -> TResult {
57    match root {
58        Element::Document(ref mut e) => {
59            let mut temp = content_func(func, &mut e.content, settings)?;
60            e.content.append(&mut temp);
61        }
62        Element::Formatted(ref mut e) => {
63            let mut temp = content_func(func, &mut e.content, settings)?;
64            e.content.append(&mut temp);
65        }
66        Element::Paragraph(ref mut e) => {
67            let mut temp = content_func(func, &mut e.content, settings)?;
68            e.content.append(&mut temp);
69        }
70        Element::ListItem(ref mut e) => {
71            let mut temp = content_func(func, &mut e.content, settings)?;
72            e.content.append(&mut temp);
73        }
74        Element::List(ref mut e) => {
75            let mut temp = content_func(func, &mut e.content, settings)?;
76            e.content.append(&mut temp);
77        }
78        Element::TableCell(ref mut e) => {
79            let mut temp = content_func(func, &mut e.content, settings)?;
80            e.content.append(&mut temp);
81        }
82        Element::HtmlTag(ref mut e) => {
83            let mut temp = content_func(func, &mut e.content, settings)?;
84            e.content.append(&mut temp);
85        }
86        Element::Gallery(ref mut e) => {
87            let mut temp = content_func(func, &mut e.content, settings)?;
88            e.content.append(&mut temp);
89        }
90        Element::Heading(ref mut e) => {
91            let mut content = content_func(func, &mut e.content, settings)?;
92            let mut caption = content_func(func, &mut e.caption, settings)?;
93            e.caption.append(&mut caption);
94            e.content.append(&mut content);
95        }
96        Element::Template(ref mut e) => {
97            let mut name = content_func(func, &mut e.name, settings)?;
98            let mut content = content_func(func, &mut e.content, settings)?;
99            e.name.append(&mut name);
100            e.content.append(&mut content);
101        }
102        Element::TemplateArgument(ref mut e) => {
103            let mut value = content_func(func, &mut e.value, settings)?;
104            e.value.append(&mut value);
105        }
106        Element::InternalReference(ref mut e) => {
107            let mut target = content_func(func, &mut e.target, settings)?;
108            let mut caption = content_func(func, &mut e.caption, settings)?;
109
110            let mut new_options = vec![];
111            for mut option in e.options.drain(..) {
112                new_options.push(content_func(func, &mut option, settings)?);
113            }
114
115            e.target.append(&mut target);
116            e.options.append(&mut new_options);
117            e.caption.append(&mut caption);
118        }
119        Element::ExternalReference(ref mut e) => {
120            let mut caption = content_func(func, &mut e.caption, settings)?;
121            e.caption.append(&mut caption);
122        }
123        Element::Table(ref mut e) => {
124            let mut caption = content_func(func, &mut e.caption, settings)?;
125            let mut rows = content_func(func, &mut e.rows, settings)?;
126            e.caption.append(&mut caption);
127            e.rows.append(&mut rows);
128        }
129        Element::TableRow(ref mut e) => {
130            let mut cells = content_func(func, &mut e.cells, settings)?;
131            e.cells.append(&mut cells);
132        }
133        Element::Text(_) | Element::Comment(_) | Element::Error(_) => (),
134    };
135    Ok(root)
136}
137
138/// Recursively apply a transformation function `func` to all children of element `root`, cloning the input.
139pub fn recurse_clone<S: Copy>(
140    func: &TFunc<S>,
141    root: &Element,
142    path: &[&Element],
143    settings: S,
144) -> TResult {
145    recurse_clone_template(func, root, path, settings, &apply_func_clone)
146}
147
148/// Recursively apply  a function `content_func` to the children list of a node, cloning the input.
149pub fn recurse_clone_template<S: Copy>(
150    func: &TFunc<S>,
151    root: &Element,
152    path: &[&Element],
153    settings: S,
154    content_func: &Fn(&TFunc<S>, &[Element], &[&Element], S) -> TListResult,
155) -> TResult {
156    let mut path = path.to_owned();
157    path.push(root);
158    let new = match *root {
159        Element::Document(ref e) => Element::Document(Document {
160            position: e.position.clone(),
161            content: content_func(func, &e.content, &path, settings)?,
162        }),
163        Element::Heading(ref e) => Element::Heading(Heading {
164            position: e.position.clone(),
165            depth: e.depth,
166            caption: content_func(func, &e.caption, &path, settings)?,
167            content: content_func(func, &e.content, &path, settings)?,
168        }),
169        Element::Formatted(ref e) => Element::Formatted(Formatted {
170            position: e.position.clone(),
171            markup: e.markup,
172            content: content_func(func, &e.content, &path, settings)?,
173        }),
174        Element::Paragraph(ref e) => Element::Paragraph(Paragraph {
175            position: e.position.clone(),
176            content: content_func(func, &e.content, &path, settings)?,
177        }),
178        Element::Template(ref e) => Element::Template(Template {
179            position: e.position.clone(),
180            name: content_func(func, &e.name, &path, settings)?,
181            content: content_func(func, &e.content, &path, settings)?,
182        }),
183        Element::TemplateArgument(ref e) => Element::TemplateArgument(TemplateArgument {
184            position: e.position.clone(),
185            name: e.name.clone(),
186            value: content_func(func, &e.value, &path, settings)?,
187        }),
188        Element::InternalReference(ref e) => {
189            let mut new_options = vec![];
190            for option in &e.options {
191                new_options.push(content_func(func, &option, &path, settings)?);
192            }
193
194            Element::InternalReference(InternalReference {
195                position: e.position.clone(),
196                target: content_func(func, &e.target, &path, settings)?,
197                options: new_options,
198                caption: content_func(func, &e.caption, &path, settings)?,
199            })
200        }
201        Element::ExternalReference(ref e) => Element::ExternalReference(ExternalReference {
202            position: e.position.clone(),
203            target: e.target.clone(),
204            caption: content_func(func, &e.caption, &path, settings)?,
205        }),
206        Element::ListItem(ref e) => Element::ListItem(ListItem {
207            position: e.position.clone(),
208            depth: e.depth,
209            kind: e.kind,
210            content: content_func(func, &e.content, &path, settings)?,
211        }),
212        Element::List(ref e) => Element::List(List {
213            position: e.position.clone(),
214            content: content_func(func, &e.content, &path, settings)?,
215        }),
216        Element::Table(ref e) => Element::Table(Table {
217            position: e.position.clone(),
218            attributes: e.attributes.clone(),
219            caption: content_func(func, &e.caption, &path, settings)?,
220            caption_attributes: e.caption_attributes.clone(),
221            rows: content_func(func, &e.rows, &path, settings)?,
222        }),
223        Element::TableRow(ref e) => Element::TableRow(TableRow {
224            position: e.position.clone(),
225            attributes: e.attributes.clone(),
226            cells: content_func(func, &e.cells, &path, settings)?,
227        }),
228        Element::TableCell(ref e) => Element::TableCell(TableCell {
229            position: e.position.clone(),
230            header: e.header,
231            attributes: e.attributes.clone(),
232            content: content_func(func, &e.content, &path, settings)?,
233        }),
234        Element::Comment(ref e) => Element::Comment(e.clone()),
235        Element::Text(ref e) => Element::Text(e.clone()),
236        Element::Error(ref e) => Element::Error(e.clone()),
237        Element::HtmlTag(ref e) => Element::HtmlTag(HtmlTag {
238            position: e.position.clone(),
239            name: e.name.clone(),
240            attributes: e.attributes.clone(),
241            content: content_func(func, &e.content, &path, settings)?,
242        }),
243        Element::Gallery(ref e) => Element::Gallery(Gallery {
244            position: e.position.clone(),
245            attributes: e.attributes.clone(),
246            content: content_func(func, &e.content, &path, settings)?,
247        }),
248    };
249    path.pop();
250    Ok(new)
251}