1use std::ops::Deref;
2
3use super::{Command, Number, Parameters, Position};
4use crate::node::Value;
5use crate::parser::{Error, Reader, Result};
6
7#[derive(Clone, Debug, Default)]
11pub struct Data(Vec<Command>);
12
13struct Parser<'l> {
14 reader: Reader<'l>,
15}
16
17impl Data {
18 #[inline]
20 pub fn new() -> Self {
21 Default::default()
22 }
23
24 #[inline]
26 pub fn parse(content: &str) -> Result<Self> {
27 Parser::new(content).process()
28 }
29
30 #[inline]
32 pub fn add(mut self, command: Command) -> Self {
33 self.append(command);
34 self
35 }
36
37 #[inline]
39 pub fn append(&mut self, command: Command) {
40 self.0.push(command);
41 }
42}
43
44macro_rules! implement {
45 (@one #[$doc:meta] fn $method:ident($command:ident, $position:ident)) => (
46 #[$doc]
47 pub fn $method<T>(mut self, parameters: T) -> Self
48 where
49 T: Into<Parameters>,
50 {
51 self.0.push(Command::$command(Position::$position, parameters.into()));
52 self
53 }
54 );
55 (@one #[$doc:meta] fn $method:ident($command:ident)) => (
56 #[$doc]
57 pub fn $method(mut self) -> Self {
58 self.0.push(Command::$command);
59 self
60 }
61 );
62 ($(#[$doc:meta] fn $method:ident($($argument:tt)*))*) => (
63 impl Data {
64 $(implement! { @one #[$doc] fn $method($($argument)*) })*
65 }
66 );
67}
68
69implement! {
70 #[doc = "Add an absolute `Command::Move` command."]
71 fn move_to(Move, Absolute)
72
73 #[doc = "Add a relative `Command::Move` command."]
74 fn move_by(Move, Relative)
75
76 #[doc = "Add an absolute `Command::Line` command."]
77 fn line_to(Line, Absolute)
78
79 #[doc = "Add a relative `Command::Line` command."]
80 fn line_by(Line, Relative)
81
82 #[doc = "Add an absolute `Command::HorizontalLine` command."]
83 fn horizontal_line_to(HorizontalLine, Absolute)
84
85 #[doc = "Add a relative `Command::HorizontalLine` command."]
86 fn horizontal_line_by(HorizontalLine, Relative)
87
88 #[doc = "Add an absolute `Command::VerticalLine` command."]
89 fn vertical_line_to(VerticalLine, Absolute)
90
91 #[doc = "Add a relative `Command::VerticalLine` command."]
92 fn vertical_line_by(VerticalLine, Relative)
93
94 #[doc = "Add an absolute `Command::QuadraticCurve` command."]
95 fn quadratic_curve_to(QuadraticCurve, Absolute)
96
97 #[doc = "Add a relative `Command::QuadraticCurve` command."]
98 fn quadratic_curve_by(QuadraticCurve, Relative)
99
100 #[doc = "Add an absolute `Command::SmoothQuadraticCurve` command."]
101 fn smooth_quadratic_curve_to(SmoothQuadraticCurve, Absolute)
102
103 #[doc = "Add a relative `Command::SmoothQuadraticCurve` command."]
104 fn smooth_quadratic_curve_by(SmoothQuadraticCurve, Relative)
105
106 #[doc = "Add an absolute `Command::CubicCurve` command."]
107 fn cubic_curve_to(CubicCurve, Absolute)
108
109 #[doc = "Add a relative `Command::CubicCurve` command."]
110 fn cubic_curve_by(CubicCurve, Relative)
111
112 #[doc = "Add an absolute `Command::SmoothCubicCurve` command."]
113 fn smooth_cubic_curve_to(SmoothCubicCurve, Absolute)
114
115 #[doc = "Add a relative `Command::SmoothCubicCurve` command."]
116 fn smooth_cubic_curve_by(SmoothCubicCurve, Relative)
117
118 #[doc = "Add an absolute `Command::EllipticalArc` command."]
119 fn elliptical_arc_to(EllipticalArc, Absolute)
120
121 #[doc = "Add a relative `Command::EllipticalArc` command."]
122 fn elliptical_arc_by(EllipticalArc, Relative)
123
124 #[doc = "Add a `Command::Close` command."]
125 fn close(Close)
126}
127
128impl Deref for Data {
129 type Target = [Command];
130
131 #[inline]
132 fn deref(&self) -> &Self::Target {
133 &self.0
134 }
135}
136
137impl From<Vec<Command>> for Data {
138 #[inline]
139 fn from(commands: Vec<Command>) -> Self {
140 Data(commands)
141 }
142}
143
144impl From<Data> for Vec<Command> {
145 #[inline]
146 fn from(Data(commands): Data) -> Self {
147 commands
148 }
149}
150
151impl From<Data> for Value {
152 #[inline]
153 fn from(Data(mut inner): Data) -> Self {
154 inner
155 .drain(..)
156 .map(String::from)
157 .collect::<Vec<_>>()
158 .join(" ")
159 .into()
160 }
161}
162
163macro_rules! raise(
164 ($parser:expr, $($argument:tt)*) => (
165 return Err(Error::new($parser.reader.position(), format!($($argument)*)))
166 );
167);
168
169impl<'l> Parser<'l> {
170 #[inline]
171 fn new(content: &'l str) -> Self {
172 Parser {
173 reader: Reader::new(content),
174 }
175 }
176
177 fn process(&mut self) -> Result<Data> {
178 let mut commands = Vec::new();
179 loop {
180 self.reader.consume_whitespace();
181 match self.read_command()? {
182 Some(command) => commands.push(command),
183 _ => break,
184 }
185 }
186 Ok(Data(commands))
187 }
188
189 fn read_command(&mut self) -> Result<Option<Command>> {
190 use super::Command::*;
191 use super::Position::*;
192
193 let name = match self.reader.next() {
194 Some(name) => match name {
195 'A'..='Z' | 'a'..='z' => name,
196 _ => raise!(self, "expected a path command"),
197 },
198 _ => return Ok(None),
199 };
200 self.reader.consume_whitespace();
201 Ok(Some(match name {
202 'M' => Move(Absolute, self.read_parameters()?.into()),
203 'm' => Move(Relative, self.read_parameters()?.into()),
204
205 'L' => Line(Absolute, self.read_parameters()?.into()),
206 'l' => Line(Relative, self.read_parameters()?.into()),
207
208 'H' => HorizontalLine(Absolute, self.read_parameters()?.into()),
209 'h' => HorizontalLine(Relative, self.read_parameters()?.into()),
210
211 'V' => VerticalLine(Absolute, self.read_parameters()?.into()),
212 'v' => VerticalLine(Relative, self.read_parameters()?.into()),
213
214 'Q' => QuadraticCurve(Absolute, self.read_parameters()?.into()),
215 'q' => QuadraticCurve(Relative, self.read_parameters()?.into()),
216
217 'T' => SmoothQuadraticCurve(Absolute, self.read_parameters()?.into()),
218 't' => SmoothQuadraticCurve(Relative, self.read_parameters()?.into()),
219
220 'C' => CubicCurve(Absolute, self.read_parameters()?.into()),
221 'c' => CubicCurve(Relative, self.read_parameters()?.into()),
222
223 'S' => SmoothCubicCurve(Absolute, self.read_parameters()?.into()),
224 's' => SmoothCubicCurve(Relative, self.read_parameters()?.into()),
225
226 'A' => EllipticalArc(Absolute, self.read_parameters_elliptical_arc()?.into()),
227 'a' => EllipticalArc(Relative, self.read_parameters_elliptical_arc()?.into()),
228
229 'Z' | 'z' => Close,
230
231 _ => raise!(self, "found an unknown path command '{}'", name),
232 }))
233 }
234
235 fn read_parameters(&mut self) -> Result<Vec<Number>> {
236 let mut parameters = Vec::new();
237
238 while let Some(number) = self.read_number()? {
239 parameters.push(number);
240 self.reader.consume_whitespace();
241 self.reader.consume_char(',');
242 }
243 Ok(parameters)
244 }
245
246 fn read_parameters_elliptical_arc(&mut self) -> Result<Vec<Number>> {
247 let mut parameters = Vec::new();
248 let mut index: usize = 1;
249
250 while let Some(number) = match index % 7 {
251 i if i == 4 || i == 5 => self.read_flag()?,
252 _ => self.read_number()?,
253 } {
254 index += 1;
255 parameters.push(number);
256 self.reader.consume_whitespace();
257 self.reader.consume_char(',');
258 }
259 Ok(parameters)
260 }
261
262 fn read_flag(&mut self) -> Result<Option<Number>> {
263 self.reader.consume_whitespace();
264
265 match self.reader.next() {
266 Some('0') => Ok(Some(0.0)),
267 Some('1') => Ok(Some(1.0)),
268 _ => raise!(self, "failed to parse a flag in an elliptical arc"),
269 }
270 }
271
272 pub fn read_number(&mut self) -> Result<Option<Number>> {
273 self.reader.consume_whitespace();
274 let number = self
275 .reader
276 .capture(|reader| reader.consume_number())
277 .map(String::from);
278 match number {
279 Some(number) => match number.parse() {
280 Ok(number) => Ok(Some(number)),
281 _ => raise!(self, "failed to parse a number '{}'", number),
282 },
283 _ => Ok(None),
284 }
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use crate::node::element::path::data::Parser;
291 use crate::node::element::path::{Command, Data, Position};
292 use crate::node::Value;
293
294 #[test]
295 fn data_append() {
296 let mut data = Data::new();
297 data.append(Command::Line(Position::Absolute, (1, 2).into()));
298 data.append(Command::Close);
299 assert_eq!(Value::from(data).to_string(), "L1,2 z");
300 }
301
302 #[test]
303 fn data_into_value() {
304 let data = Data::new()
305 .line_to((1, 2))
306 .cubic_curve_by((1, 2.5, 3, 4, 5, 6))
307 .close();
308
309 assert_eq!(Value::from(data).to_string(), "L1,2 c1,2.5,3,4,5,6 z");
310 }
311
312 #[test]
313 fn data_parse() {
314 let data = Data::parse("M1,2 l3,4").unwrap();
315
316 assert_eq!(data.len(), 2);
317 match data[0] {
318 Command::Move(Position::Absolute, ref parameters) => {
319 assert_eq!(¶meters[..], &[1.0, 2.0])
320 }
321 _ => unreachable!(),
322 }
323 match data[1] {
324 Command::Line(Position::Relative, ref parameters) => {
325 assert_eq!(¶meters[..], &[3.0, 4.0])
326 }
327 _ => unreachable!(),
328 }
329 }
330
331 #[test]
332 fn parser_read_command() {
333 macro_rules! run(
334 ($content:expr) => ({
335 let mut parser = Parser::new($content);
336 parser.read_command().unwrap().unwrap()
337 });
338 );
339
340 macro_rules! test(
341 ($content:expr, $command:ident, $position:ident, $parameters:expr) => (
342 match run!($content) {
343 Command::$command(Position::$position, parameters) => assert_eq!(¶meters[..], $parameters),
344 _ => unreachable!(),
345 }
346 );
347 ($content:expr, $command:ident) => (
348 match run!($content) {
349 Command::$command => {}
350 _ => unreachable!(),
351 }
352 );
353 );
354
355 test!("M4,2", Move, Absolute, &[4.0, 2.0]);
356 test!("m4,\n2", Move, Relative, &[4.0, 2.0]);
357
358 test!("L7, 8 9", Line, Absolute, &[7.0, 8.0, 9.0]);
359 test!("l 7,8 \n9", Line, Relative, &[7.0, 8.0, 9.0]);
360
361 test!("H\t6,9", HorizontalLine, Absolute, &[6.0, 9.0]);
362 test!("h6, \t9", HorizontalLine, Relative, &[6.0, 9.0]);
363
364 test!("V2.1,-3", VerticalLine, Absolute, &[2.1, -3.0]);
365 test!("v\n2.1 -3", VerticalLine, Relative, &[2.1, -3.0]);
366
367 test!("Q90.5 0", QuadraticCurve, Absolute, &[90.5, 0.0]);
368 test!("q90.5\n, 0", QuadraticCurve, Relative, &[90.5, 0.0]);
369
370 test!("T-1", SmoothQuadraticCurve, Absolute, &[-1.0]);
371 test!("t -1", SmoothQuadraticCurve, Relative, &[-1.0]);
372
373 test!("C0,1 0,2", CubicCurve, Absolute, &[0.0, 1.0, 0.0, 2.0]);
374 test!("c0 ,1 0, 2", CubicCurve, Relative, &[0.0, 1.0, 0.0, 2.0]);
375
376 test!("S42,0", SmoothCubicCurve, Absolute, &[42.0, 0.0]);
377 test!("s \t 42,0", SmoothCubicCurve, Relative, &[42.0, 0.0]);
378
379 test!(
380 "A1 1 2.6,0 0 0 -7",
381 EllipticalArc,
382 Absolute,
383 &[1.0, 1.0, 2.6, 0.0, 0.0, 0.0, -7.0]
384 );
385 test!(
386 "a1 1 2.6,0 0 0 -7",
387 EllipticalArc,
388 Relative,
389 &[1.0, 1.0, 2.6, 0.0, 0.0, 0.0, -7.0]
390 );
391 test!(
392 "a32 32 0 00.03-45.22",
393 EllipticalArc,
394 Relative,
395 &[32.0, 32.0, 0.0, 0.0, 0.0, 0.03, -45.22]
396 );
397 test!(
398 "a48 48 0 1148-48",
399 EllipticalArc,
400 Relative,
401 &[48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0]
402 );
403 test!(
404 "a82.6 82.6 0 0033.48-20.25",
405 EllipticalArc,
406 Relative,
407 &[82.6, 82.6, 0.0, 0.0, 0.0, 33.48, -20.25]
408 );
409 test!(
410 "a82.45 82.45 0 00-20.24 33.47",
411 EllipticalArc,
412 Relative,
413 &[82.45, 82.45, 0.0, 0.0, 0.0, -20.24, 33.47]
414 );
415 test!(
416 "a48 48 0 1148-48 48 48 0 01-48 48",
417 EllipticalArc,
418 Relative,
419 &[48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0, 48.0, 48.0, 0.0, 0.0, 1.0, -48.0, 48.0]
420 );
421 test!(
422 "a48 48 0 1148-48 48 48 0 01-48 48 32 32 0 11.03-45.22",
423 EllipticalArc,
424 Relative,
425 &[
426 48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0, 48.0, 48.0, 0.0, 0.0, 1.0, -48.0, 48.0,
427 32.0, 32.0, 0.0, 1.0, 1.0, 0.03, -45.22
428 ]
429 );
430 test!(
431 "a2.51 2.51 0 01.25.32",
432 EllipticalArc,
433 Relative,
434 &[2.51, 2.51, 0.0, 0.0, 1.0, 0.25, 0.32]
435 );
436 test!(
437 "a1 1 0 00.25.32",
438 EllipticalArc,
439 Relative,
440 &[1., 1., 0.0, 0.0, 0.0, 0.25, 0.32]
441 );
442 test!(
443 "a1 1 0 000.25.32",
444 EllipticalArc,
445 Relative,
446 &[1., 1., 0.0, 0.0, 0.0, 0.25, 0.32]
447 );
448
449 test!("Z", Close);
450 test!("z", Close);
451 }
452
453 #[test]
454 fn parser_read_parameters() {
455 macro_rules! test(
456 ($content:expr, $parameters:expr) => ({
457 let mut parser = Parser::new($content);
458 let parameters = parser.read_parameters().unwrap();
459 assert_eq!(¶meters[..], $parameters);
460 });
461 );
462
463 test!("1,2 3,4 5 6.7", &[1.0, 2.0, 3.0, 4.0, 5.0, 6.7]);
464 test!("4-3.1.3e2.4", &[4.0, -3.1, 0.3e2, 0.4]);
465 }
466
467 #[test]
468 fn parser_read_parameters_elliptical_arc() {
469 macro_rules! test(
470 ($content:expr, $parameters:expr) => ({
471 let mut parser = Parser::new($content);
472 let parameters = parser.read_parameters_elliptical_arc().unwrap();
473 assert_eq!(¶meters[..], $parameters);
474 });
475 );
476
477 test!(
478 "32 32 0 00.03-45.22",
479 &[32.0, 32.0, 0.0, 0.0, 0.0, 0.03, -45.22]
480 );
481 test!("48 48 0 1148-48", &[48.0, 48.0, 0.0, 1.0, 1.0, 48.0, -48.0]);
482 }
483
484 #[test]
485 fn parser_read_number() {
486 macro_rules! test(
487 ($content:expr, $value:expr) => ({
488 let mut parser = Parser::new($content);
489 assert_eq!(parser.read_number().unwrap().unwrap(), $value);
490 });
491 );
492
493 test!("0.30000000000000004", 0.3);
494 test!("1e-4", 1e-4);
495 test!("-1E2", -1e2);
496 test!("-0.00100E-002", -1e-5);
497 }
498}