Skip to main content

tagger/
lib.rs

1use std::fmt;
2use std::fmt::Write;
3
4#[cfg(doctest)]
5mod test_readme {
6    macro_rules! external_doc_test {
7        ($x:expr) => {
8            #[doc = $x]
9            extern "C" {}
10        };
11    }
12
13    external_doc_test!(include_str!("../README.md"));
14}
15
16///
17/// Construct and Write a SVG path's data.
18///
19/// following: [w3 spec](https://www.w3.org/TR/SVG/paths.html#PathDataGeneralInformation)
20///
21pub enum PathCommand<F> {
22    /// move to
23    M(F, F),
24    /// relative move to
25    M_(F, F),
26    /// line to
27    L(F, F),
28    /// relative line to
29    L_(F, F),
30    /// horizontal to
31    H(F),
32    /// relative horizontal to
33    H_(F),
34    /// vertical to
35    V(F),
36    /// relative vertical to
37    V_(F),
38    /// curve to
39    C(F, F, F, F, F, F),
40    /// relative curve to
41    C_(F, F, F, F, F, F),
42    /// shorthand curve to
43    S(F, F, F, F),
44    /// relative shorthand curve to
45    S_(F, F, F, F),
46    /// quadratic bezier curve to
47    Q(F, F, F, F),
48    /// relative quadratic bezier curve to
49    Q_(F, F, F, F),
50    /// shorthand quadratic bezier curve to
51    T(F, F),
52    /// relative shorthand quadratic bezier curve to
53    T_(F, F),
54    /// elliptical arc
55    A(F, F, F, F, F, F, F),
56    /// relative elliptical arc
57    A_(F, F, F, F, F, F, F),
58    /// close path
59    Z(F),
60}
61
62impl<F> PathCommand<F> {
63    #[inline(always)]
64    pub fn map<J>(self, mut func: impl FnMut(F) -> J) -> PathCommand<J> {
65        use PathCommand::*;
66
67        match self {
68            M(x, y) => M(func(x), func(y)),
69            M_(x, y) => M_(func(x), func(y)),
70            L(x, y) => L(func(x), func(y)),
71            L_(x, y) => L_(func(x), func(y)),
72            H(a) => H(func(a)),
73            H_(a) => H_(func(a)),
74            V(a) => V(func(a)),
75            V_(a) => V_(func(a)),
76            C(x1, y1, x2, y2, x, y) => C(func(x1), func(y1), func(x2), func(y2), func(x), func(y)),
77            C_(dx1, dy1, dx2, dy2, dx, dy) => C_(
78                func(dx1),
79                func(dy1),
80                func(dx2),
81                func(dy2),
82                func(dx),
83                func(dy),
84            ),
85            S(x2, y2, x, y) => S(func(x2), func(y2), func(x), func(y)),
86            S_(x2, y2, x, y) => S_(func(x2), func(y2), func(x), func(y)),
87            Q(x1, y1, x, y) => Q(func(x1), func(y1), func(x), func(y)),
88            Q_(dx1, dy1, dx, dy) => Q_(func(dx1), func(dy1), func(dx), func(dy)),
89            T(x, y) => T(func(x), func(y)),
90            T_(x, y) => T_(func(x), func(y)),
91            A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y) => A(
92                func(rx),
93                func(ry),
94                func(x_axis_rotation),
95                func(large_arc_flag),
96                func(sweep_flag),
97                func(x),
98                func(y),
99            ),
100            A_(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, dx, dy) => A_(
101                func(rx),
102                func(ry),
103                func(x_axis_rotation),
104                func(large_arc_flag),
105                func(sweep_flag),
106                func(dx),
107                func(dy),
108            ),
109            Z(a) => Z(func(a)),
110        }
111    }
112
113    #[inline(always)]
114    fn write<T: fmt::Write>(&self, mut writer: T) -> fmt::Result
115    where
116        F: fmt::Display,
117    {
118        use PathCommand::*;
119        match self {
120            M(x, y) => {
121                write!(writer, " M {} {}", x, y)
122            }
123            M_(x, y) => {
124                write!(writer, " m {} {}", x, y)
125            }
126            L(x, y) => {
127                write!(writer, " L {} {}", x, y)
128            }
129            L_(x, y) => {
130                write!(writer, " l {} {}", x, y)
131            }
132            H(a) => {
133                write!(writer, " H {}", a)
134            }
135            H_(a) => {
136                write!(writer, " h {}", a)
137            }
138            V(a) => {
139                write!(writer, " V {}", a)
140            }
141            V_(a) => {
142                write!(writer, " v {}", a)
143            }
144            C(x1, y1, x2, y2, x, y) => {
145                write!(writer, " C {} {}, {} {}, {} {}", x1, y1, x2, y2, x, y)
146            }
147            C_(dx1, dy1, dx2, dy2, dx, dy) => {
148                write!(writer, " c {} {}, {} {}, {} {}", dx1, dy1, dx2, dy2, dx, dy)
149            }
150            S(x2, y2, x, y) => {
151                write!(writer, " S {},{} {} {}", x2, y2, x, y)
152            }
153            S_(x2, y2, x, y) => {
154                write!(writer, " s {},{} {} {}", x2, y2, x, y)
155            }
156            Q(x1, y1, x, y) => {
157                write!(writer, " Q {} {}, {} {}", x1, y1, x, y)
158            }
159            Q_(dx1, dy1, dx, dy) => {
160                write!(writer, " q {} {}, {} {}", dx1, dy1, dx, dy)
161            }
162            T(x, y) => {
163                write!(writer, " T {} {}", x, y)
164            }
165            T_(x, y) => {
166                write!(writer, " t {} {}", x, y)
167            }
168            A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y) => {
169                write!(
170                    writer,
171                    " A {} {} {} {} {} {} {}",
172                    rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y
173                )
174            }
175            A_(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, dx, dy) => {
176                write!(
177                    writer,
178                    " a {} {} {} {} {} {} {}",
179                    rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, dx, dy
180                )
181            }
182            Z(_) => {
183                write!(writer, " Z")
184            }
185        }
186    }
187}
188
189///
190/// Build a path.
191///
192pub struct PathBuilder<'a, T> {
193    writer: &'a mut T,
194}
195impl<'a, T: fmt::Write> PathBuilder<'a, T> {
196    #[inline(always)]
197    pub fn put(&mut self, command: crate::PathCommand<impl fmt::Display>) -> fmt::Result {
198        command.write(escape_guard(&mut self.writer))
199    }
200}
201
202///
203/// Build up a list of points.
204///
205pub struct PointsBuilder<'a, T> {
206    writer: &'a mut T,
207}
208impl<'a, T: fmt::Write> PointsBuilder<'a, T> {
209    #[inline(always)]
210    pub fn put(&mut self, x: impl fmt::Display, y: impl fmt::Display) -> fmt::Result {
211        write!(escape_guard(&mut self.writer), "{},{} ", x, y)
212    }
213}
214
215///
216/// Used to wrap a `std::io::Write` to have `std::io::Write`.
217/// The underlying error can be extracted through the error field.
218///
219pub struct Adaptor<T> {
220    pub inner: T,
221    pub error: Result<(), std::io::Error>,
222}
223
224///
225/// Create an initial `ElemWriter`
226///
227pub fn new<T: fmt::Write>(a: T) -> ElemWriter<T> {
228    ElemWriter(a)
229}
230
231///Update a `std::io::Write` to be a `std::fmt::Write`
232pub fn upgrade_write<T: std::io::Write>(inner: T) -> Adaptor<T> {
233    Adaptor {
234        inner,
235        error: Ok(()),
236    }
237}
238
239impl<T: std::io::Write> std::fmt::Write for Adaptor<T> {
240    fn write_str(&mut self, s: &str) -> std::fmt::Result {
241        match self.inner.write_all(s.as_bytes()) {
242            Ok(()) => Ok(()),
243            Err(e) => {
244                self.error = Err(e);
245                Err(std::fmt::Error)
246            }
247        }
248    }
249}
250
251///
252/// A struct that captures a half-made element. To
253/// complete building an element, `build()` must be called.
254///
255#[must_use]
256pub struct ElementBridge<'a, T, D, K> {
257    writer: &'a mut ElemWriter<T>,
258    tag: D,
259    pub k: K,
260}
261impl<'a, T: fmt::Write, D: fmt::Display, K> ElementBridge<'a, T, D, K> {
262    pub fn build<J>(
263        self,
264        func: impl FnOnce(&mut ElemWriter<T>) -> Result<J, fmt::Error>,
265    ) -> Result<J, fmt::Error> {
266        let k = func(self.writer)?;
267        self.writer.0.write_str("</")?;
268        write!(escape_guard(&mut self.writer.0), "{}", self.tag)?;
269        self.writer.0.write_char('>')?;
270        Ok(k)
271    }
272}
273
274///
275/// Create attributes.
276///
277pub struct AttrWriter<'a, T>(&'a mut T);
278impl<'a, T: fmt::Write> AttrWriter<'a, T> {
279    pub fn attr(&mut self, a: impl fmt::Display, b: impl fmt::Display) -> fmt::Result {
280        write!(escape_guard(&mut self.0), " {}", a)?;
281        self.0.write_str("=\"")?;
282        write!(escape_guard(&mut self.0), "{}", b)?;
283        self.0.write_str("\"")
284    }
285
286    ///
287    /// WARNING: The user can escape xml here and inject any xml elements.
288    ///
289    #[deprecated(note = "please use `writer_escapable or writer_safe` instead")]
290    pub fn writer(&mut self) -> &mut T {
291        self.0
292    }
293
294    pub fn writer_safe(&mut self) -> EscapeGuard<&mut T> {
295        escape_guard(self.0)
296    }
297
298    ///
299    /// WARNING: The user can escape xml here and inject any xml elements.
300    ///
301    pub fn writer_escapable(&mut self) -> &mut T {
302        self.0
303    }
304
305    pub fn put_raw(&mut self, a: impl fmt::Display) -> fmt::Result {
306        write!(escape_guard(&mut self.0), " {}", a)
307    }
308
309    ///
310    /// WARNING: The user can escape xml here and inject any xml elements.
311    ///
312    pub fn put_raw_escapable(&mut self, a: impl fmt::Display) -> fmt::Result {
313        write!(&mut self.0, " {}", a)
314    }
315    pub fn path(&mut self, a: impl FnOnce(&mut PathBuilder<T>) -> fmt::Result) -> fmt::Result {
316        let mut p = PathBuilder { writer: self.0 };
317        p.writer.write_str(" d=\"")?;
318        a(&mut p)?;
319        p.writer.write_str("\"")
320    }
321    pub fn points(&mut self, a: impl FnOnce(&mut PointsBuilder<T>) -> fmt::Result) -> fmt::Result {
322        let mut p = PointsBuilder { writer: self.0 };
323        p.writer.write_str(" points=\"")?;
324        a(&mut p)?;
325        p.writer.write_str("\"")
326    }
327}
328
329///
330/// Create elements with a start and end tag, or elements with a single tag.
331///
332pub struct ElemWriter<T>(T);
333
334impl<'a> ElemWriter<&'a mut dyn fmt::Write> {
335    ///
336    /// Swap out the writer before the closing elements are written out.
337    ///
338    pub fn swap_writer(&mut self, other: &'a mut dyn fmt::Write) {
339        self.0 = other;
340    }
341}
342
343impl<T: fmt::Write> ElemWriter<T> {
344    pub fn into_writer(self) -> T {
345        self.0
346    }
347
348    ///
349    /// WARNING: The user can escape xml here and inject any xml elements.
350    ///
351    #[deprecated(note = "please use `writer_escapable or writer_safe` instead")]
352    pub fn writer(&mut self) -> &mut T {
353        &mut self.0
354    }
355
356    pub fn writer_safe(&mut self) -> EscapeGuard<&mut T> {
357        escape_guard(&mut self.0)
358    }
359
360    ///
361    /// WARNING: The user can escape xml here and inject any xml elements.
362    ///
363    pub fn writer_escapable(&mut self) -> &mut T {
364        &mut self.0
365    }
366
367    pub fn put_raw(&mut self, a: impl fmt::Display) -> fmt::Result {
368        write!(escape_guard(&mut self.0), " {}", a)
369    }
370
371    ///
372    /// WARNING: The user can escape xml here and inject any xml elements.
373    ///
374    pub fn put_raw_escapable(&mut self, a: impl fmt::Display) -> fmt::Result {
375        write!(&mut self.0, " {}", a)
376    }
377
378    pub fn single<D: fmt::Display>(
379        &mut self,
380        tag: D,
381        func: impl FnOnce(&mut AttrWriter<T>) -> fmt::Result,
382    ) -> fmt::Result {
383        self.0.write_char('<')?;
384        write!(escape_guard(&mut self.0), "{}", tag)?;
385        self.0.write_char(' ')?;
386        func(&mut AttrWriter(&mut self.0))?;
387        self.0.write_str(" />")
388    }
389    pub fn elem<D: fmt::Display, K>(
390        &mut self,
391        tag: D,
392        func: impl FnOnce(&mut AttrWriter<T>) -> Result<K, fmt::Error>,
393    ) -> Result<ElementBridge<T, D, K>, fmt::Error> {
394        self.0.write_char('<')?;
395        write!(escape_guard(&mut self.0), "{}", tag)?;
396        self.0.write_char(' ')?;
397        let k = func(&mut AttrWriter(&mut self.0))?;
398        self.0.write_str(" >")?;
399
400        Ok(ElementBridge {
401            writer: self,
402            tag,
403            k,
404        })
405    }
406}
407
408///
409/// Specify no attributes needed.
410/// Equivalent to writing `|_|{}`.
411///
412pub fn no_attr<T>() -> impl FnOnce(&mut AttrWriter<T>) -> fmt::Result {
413    move |_| Ok(())
414}
415
416///
417/// Writer adaptor that disallows escaping from xml.
418///
419pub fn escape_guard<T: std::fmt::Write>(a: T) -> EscapeGuard<T> {
420    EscapeGuard::new(a)
421}
422
423/// Writer adaptor that replaces xml escaping characters with their encoded value.
424///
425/// Disallowed characters are `"` `'` `<` `>` `&`. characters are replaced with their equivalent from:
426/// [https://dev.w3.org/html5/html-author/charref](https://dev.w3.org/html5/html-author/charref)
427///
428pub struct EscapeGuard<T> {
429    writer: T,
430}
431
432impl<T: std::fmt::Write> EscapeGuard<T> {
433    pub fn new(writer: T) -> EscapeGuard<T> {
434        EscapeGuard { writer }
435    }
436}
437
438impl<T: std::fmt::Write> std::fmt::Write for EscapeGuard<T> {
439    fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
440        for c in s.chars() {
441            let r = match c {
442                '\"' => Some("&quot;"),
443                '\'' => Some("&apos;"),
444                '<' => Some("&lt;"),
445                '>' => Some("&gt;"),
446                '&' => Some("&amp;"),
447                _ => None,
448            };
449
450            if let Some(r) = r {
451                self.writer.write_str(r)?;
452            } else {
453                self.writer.write_char(c)?;
454            }
455        }
456        Ok(())
457    }
458}