markdown_ppp/ast_transform/
pipeline.rs1use super::transformer::Transformer;
27use crate::ast::*;
28
29pub struct TransformPipeline {
49 steps: Vec<Box<dyn FnOnce(Document) -> Document>>,
50}
51
52impl TransformPipeline {
53 pub fn new() -> Self {
55 Self { steps: Vec::new() }
56 }
57
58 pub fn transform_text<F>(mut self, f: F) -> Self
60 where
61 F: Fn(String) -> String + 'static,
62 {
63 use crate::ast_transform::Transform;
64 self.steps.push(Box::new(move |doc| doc.transform_text(f)));
65 self
66 }
67
68 pub fn transform_image_urls<F>(mut self, f: F) -> Self
70 where
71 F: Fn(String) -> String + 'static,
72 {
73 use crate::ast_transform::Transform;
74 self.steps
75 .push(Box::new(move |doc| doc.transform_image_urls(f)));
76 self
77 }
78
79 pub fn transform_link_urls<F>(mut self, f: F) -> Self
81 where
82 F: Fn(String) -> String + 'static,
83 {
84 use crate::ast_transform::Transform;
85 self.steps
86 .push(Box::new(move |doc| doc.transform_link_urls(f)));
87 self
88 }
89
90 pub fn transform_autolink_urls<F>(mut self, f: F) -> Self
92 where
93 F: Fn(String) -> String + 'static,
94 {
95 use crate::ast_transform::Transform;
96 self.steps
97 .push(Box::new(move |doc| doc.transform_autolink_urls(f)));
98 self
99 }
100
101 pub fn transform_code<F>(mut self, f: F) -> Self
103 where
104 F: Fn(String) -> String + 'static,
105 {
106 use crate::ast_transform::Transform;
107 self.steps.push(Box::new(move |doc| doc.transform_code(f)));
108 self
109 }
110
111 pub fn transform_html<F>(mut self, f: F) -> Self
113 where
114 F: Fn(String) -> String + 'static,
115 {
116 use crate::ast_transform::Transform;
117 self.steps.push(Box::new(move |doc| doc.transform_html(f)));
118 self
119 }
120
121 pub fn transform_with<T: Transformer + 'static>(mut self, transformer: T) -> Self {
123 use crate::ast_transform::Transform;
124 self.steps
125 .push(Box::new(move |doc| doc.transform_with(transformer)));
126 self
127 }
128
129 pub fn custom<F>(mut self, f: F) -> Self
131 where
132 F: FnOnce(Document) -> Document + 'static,
133 {
134 self.steps.push(Box::new(f));
135 self
136 }
137
138 pub fn when<F>(mut self, condition: bool, builder: F) -> Self
140 where
141 F: FnOnce(TransformPipeline) -> TransformPipeline,
142 {
143 if condition {
144 let sub_pipeline = builder(TransformPipeline::new());
145 self.steps
146 .push(Box::new(move |doc| sub_pipeline.apply(doc)));
147 }
148 self
149 }
150
151 pub fn when_doc<P, F>(mut self, predicate: P, builder: F) -> Self
153 where
154 P: Fn(&Document) -> bool + 'static,
155 F: FnOnce(TransformPipeline) -> TransformPipeline + 'static,
156 {
157 self.steps.push(Box::new(move |doc| {
158 if predicate(&doc) {
159 let sub_pipeline = builder(TransformPipeline::new());
160 sub_pipeline.apply(doc)
161 } else {
162 doc
163 }
164 }));
165 self
166 }
167
168 pub fn remove_empty_paragraphs(mut self) -> Self {
170 use crate::ast_transform::FilterTransform;
171 self.steps
172 .push(Box::new(|doc| doc.remove_empty_paragraphs()));
173 self
174 }
175
176 pub fn remove_empty_text(mut self) -> Self {
178 use crate::ast_transform::FilterTransform;
179 self.steps.push(Box::new(|doc| doc.remove_empty_text()));
180 self
181 }
182
183 pub fn normalize_whitespace(mut self) -> Self {
185 use crate::ast_transform::FilterTransform;
186 self.steps.push(Box::new(|doc| doc.normalize_whitespace()));
187 self
188 }
189
190 pub fn filter_blocks<F>(mut self, predicate: F) -> Self
192 where
193 F: Fn(&Block) -> bool + 'static,
194 {
195 use crate::ast_transform::FilterTransform;
196 self.steps
197 .push(Box::new(move |doc| doc.filter_blocks(predicate)));
198 self
199 }
200
201 pub fn apply(self, mut doc: Document) -> Document {
203 for step in self.steps {
204 doc = step(doc);
205 }
206 doc
207 }
208}
209
210impl Default for TransformPipeline {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216pub trait PipeExt {
218 fn pipe<F, R>(self, f: F) -> R
220 where
221 F: FnOnce(Self) -> R,
222 Self: Sized,
223 {
224 f(self)
225 }
226}
227
228impl PipeExt for Document {}