1use crate::prelude::*;
2use std::f64;
3use std::iter::Peekable;
4use std::ops::Neg;
5use std::{convert::TryFrom, str::Chars};
6
7#[derive(Clone, PartialEq, PartialOrd, Debug)]
11pub enum Expression {
12 Method(String, Vec<Expression>),
13 Complex(Vec<Expression>),
14 Number(Number, String),
15 Color(Color),
16 Other(String),
17}
18
19impl Expression {
20 pub fn number(&self) -> Option<Number> {
22 match self {
23 Expression::Number(number, d) if d.is_empty() => Some(*number),
24 _ => None,
25 }
26 }
27
28 pub fn color(&self) -> Option<Color> {
29 match self {
30 Expression::Color(color) => Some(*color),
31 Expression::Method(name, args) => {
32 let mut values = [0.0f64; 4];
33 for (i, arg) in args.iter().enumerate() {
34 if i > 3 {
35 return None;
36 }
37 let (mut v, p): (f64, bool) = match arg {
38 Expression::Number(v, u) if u.is_empty() => ((*v).into(), false),
39 Expression::Number(v, u) if u == "%" => ((*v).into(), true),
40 _ => {
41 return None;
42 }
43 };
44 if name == "rgb" || name == "rgba" {
45 if p {
46 v = v * 100.0 / 255.0;
47 } else if v <= 1.0 {
48 v = 255.0 * v.fract();
49 }
50 } else if i != 0 && p {
51 v /= 100.0;
52 }
53 values[i] = v;
54 }
55 if args.len() == 3 {
56 Some(match &name[..] {
57 "rgb" => Color::rgb(values[0] as u8, values[1] as u8, values[2] as u8),
58 "hsv" | "hsb" => Color::hsv(values[0], values[1], values[2]),
59 "hsl" => Color::hsl(values[0], values[1], values[2]),
60 _ => return None,
61 })
62 } else {
63 Some(match &name[..] {
64 "rgba" => Color::rgba(
65 values[0] as u8,
66 values[1] as u8,
67 values[2] as u8,
68 values[3] as u8,
69 ),
70 "hsva" | "hsba" => Color::hsva(values[0], values[1], values[2], values[3]),
71 "hsla" => Color::hsla(values[0], values[1], values[2], values[3]),
72 _ => return None,
73 })
74 }
75 }
76 Expression::Other(s) => Color::from_name(s),
77 _ => None,
78 }
79 }
80
81 fn gradient_stop(&self) -> Option<GradientStop> {
82 if let Some(color) = self.color() {
83 return Some(GradientStop { pos: None, color });
84 }
85 match self {
86 Expression::Complex(v) if v.len() == 2 => {
87 let color = match v[0].color() {
88 Some(color) => color,
89 None => return None,
90 };
91 let pos = match v[1] {
92 Expression::Number(n, ref m) => OnLinePos::try_from((n, &m[..])).ok()?,
93 _ => return None,
94 };
95 Some(GradientStop {
96 pos: Some(pos),
97 color,
98 })
99 }
100 _ => None,
101 }
102 }
103
104 pub fn relative_dir(&self) -> Option<RelativeDir> {
105 match self {
106 Expression::Other(label) => match &label[..] {
107 "to top" => Some(RelativeDir::Top),
108 "to top right" => Some(RelativeDir::TopRight),
109 "to right" => Some(RelativeDir::Right),
110 "to bottom right" => Some(RelativeDir::BottomRight),
111 "to bottom" => Some(RelativeDir::Bottom),
112 "to bottom left" => Some(RelativeDir::BottomLeft),
113 "to left" => Some(RelativeDir::Left),
114 "to top left" => Some(RelativeDir::TopLeft),
115 _ => None,
116 },
117 _ => None,
118 }
119 }
120
121 pub fn angle(&self) -> Option<Angle> {
122 match self {
123 Expression::Number(num, unit) => {
124 let num: f64 = (*num).into();
125 let angle = match &unit[..] {
126 "rad" => Angle::from_radians(num),
127 "turn" => Angle::from_turn(num),
128 "deg" | "" => Angle::from_degrees(num),
129 _ => {
130 return None;
131 }
132 };
133 Some(angle)
134 }
135 _ => None,
136 }
137 }
138
139 pub fn css_gradient(&self) -> Option<Gradient> {
140 let mut displacement = OnPlanePos::new(
141 OnLinePos::new(0.0, OnLinePosKind::Pixels),
142 OnLinePos::new(0.0, OnLinePosKind::Pixels),
143 );
144 let (name, args) = match self {
145 Expression::Method(name, args) => (name, args),
146 Expression::Complex(exprs) if exprs.len() <= 3 + 1 => {
147 let mut i = 0;
148 let (name, args) = match exprs.get(i) {
149 Some(Expression::Method(name, args)) => {
150 i += 1;
151 (name, args)
152 }
153 _ => {
154 return None;
155 }
156 };
157 *displacement.x_mut() = match exprs.get(i) {
158 Some(Expression::Number(n, u)) => {
159 i += 1;
160 OnLinePos::try_from((*n, &u[..])).ok()?
161 }
162 _ => {
163 return None;
164 }
165 };
166 *displacement.y_mut() = match exprs.get(i) {
167 Some(Expression::Number(n, u)) => OnLinePos::try_from((*n, &u[..])).ok()?,
168 _ => {
169 return None;
170 }
171 };
172 (name, args)
173 }
174 _ => return None,
175 };
176 if args.is_empty() {
177 return None;
178 }
179 let (radial, repeat) = match &name[..] {
180 "repeating-linear-gradient" => (false, true),
181 "linear-gradient" => (false, false),
182 "radial-gradient" => (true, false),
183 "repeating-radial-gradient" => (true, true),
184 _ => {
185 return None;
186 }
187 };
188 let mut i = 0;
189 let kind;
190 if radial {
191 return None;
193 } else {
194 let mut coords = LinearGradientCoords::Angle {
195 displacement,
196 angle: Angle::zero(),
197 };
198 if let Some(direction) = args[0].relative_dir() {
199 coords = LinearGradientCoords::Direction {
200 direction,
201 displacement,
202 };
203 } else if let Some(angle) = args[0].angle() {
204 coords = LinearGradientCoords::Angle {
205 angle,
206 displacement,
207 };
208 i += 1;
209 }
210 kind = GradientKind::Linear(coords);
211 }
212 let stops: Vec<GradientStop> = args
213 .iter()
214 .skip(i)
215 .filter_map(|stop| stop.gradient_stop())
216 .collect();
217 if stops.is_empty() {
218 return None;
219 }
220 Some(Gradient {
221 kind,
222 stops,
223 repeat,
224 })
225 }
226
227 pub fn brush(&self) -> Option<Brush> {
228 if let Some(color) = self.color() {
229 return Some(Brush::from(color));
230 }
231 if let Some(g) = self.css_gradient() {
232 return Some(Brush::from(g));
233 }
234 None
235 }
236}
237
238impl Default for Expression {
239 fn default() -> Self {
240 Expression::Complex(Vec::new())
241 }
242}
243
244impl From<Expression> for Number {
245 fn from(e: Expression) -> Self {
246 match e {
247 Expression::Number(num, _) => num,
248 _ => Number::default(),
249 }
250 }
251}
252
253pub(crate) fn parse_expression_with_complex(chrs: &mut Peekable<Chars>) -> Option<Expression> {
254 let mut v = Vec::new();
255 while let Some(c) = chrs.peek() {
256 let c = *c;
257 if c == ',' || c == ')' {
258 break;
259 } else if c.is_whitespace() {
260 chrs.next().unwrap();
262 continue;
263 }
264 let expr = parse_expression(chrs)?;
265 v.push(expr);
266 }
267 if v.is_empty() {
268 None
269 } else if v.len() == 1 {
270 Some(v[0].to_owned())
271 } else {
272 Some(Expression::Complex(v))
273 }
274}
275
276fn is_number_component(c: char) -> bool {
277 c.is_ascii_digit() || c == '.' || c == '-'
278}
279
280fn parse_expression(chrs: &mut Peekable<Chars>) -> Option<Expression> {
281 let mut text = String::new();
282 let method;
283 loop {
284 match chrs.peek() {
285 Some('(') => {
286 chrs.next().unwrap();
287 method = true;
288 break;
289 }
290 Some(c) if *c == ',' || *c == ')' || (c.is_whitespace() && text != "to") => {
291 method = false;
292 break;
293 }
294 Some(c) => {
295 text.push(*c);
296 chrs.next().unwrap();
297 }
298 None => {
299 method = false;
300 break;
301 }
302 }
303 }
304 debug_assert!(!text.is_empty());
305 if method {
306 let mut args = Vec::new();
307 loop {
308 match chrs.peek() {
309 Some(c) if c.is_whitespace() || *c == ',' => {
310 chrs.next().unwrap();
311 }
312 None | Some(')') => {
313 let _ = chrs.next();
314 break;
315 }
316 _ => {
317 args.push(parse_expression_with_complex(chrs)?);
318 }
319 }
320 }
321 Some(Expression::Method(text, args))
322 } else {
323 if text.starts_with('#') {
324 return Some(Expression::Color(Color::from(text)));
325 } else if text.starts_with(is_number_component) {
326 if let Some(mut ofs) = text.rfind(is_number_component) {
327 ofs += 1; if text[..ofs]
329 .find(|x| x == '.' || x == 'e' || x == 'E')
330 .is_some()
331 {
332 if let Ok(v) = lexical_core::parse(text[..ofs].as_bytes()) {
333 return Some(Expression::Number(Number::Float(v), text[ofs..].to_owned()));
334 }
335 } else if let Ok(v) = lexical_core::parse(text[..ofs].as_bytes()) {
336 return Some(Expression::Number(Number::Real(v), text[ofs..].to_owned()));
337 }
338 }
339 }
340 Some(Expression::Other(text))
341 }
342}
343
344impl From<&str> for Expression {
345 fn from(s: &str) -> Expression {
346 parse_expression_with_complex(&mut s.chars().peekable()).unwrap_or_default()
347 }
348}
349
350impl From<String> for Expression {
351 fn from(s: String) -> Expression {
352 Expression::from(&s[..])
353 }
354}
355
356#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
358pub struct OnPlanePos {
359 x: OnLinePos,
360 y: OnLinePos,
361}
362
363impl OnPlanePos {
364 pub fn new(x: OnLinePos, y: OnLinePos) -> OnPlanePos {
365 OnPlanePos { x, y }
366 }
367
368 pub fn x(&self) -> OnLinePos {
369 self.x
370 }
371
372 pub fn y(&self) -> OnLinePos {
373 self.y
374 }
375
376 pub fn x_mut(&mut self) -> &mut OnLinePos {
377 &mut self.x
378 }
379
380 pub fn y_mut(&mut self) -> &mut OnLinePos {
381 &mut self.y
382 }
383
384 pub fn pixels(&self, size: Size) -> Point {
386 Point::from((self.x.pixels(size.width()), self.y.pixels(size.height())))
387 }
388
389 pub fn percent(&self, size: Size) -> Point {
391 Point::from((self.x.percent(size.width()), self.y.percent(size.height())))
392 }
393
394 pub fn unit_percent(&self, size: Size) -> Point {
396 Point::from((
397 self.x.unit_percent(size.width()),
398 self.y.unit_percent(size.height()),
399 ))
400 }
401}
402
403impl Default for OnPlanePos {
404 fn default() -> Self {
405 OnPlanePos::new(Default::default(), Default::default())
406 }
407}
408
409#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
411pub struct OnLinePos {
412 pos: f64,
413 kind: OnLinePosKind,
414}
415
416impl OnLinePos {
417 pub fn new(pos: f64, kind: OnLinePosKind) -> OnLinePos {
418 OnLinePos { pos, kind }
419 }
420
421 pub fn from_unit_percent(pos: f64) -> OnLinePos {
422 Self::new(pos * 100.0, OnLinePosKind::Percentage)
423 }
424
425 pub fn pos(&self) -> f64 {
426 self.pos
427 }
428
429 pub fn pixels(&self, line_length: f64) -> f64 {
431 match self.kind {
432 OnLinePosKind::Pixels => self.pos,
433 OnLinePosKind::Percentage => line_length * self.pos / 100.0,
434 }
435 }
436
437 pub fn percent(&self, line_length: f64) -> f64 {
439 match self.kind {
440 OnLinePosKind::Pixels => self.pos / line_length * 100.0,
441 OnLinePosKind::Percentage => self.pos,
442 }
443 }
444
445 pub fn unit_percent(&self, line_length: f64) -> f64 {
447 self.percent(line_length) / 100.0
448 }
449}
450
451impl Default for OnLinePos {
452 fn default() -> Self {
453 Self {
454 pos: 0.0,
455 kind: OnLinePosKind::default(),
456 }
457 }
458}
459
460impl<N> TryFrom<(N, &str)> for OnLinePos
461where
462 N: Into<f64>,
463{
464 type Error = ();
465
466 fn try_from(value: (N, &str)) -> Result<Self, Self::Error> {
467 let kind = OnLinePosKind::try_from(value.1)?;
468 Ok(OnLinePos {
469 pos: (value.0).into(),
470 kind,
471 })
472 }
473}
474
475#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
477pub enum OnLinePosKind {
478 Percentage,
480 Pixels,
482}
483
484impl TryFrom<&str> for OnLinePosKind {
485 type Error = ();
486
487 fn try_from(value: &str) -> Result<Self, Self::Error> {
488 match value {
489 "px" => Ok(OnLinePosKind::Pixels),
490 "%" => Ok(OnLinePosKind::Percentage),
491 _ => Err(()),
492 }
493 }
494}
495
496impl Default for OnLinePosKind {
497 fn default() -> Self {
498 Self::Pixels
499 }
500}
501
502impl Neg for OnLinePos {
503 type Output = OnLinePos;
504
505 fn neg(mut self) -> Self::Output {
506 self.pos = -self.pos;
507 self
508 }
509}