1use alloc::{
4 string::{String, ToString},
5 vec::Vec,
6};
7use core::fmt;
8use std::num::ParseFloatError;
9use crate::corety::AzString;
10
11#[cfg(feature = "parser")]
12use crate::props::basic::{
13 error::WrongComponentCountError,
14 length::parse_float_value,
15 parse::{parse_parentheses, ParenthesisParseError, ParenthesisParseErrorOwned},
16};
17use crate::{
18 codegen::format::GetHash,
19 props::{
20 basic::{
21 angle::{
22 parse_angle_value, AngleValue, CssAngleValueParseError,
23 CssAngleValueParseErrorOwned,
24 },
25 length::{PercentageParseError, PercentageValue},
26 pixel::{
27 parse_pixel_value, CssPixelValueParseError, CssPixelValueParseErrorOwned,
28 PixelValue,
29 },
30 FloatValue,
31 },
32 formatter::PrintAsCssValue,
33 },
34};
35
36#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40#[repr(C)]
41pub struct StylePerspectiveOrigin {
42 pub x: PixelValue,
43 pub y: PixelValue,
44}
45
46impl StylePerspectiveOrigin {
47 #[must_use] pub fn interpolate(&self, other: &Self, t: f32) -> Self {
48 Self {
49 x: self.x.interpolate(&other.x, t),
50 y: self.y.interpolate(&other.y, t),
51 }
52 }
53}
54
55impl PrintAsCssValue for StylePerspectiveOrigin {
56 fn print_as_css_value(&self) -> String {
57 format!("{} {}", self.x, self.y)
58 }
59}
60
61#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
63#[repr(C)]
64pub struct StyleTransformOrigin {
65 pub x: PixelValue,
66 pub y: PixelValue,
67}
68
69impl Default for StyleTransformOrigin {
70 fn default() -> Self {
71 Self {
72 x: PixelValue::const_percent(50),
73 y: PixelValue::const_percent(50),
74 }
75 }
76}
77
78impl StyleTransformOrigin {
79 #[must_use] pub fn interpolate(&self, other: &Self, t: f32) -> Self {
80 Self {
81 x: self.x.interpolate(&other.x, t),
82 y: self.y.interpolate(&other.y, t),
83 }
84 }
85}
86
87impl PrintAsCssValue for StyleTransformOrigin {
88 fn print_as_css_value(&self) -> String {
89 format!("{} {}", self.x, self.y)
90 }
91}
92
93impl crate::codegen::format::FormatAsRustCode for StylePerspectiveOrigin {
95 fn format_as_rust_code(&self, _tabs: usize) -> String {
96 format!(
97 "StylePerspectiveOrigin {{ x: {}, y: {} }}",
98 crate::codegen::format::format_pixel_value(&self.x),
99 crate::codegen::format::format_pixel_value(&self.y)
100 )
101 }
102}
103
104impl crate::codegen::format::FormatAsRustCode for StyleTransformOrigin {
106 fn format_as_rust_code(&self, _tabs: usize) -> String {
107 format!(
108 "StyleTransformOrigin {{ x: {}, y: {} }}",
109 crate::codegen::format::format_pixel_value(&self.x),
110 crate::codegen::format::format_pixel_value(&self.y)
111 )
112 }
113}
114
115#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
117#[repr(C)]
118pub enum StyleBackfaceVisibility {
119 #[default]
120 Visible,
121 Hidden,
122}
123
124impl PrintAsCssValue for StyleBackfaceVisibility {
125 fn print_as_css_value(&self) -> String {
126 String::from(match self {
127 Self::Hidden => "hidden",
128 Self::Visible => "visible",
129 })
130 }
131}
132
133#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
135#[repr(C, u8)]
136pub enum StyleTransform {
137 Matrix(StyleTransformMatrix2D),
138 Matrix3D(StyleTransformMatrix3D),
139 Translate(StyleTransformTranslate2D),
140 Translate3D(StyleTransformTranslate3D),
141 TranslateX(PixelValue),
142 TranslateY(PixelValue),
143 TranslateZ(PixelValue),
144 Rotate(AngleValue),
145 Rotate3D(StyleTransformRotate3D),
146 RotateX(AngleValue),
147 RotateY(AngleValue),
148 RotateZ(AngleValue),
149 Scale(StyleTransformScale2D),
150 Scale3D(StyleTransformScale3D),
151 ScaleX(PercentageValue),
152 ScaleY(PercentageValue),
153 ScaleZ(PercentageValue),
154 Skew(StyleTransformSkew2D),
155 SkewX(AngleValue),
156 SkewY(AngleValue),
157 Perspective(PixelValue),
158}
159
160impl_option!(
161 StyleTransform,
162 OptionStyleTransform,
163 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd]
164);
165
166impl_vec!(StyleTransform, StyleTransformVec, StyleTransformVecDestructor, StyleTransformVecDestructorType, StyleTransformVecSlice, OptionStyleTransform);
167impl_vec_debug!(StyleTransform, StyleTransformVec);
168impl_vec_partialord!(StyleTransform, StyleTransformVec);
169impl_vec_ord!(StyleTransform, StyleTransformVec);
170impl_vec_clone!(
171 StyleTransform,
172 StyleTransformVec,
173 StyleTransformVecDestructor
174);
175impl_vec_partialeq!(StyleTransform, StyleTransformVec);
176impl_vec_eq!(StyleTransform, StyleTransformVec);
177impl_vec_hash!(StyleTransform, StyleTransformVec);
178
179impl PrintAsCssValue for StyleTransformVec {
180 fn print_as_css_value(&self) -> String {
181 self.as_ref()
182 .iter()
183 .map(PrintAsCssValue::print_as_css_value)
184 .collect::<Vec<_>>()
185 .join(" ")
186 }
187}
188
189impl crate::codegen::format::FormatAsRustCode for StyleTransformVec {
191 fn format_as_rust_code(&self, _tabs: usize) -> String {
192 format!(
193 "StyleTransformVec::from_const_slice(STYLE_TRANSFORM_{}_ITEMS)",
194 self.get_hash()
195 )
196 }
197}
198
199impl PrintAsCssValue for StyleTransform {
200 fn print_as_css_value(&self) -> String {
201 match self {
202 Self::Matrix(m) => format!(
203 "matrix({}, {}, {}, {}, {}, {})",
204 m.a, m.b, m.c, m.d, m.tx, m.ty
205 ),
206 Self::Matrix3D(m) => format!(
207 "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
208 m.m11,
209 m.m12,
210 m.m13,
211 m.m14,
212 m.m21,
213 m.m22,
214 m.m23,
215 m.m24,
216 m.m31,
217 m.m32,
218 m.m33,
219 m.m34,
220 m.m41,
221 m.m42,
222 m.m43,
223 m.m44
224 ),
225 Self::Translate(t) => format!("translate({}, {})", t.x, t.y),
226 Self::Translate3D(t) => format!("translate3d({}, {}, {})", t.x, t.y, t.z),
227 Self::TranslateX(x) => format!("translateX({x})"),
228 Self::TranslateY(y) => format!("translateY({y})"),
229 Self::TranslateZ(z) => format!("translateZ({z})"),
230 Self::Rotate(r) => format!("rotate({r})"),
231 Self::Rotate3D(r) => {
232 format!("rotate3d({}, {}, {}, {})", r.x, r.y, r.z, r.angle)
233 }
234 Self::RotateX(x) => format!("rotateX({x})"),
235 Self::RotateY(y) => format!("rotateY({y})"),
236 Self::RotateZ(z) => format!("rotateZ({z})"),
237 Self::Scale(s) => format!("scale({}, {})", s.x, s.y),
238 Self::Scale3D(s) => format!("scale3d({}, {}, {})", s.x, s.y, s.z),
239 Self::ScaleX(x) => format!("scaleX({x})"),
240 Self::ScaleY(y) => format!("scaleY({y})"),
241 Self::ScaleZ(z) => format!("scaleZ({z})"),
242 Self::Skew(sk) => format!("skew({}, {})", sk.x, sk.y),
243 Self::SkewX(x) => format!("skewX({x})"),
244 Self::SkewY(y) => format!("skewY({y})"),
245 Self::Perspective(dist) => format!("perspective({dist})"),
246 }
247 }
248}
249
250#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
252#[repr(C)]
253pub struct StyleTransformMatrix2D {
254 pub a: FloatValue,
255 pub b: FloatValue,
256 pub c: FloatValue,
257 pub d: FloatValue,
258 pub tx: FloatValue,
259 pub ty: FloatValue,
260}
261
262impl Default for StyleTransformMatrix2D {
263 fn default() -> Self {
264 Self {
265 a: FloatValue::const_new(1),
266 b: FloatValue::const_new(0),
267 c: FloatValue::const_new(0),
268 d: FloatValue::const_new(1),
269 tx: FloatValue::const_new(0),
270 ty: FloatValue::const_new(0),
271 }
272 }
273}
274
275#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
277#[repr(C)]
278pub struct StyleTransformMatrix3D {
279 pub m11: FloatValue,
280 pub m12: FloatValue,
281 pub m13: FloatValue,
282 pub m14: FloatValue,
283 pub m21: FloatValue,
284 pub m22: FloatValue,
285 pub m23: FloatValue,
286 pub m24: FloatValue,
287 pub m31: FloatValue,
288 pub m32: FloatValue,
289 pub m33: FloatValue,
290 pub m34: FloatValue,
291 pub m41: FloatValue,
292 pub m42: FloatValue,
293 pub m43: FloatValue,
294 pub m44: FloatValue,
295}
296
297impl Default for StyleTransformMatrix3D {
298 fn default() -> Self {
299 Self {
300 m11: FloatValue::const_new(1),
301 m12: FloatValue::const_new(0),
302 m13: FloatValue::const_new(0),
303 m14: FloatValue::const_new(0),
304 m21: FloatValue::const_new(0),
305 m22: FloatValue::const_new(1),
306 m23: FloatValue::const_new(0),
307 m24: FloatValue::const_new(0),
308 m31: FloatValue::const_new(0),
309 m32: FloatValue::const_new(0),
310 m33: FloatValue::const_new(1),
311 m34: FloatValue::const_new(0),
312 m41: FloatValue::const_new(0),
313 m42: FloatValue::const_new(0),
314 m43: FloatValue::const_new(0),
315 m44: FloatValue::const_new(1),
316 }
317 }
318}
319
320#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
322#[repr(C)]
323pub struct StyleTransformTranslate2D {
324 pub x: PixelValue,
325 pub y: PixelValue,
326}
327
328#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
330#[repr(C)]
331pub struct StyleTransformTranslate3D {
332 pub x: PixelValue,
333 pub y: PixelValue,
334 pub z: PixelValue,
335}
336
337#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
339#[repr(C)]
340pub struct StyleTransformRotate3D {
341 pub x: FloatValue,
342 pub y: FloatValue,
343 pub z: FloatValue,
344 pub angle: AngleValue,
345}
346
347#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
349#[repr(C)]
350pub struct StyleTransformScale2D {
351 pub x: FloatValue,
352 pub y: FloatValue,
353}
354
355#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
357#[repr(C)]
358pub struct StyleTransformScale3D {
359 pub x: FloatValue,
360 pub y: FloatValue,
361 pub z: FloatValue,
362}
363
364#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
366#[repr(C)]
367pub struct StyleTransformSkew2D {
368 pub x: AngleValue,
369 pub y: AngleValue,
370}
371
372#[derive(Clone, PartialEq, Eq)]
375pub enum CssStyleTransformParseError<'a> {
376 InvalidTransform(&'a str),
377 InvalidParenthesis(ParenthesisParseError<'a>),
378 WrongNumberOfComponents {
379 expected: usize,
380 got: usize,
381 input: &'a str,
382 },
383 NumberParseError(ParseFloatError),
384 PixelValueParseError(CssPixelValueParseError<'a>),
385 AngleValueParseError(CssAngleValueParseError<'a>),
386 PercentageValueParseError(PercentageParseError),
387}
388
389impl_debug_as_display!(CssStyleTransformParseError<'a>);
390impl_display! { CssStyleTransformParseError<'a>, {
391 InvalidTransform(e) => format!("Invalid transform property: \"{}\"", e),
392 InvalidParenthesis(e) => format!("Invalid transform property - parenthesis error: {}", e),
393 WrongNumberOfComponents { expected, got, input } => format!("Invalid number of components: expected {}, got {}: \"{}\"", expected, got, input),
394 NumberParseError(e) => format!("Could not parse number: {}", e),
395 PixelValueParseError(e) => format!("Invalid pixel value: {}", e),
396 AngleValueParseError(e) => format!("Invalid angle value: {}", e),
397 PercentageValueParseError(e) => format!("Error parsing percentage: {}", e),
398}}
399
400impl_from! { ParenthesisParseError<'a>, CssStyleTransformParseError::InvalidParenthesis }
401impl_from! { CssPixelValueParseError<'a>, CssStyleTransformParseError::PixelValueParseError }
402impl_from! { CssAngleValueParseError<'a>, CssStyleTransformParseError::AngleValueParseError }
403impl From<ParseFloatError> for CssStyleTransformParseError<'_> {
406 fn from(e: ParseFloatError) -> Self {
407 Self::NumberParseError(e)
408 }
409}
410
411impl From<PercentageParseError> for CssStyleTransformParseError<'_> {
412 fn from(p: PercentageParseError) -> Self {
413 Self::PercentageValueParseError(p)
414 }
415}
416
417#[derive(Debug, Clone, PartialEq, Eq)]
418#[repr(C, u8)]
419pub enum CssStyleTransformParseErrorOwned {
420 InvalidTransform(AzString),
421 InvalidParenthesis(ParenthesisParseErrorOwned),
422 WrongNumberOfComponents(WrongComponentCountError),
423 NumberParseError(crate::props::basic::error::ParseFloatError),
424 PixelValueParseError(CssPixelValueParseErrorOwned),
425 AngleValueParseError(CssAngleValueParseErrorOwned),
426 PercentageValueParseError(PercentageParseError),
427}
428
429impl CssStyleTransformParseError<'_> {
430 #[must_use] pub fn to_contained(&self) -> CssStyleTransformParseErrorOwned {
431 match self {
432 Self::InvalidTransform(s) => {
433 CssStyleTransformParseErrorOwned::InvalidTransform((*s).to_string().into())
434 }
435 Self::InvalidParenthesis(e) => {
436 CssStyleTransformParseErrorOwned::InvalidParenthesis(e.to_contained())
437 }
438 Self::WrongNumberOfComponents {
439 expected,
440 got,
441 input,
442 } => CssStyleTransformParseErrorOwned::WrongNumberOfComponents(WrongComponentCountError {
443 expected: *expected,
444 got: *got,
445 input: (*input).to_string().into(),
446 }),
447 Self::NumberParseError(e) => {
448 CssStyleTransformParseErrorOwned::NumberParseError(e.clone().into())
449 }
450 Self::PixelValueParseError(e) => {
451 CssStyleTransformParseErrorOwned::PixelValueParseError(e.to_contained())
452 }
453 Self::AngleValueParseError(e) => {
454 CssStyleTransformParseErrorOwned::AngleValueParseError(e.to_contained())
455 }
456 Self::PercentageValueParseError(e) => {
457 CssStyleTransformParseErrorOwned::PercentageValueParseError(e.clone())
458 }
459 }
460 }
461}
462
463impl CssStyleTransformParseErrorOwned {
464 #[must_use] pub fn to_shared(&self) -> CssStyleTransformParseError<'_> {
465 match self {
466 Self::InvalidTransform(s) => CssStyleTransformParseError::InvalidTransform(s),
467 Self::InvalidParenthesis(e) => {
468 CssStyleTransformParseError::InvalidParenthesis(e.to_shared())
469 }
470 Self::WrongNumberOfComponents(e) => CssStyleTransformParseError::WrongNumberOfComponents {
471 expected: e.expected,
472 got: e.got,
473 input: e.input.as_str(),
474 },
475 Self::NumberParseError(e) => CssStyleTransformParseError::NumberParseError(e.to_std()),
476 Self::PixelValueParseError(e) => {
477 CssStyleTransformParseError::PixelValueParseError(e.to_shared())
478 }
479 Self::AngleValueParseError(e) => {
480 CssStyleTransformParseError::AngleValueParseError(e.to_shared())
481 }
482 Self::PercentageValueParseError(e) => {
483 CssStyleTransformParseError::PercentageValueParseError(e.clone())
484 }
485 }
486 }
487}
488
489#[derive(Clone, PartialEq, Eq)]
490pub enum CssStyleTransformOriginParseError<'a> {
491 WrongNumberOfComponents {
492 expected: usize,
493 got: usize,
494 input: &'a str,
495 },
496 PixelValueParseError(CssPixelValueParseError<'a>),
497}
498
499impl_debug_as_display!(CssStyleTransformOriginParseError<'a>);
500impl_display! { CssStyleTransformOriginParseError<'a>, {
501 WrongNumberOfComponents { expected, got, input } => format!("Invalid number of components: expected {}, got {}: \"{}\"", expected, got, input),
502 PixelValueParseError(e) => format!("Invalid pixel value: {}", e),
503}}
504impl_from! { CssPixelValueParseError<'a>, CssStyleTransformOriginParseError::PixelValueParseError }
505
506#[derive(Debug, Clone, PartialEq, Eq)]
507#[repr(C, u8)]
508pub enum CssStyleTransformOriginParseErrorOwned {
509 WrongNumberOfComponents(WrongComponentCountError),
510 PixelValueParseError(CssPixelValueParseErrorOwned),
511}
512
513impl CssStyleTransformOriginParseError<'_> {
514 #[must_use] pub fn to_contained(&self) -> CssStyleTransformOriginParseErrorOwned {
515 match self {
516 Self::WrongNumberOfComponents {
517 expected,
518 got,
519 input,
520 } => CssStyleTransformOriginParseErrorOwned::WrongNumberOfComponents(WrongComponentCountError {
521 expected: *expected,
522 got: *got,
523 input: (*input).to_string().into(),
524 }),
525 Self::PixelValueParseError(e) => {
526 CssStyleTransformOriginParseErrorOwned::PixelValueParseError(e.to_contained())
527 }
528 }
529 }
530}
531
532impl CssStyleTransformOriginParseErrorOwned {
533 #[must_use] pub fn to_shared(&self) -> CssStyleTransformOriginParseError<'_> {
534 match self {
535 Self::WrongNumberOfComponents(e) => CssStyleTransformOriginParseError::WrongNumberOfComponents {
536 expected: e.expected,
537 got: e.got,
538 input: e.input.as_str(),
539 },
540 Self::PixelValueParseError(e) => {
541 CssStyleTransformOriginParseError::PixelValueParseError(e.to_shared())
542 }
543 }
544 }
545}
546
547#[derive(Clone, PartialEq, Eq)]
548pub enum CssStylePerspectiveOriginParseError<'a> {
549 WrongNumberOfComponents {
550 expected: usize,
551 got: usize,
552 input: &'a str,
553 },
554 PixelValueParseError(CssPixelValueParseError<'a>),
555}
556
557impl_debug_as_display!(CssStylePerspectiveOriginParseError<'a>);
558impl_display! { CssStylePerspectiveOriginParseError<'a>, {
559 WrongNumberOfComponents { expected, got, input } => format!("Invalid number of components: expected {}, got {}: \"{}\"", expected, got, input),
560 PixelValueParseError(e) => format!("Invalid pixel value: {}", e),
561}}
562impl_from! { CssPixelValueParseError<'a>, CssStylePerspectiveOriginParseError::PixelValueParseError }
563
564#[derive(Debug, Clone, PartialEq, Eq)]
565#[repr(C, u8)]
566pub enum CssStylePerspectiveOriginParseErrorOwned {
567 WrongNumberOfComponents(WrongComponentCountError),
568 PixelValueParseError(CssPixelValueParseErrorOwned),
569}
570
571impl CssStylePerspectiveOriginParseError<'_> {
572 #[must_use] pub fn to_contained(&self) -> CssStylePerspectiveOriginParseErrorOwned {
573 match self {
574 Self::WrongNumberOfComponents {
575 expected,
576 got,
577 input,
578 } => CssStylePerspectiveOriginParseErrorOwned::WrongNumberOfComponents(WrongComponentCountError {
579 expected: *expected,
580 got: *got,
581 input: (*input).to_string().into(),
582 }),
583 Self::PixelValueParseError(e) => {
584 CssStylePerspectiveOriginParseErrorOwned::PixelValueParseError(e.to_contained())
585 }
586 }
587 }
588}
589
590impl CssStylePerspectiveOriginParseErrorOwned {
591 #[must_use] pub fn to_shared(&self) -> CssStylePerspectiveOriginParseError<'_> {
592 match self {
593 Self::WrongNumberOfComponents(e) => CssStylePerspectiveOriginParseError::WrongNumberOfComponents {
594 expected: e.expected,
595 got: e.got,
596 input: e.input.as_str(),
597 },
598 Self::PixelValueParseError(e) => {
599 CssStylePerspectiveOriginParseError::PixelValueParseError(e.to_shared())
600 }
601 }
602 }
603}
604
605#[derive(Clone, PartialEq, Eq)]
606pub enum CssBackfaceVisibilityParseError<'a> {
607 InvalidValue(&'a str),
608}
609
610impl_debug_as_display!(CssBackfaceVisibilityParseError<'a>);
611impl_display! { CssBackfaceVisibilityParseError<'a>, {
612 InvalidValue(s) => format!("Invalid value for backface-visibility: \"{}\", expected \"visible\" or \"hidden\"", s),
613}}
614
615#[derive(Debug, Clone, PartialEq, Eq)]
616#[repr(C, u8)]
617pub enum CssBackfaceVisibilityParseErrorOwned {
618 InvalidValue(AzString),
619}
620
621impl CssBackfaceVisibilityParseError<'_> {
622 #[must_use] pub fn to_contained(&self) -> CssBackfaceVisibilityParseErrorOwned {
623 match self {
624 Self::InvalidValue(s) => {
625 CssBackfaceVisibilityParseErrorOwned::InvalidValue((*s).to_string().into())
626 }
627 }
628 }
629}
630
631impl CssBackfaceVisibilityParseErrorOwned {
632 #[must_use] pub fn to_shared(&self) -> CssBackfaceVisibilityParseError<'_> {
633 match self {
634 Self::InvalidValue(s) => CssBackfaceVisibilityParseError::InvalidValue(s),
635 }
636 }
637}
638
639#[cfg(feature = "parser")]
642pub fn parse_style_transform_vec(
646 input: &str,
647) -> Result<StyleTransformVec, CssStyleTransformParseError<'_>> {
648 crate::props::basic::parse::split_string_respect_whitespace(input)
649 .iter()
650 .map(|i| parse_style_transform(i))
651 .collect::<Result<Vec<_>, _>>()
652 .map(Into::into)
653}
654
655#[cfg(feature = "parser")]
656#[allow(clippy::too_many_lines)] pub fn parse_style_transform(
661 input: &str,
662) -> Result<StyleTransform, CssStyleTransformParseError<'_>> {
663 fn get_numbers(
664 input: &str,
665 expected: usize,
666 ) -> Result<Vec<f32>, CssStyleTransformParseError<'_>> {
667 let numbers: Vec<_> = input
668 .split(',')
669 .map(|s| s.trim().parse::<f32>())
670 .collect::<Result<_, _>>()?;
671 if numbers.len() == expected {
672 Ok(numbers)
673 } else {
674 Err(CssStyleTransformParseError::WrongNumberOfComponents {
675 expected,
676 got: numbers.len(),
677 input,
678 })
679 }
680 }
681
682 let (transform_type, transform_values) = parse_parentheses(
683 input,
684 &[
685 "matrix",
686 "matrix3d",
687 "translate",
688 "translate3d",
689 "translateX",
690 "translateY",
691 "translateZ",
692 "rotate",
693 "rotate3d",
694 "rotateX",
695 "rotateY",
696 "rotateZ",
697 "scale",
698 "scale3d",
699 "scaleX",
700 "scaleY",
701 "scaleZ",
702 "skew",
703 "skewX",
704 "skewY",
705 "perspective",
706 ],
707 )?;
708
709 match transform_type {
710 "matrix" => {
711 let nums = get_numbers(transform_values, 6)?;
712 Ok(StyleTransform::Matrix(StyleTransformMatrix2D {
713 a: FloatValue::new(nums[0]),
714 b: FloatValue::new(nums[1]),
715 c: FloatValue::new(nums[2]),
716 d: FloatValue::new(nums[3]),
717 tx: FloatValue::new(nums[4]),
718 ty: FloatValue::new(nums[5]),
719 }))
720 }
721 "matrix3d" => {
722 let nums = get_numbers(transform_values, 16)?;
723 Ok(StyleTransform::Matrix3D(StyleTransformMatrix3D {
724 m11: FloatValue::new(nums[0]),
725 m12: FloatValue::new(nums[1]),
726 m13: FloatValue::new(nums[2]),
727 m14: FloatValue::new(nums[3]),
728 m21: FloatValue::new(nums[4]),
729 m22: FloatValue::new(nums[5]),
730 m23: FloatValue::new(nums[6]),
731 m24: FloatValue::new(nums[7]),
732 m31: FloatValue::new(nums[8]),
733 m32: FloatValue::new(nums[9]),
734 m33: FloatValue::new(nums[10]),
735 m34: FloatValue::new(nums[11]),
736 m41: FloatValue::new(nums[12]),
737 m42: FloatValue::new(nums[13]),
738 m43: FloatValue::new(nums[14]),
739 m44: FloatValue::new(nums[15]),
740 }))
741 }
742 "translate" => {
743 let components: Vec<_> = transform_values.split(',').collect();
744
745 if components.len() > 2 {
747 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
748 expected: 2,
749 got: components.len(),
750 input: transform_values,
751 });
752 }
753
754 let x = parse_pixel_value(
755 components.first()
756 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
757 expected: 2,
758 got: 0,
759 input: transform_values,
760 })?
761 .trim(),
762 )?;
763 let y = match components.get(1) {
764 Some(c) => parse_pixel_value(c.trim())?,
765 None => PixelValue::px(0.0),
766 };
767 Ok(StyleTransform::Translate(StyleTransformTranslate2D {
768 x,
769 y,
770 }))
771 }
772 "translate3d" => {
773 let components: Vec<_> = transform_values.split(',').collect();
774 let x = parse_pixel_value(
775 components.first()
776 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
777 expected: 3,
778 got: 0,
779 input: transform_values,
780 })?
781 .trim(),
782 )?;
783 let y = parse_pixel_value(
784 components
785 .get(1)
786 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
787 expected: 3,
788 got: 1,
789 input: transform_values,
790 })?
791 .trim(),
792 )?;
793 let z = parse_pixel_value(
794 components
795 .get(2)
796 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
797 expected: 3,
798 got: 2,
799 input: transform_values,
800 })?
801 .trim(),
802 )?;
803 Ok(StyleTransform::Translate3D(StyleTransformTranslate3D {
804 x,
805 y,
806 z,
807 }))
808 }
809 "translateX" => Ok(StyleTransform::TranslateX(parse_pixel_value(
810 transform_values,
811 )?)),
812 "translateY" => Ok(StyleTransform::TranslateY(parse_pixel_value(
813 transform_values,
814 )?)),
815 "translateZ" => Ok(StyleTransform::TranslateZ(parse_pixel_value(
816 transform_values,
817 )?)),
818 "rotate" => Ok(StyleTransform::Rotate(parse_angle_value(transform_values)?)),
819 "rotate3d" => {
820 let parts: Vec<_> = transform_values.splitn(4, ',').collect();
821 if parts.len() != 4 {
822 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
823 expected: 4,
824 got: parts.len(),
825 input: transform_values,
826 });
827 }
828 let x = parts[0].trim().parse::<f32>()?;
829 let y = parts[1].trim().parse::<f32>()?;
830 let z = parts[2].trim().parse::<f32>()?;
831 let angle = parse_angle_value(parts[3].trim())?;
832 Ok(StyleTransform::Rotate3D(StyleTransformRotate3D {
833 x: FloatValue::new(x),
834 y: FloatValue::new(y),
835 z: FloatValue::new(z),
836 angle,
837 }))
838 }
839 "rotateX" => Ok(StyleTransform::RotateX(parse_angle_value(
840 transform_values,
841 )?)),
842 "rotateY" => Ok(StyleTransform::RotateY(parse_angle_value(
843 transform_values,
844 )?)),
845 "rotateZ" => Ok(StyleTransform::RotateZ(parse_angle_value(
846 transform_values,
847 )?)),
848 "scale" => {
849 let parts: Vec<_> = transform_values.split(',').collect();
850 if parts.is_empty() || parts.len() > 2 {
851 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
852 expected: 2,
853 got: parts.len(),
854 input: transform_values,
855 });
856 }
857 let x = parts[0].trim().parse::<f32>()?;
858 let y = if parts.len() == 2 {
859 parts[1].trim().parse::<f32>()?
860 } else {
861 x
862 };
863 Ok(StyleTransform::Scale(StyleTransformScale2D {
864 x: FloatValue::new(x),
865 y: FloatValue::new(y),
866 }))
867 }
868 "scale3d" => {
869 let nums = get_numbers(transform_values, 3)?;
870 Ok(StyleTransform::Scale3D(StyleTransformScale3D {
871 x: FloatValue::new(nums[0]),
872 y: FloatValue::new(nums[1]),
873 z: FloatValue::new(nums[2]),
874 }))
875 }
876 "scaleX" => Ok(StyleTransform::ScaleX(PercentageValue::new(
877 transform_values.trim().parse::<f32>()? * 100.0,
878 ))),
879 "scaleY" => Ok(StyleTransform::ScaleY(PercentageValue::new(
880 transform_values.trim().parse::<f32>()? * 100.0,
881 ))),
882 "scaleZ" => Ok(StyleTransform::ScaleZ(PercentageValue::new(
883 transform_values.trim().parse::<f32>()? * 100.0,
884 ))),
885 "skew" => {
886 let components: Vec<_> = transform_values.split(',').collect();
887 if components.is_empty() || components.len() > 2 {
888 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
889 expected: 2,
890 got: components.len(),
891 input: transform_values,
892 });
893 }
894 let x = parse_angle_value(components[0].trim())?;
895 let y = match components.get(1) {
896 Some(c) => parse_angle_value(c.trim())?,
897 None => AngleValue::deg(0.0),
898 };
899 Ok(StyleTransform::Skew(StyleTransformSkew2D { x, y }))
900 }
901 "skewX" => Ok(StyleTransform::SkewX(parse_angle_value(transform_values)?)),
902 "skewY" => Ok(StyleTransform::SkewY(parse_angle_value(transform_values)?)),
903 "perspective" => Ok(StyleTransform::Perspective(parse_pixel_value(
904 transform_values,
905 )?)),
906 _ => unreachable!(),
907 }
908}
909
910#[cfg(feature = "parser")]
911pub fn parse_style_transform_origin(
915 input: &str,
916) -> Result<StyleTransformOrigin, CssStyleTransformOriginParseError<'_>> {
917 fn parse_position_component(
919 s: &str,
920 is_horizontal: bool,
921 ) -> Result<PixelValue, CssPixelValueParseError<'_>> {
922 match s.trim() {
923 "left" if is_horizontal => Ok(PixelValue::percent(0.0)),
924 "center" => Ok(PixelValue::percent(50.0)),
925 "right" if is_horizontal => Ok(PixelValue::percent(100.0)),
926 "top" if !is_horizontal => Ok(PixelValue::percent(0.0)),
927 "bottom" if !is_horizontal => Ok(PixelValue::percent(100.0)),
928 _ => parse_pixel_value(s),
929 }
930 }
931
932 let components: Vec<_> = input.split_whitespace().collect();
933 if components.len() != 2 {
934 return Err(CssStyleTransformOriginParseError::WrongNumberOfComponents {
935 expected: 2,
936 got: components.len(),
937 input,
938 });
939 }
940
941 let x = parse_position_component(components[0], true)?;
942 let y = parse_position_component(components[1], false)?;
943 Ok(StyleTransformOrigin { x, y })
944}
945
946#[cfg(feature = "parser")]
947pub fn parse_style_perspective_origin(
951 input: &str,
952) -> Result<StylePerspectiveOrigin, CssStylePerspectiveOriginParseError<'_>> {
953 let components: Vec<_> = input.split_whitespace().collect();
954 if components.len() != 2 {
955 return Err(
956 CssStylePerspectiveOriginParseError::WrongNumberOfComponents {
957 expected: 2,
958 got: components.len(),
959 input,
960 },
961 );
962 }
963 let x = parse_pixel_value(components[0])?;
964 let y = parse_pixel_value(components[1])?;
965 Ok(StylePerspectiveOrigin { x, y })
966}
967
968#[cfg(feature = "parser")]
969pub fn parse_style_backface_visibility(
973 input: &str,
974) -> Result<StyleBackfaceVisibility, CssBackfaceVisibilityParseError<'_>> {
975 match input.trim() {
976 "visible" => Ok(StyleBackfaceVisibility::Visible),
977 "hidden" => Ok(StyleBackfaceVisibility::Hidden),
978 _ => Err(CssBackfaceVisibilityParseError::InvalidValue(input)),
979 }
980}
981
982#[cfg(all(test, feature = "parser"))]
983mod tests {
984 #![allow(clippy::float_cmp)]
986 use super::*;
987
988 #[test]
989 fn test_parse_transform_vec() {
990 let result =
991 parse_style_transform_vec("translateX(10px) rotate(90deg) scale(0.5, 0.5)").unwrap();
992 assert_eq!(result.len(), 3);
993 assert!(matches!(
994 result.as_slice()[0],
995 StyleTransform::TranslateX(_)
996 ));
997 assert!(matches!(result.as_slice()[1], StyleTransform::Rotate(_)));
998 assert!(matches!(result.as_slice()[2], StyleTransform::Scale(_)));
999 }
1000
1001 #[test]
1002 fn test_parse_transform_functions() {
1003 assert_eq!(
1005 parse_style_transform("translateX(50%)").unwrap(),
1006 StyleTransform::TranslateX(PixelValue::percent(50.0))
1007 );
1008 let translate = parse_style_transform("translate(10px, -20px)").unwrap();
1009 if let StyleTransform::Translate(t) = translate {
1010 assert_eq!(t.x, PixelValue::px(10.0));
1011 assert_eq!(t.y, PixelValue::px(-20.0));
1012 } else {
1013 panic!("Expected Translate");
1014 }
1015
1016 assert_eq!(
1018 parse_style_transform("scaleY(1.2)").unwrap(),
1019 StyleTransform::ScaleY(PercentageValue::new(120.0))
1020 );
1021 let scale = parse_style_transform("scale(2, 0.5)").unwrap();
1022 if let StyleTransform::Scale(s) = scale {
1023 assert_eq!(s.x.get(), 2.0);
1024 assert_eq!(s.y.get(), 0.5);
1025 } else {
1026 panic!("Expected Scale");
1027 }
1028
1029 assert_eq!(
1031 parse_style_transform("rotate(0.25turn)").unwrap(),
1032 StyleTransform::Rotate(AngleValue::turn(0.25))
1033 );
1034
1035 assert_eq!(
1037 parse_style_transform("skewX(-10deg)").unwrap(),
1038 StyleTransform::SkewX(AngleValue::deg(-10.0))
1039 );
1040 let skew = parse_style_transform("skew(20deg, 30deg)").unwrap();
1041 if let StyleTransform::Skew(s) = skew {
1042 assert_eq!(s.x, AngleValue::deg(20.0));
1043 assert_eq!(s.y, AngleValue::deg(30.0));
1044 } else {
1045 panic!("Expected Skew");
1046 }
1047 }
1048
1049 #[test]
1050 fn test_parse_transform_origin() {
1051 let result = parse_style_transform_origin("50% 50%").unwrap();
1052 assert_eq!(result.x, PixelValue::percent(50.0));
1053 assert_eq!(result.y, PixelValue::percent(50.0));
1054
1055 let result = parse_style_transform_origin("left top").unwrap();
1056 assert_eq!(result.x, PixelValue::percent(0.0));
1057 assert_eq!(result.y, PixelValue::percent(0.0));
1058
1059 let result = parse_style_transform_origin("20px bottom").unwrap();
1060 assert_eq!(result.x, PixelValue::px(20.0));
1061 assert_eq!(result.y, PixelValue::percent(100.0));
1062 }
1063
1064 #[test]
1065 fn test_parse_backface_visibility() {
1066 assert_eq!(
1067 parse_style_backface_visibility("visible").unwrap(),
1068 StyleBackfaceVisibility::Visible
1069 );
1070 assert_eq!(
1071 parse_style_backface_visibility("hidden").unwrap(),
1072 StyleBackfaceVisibility::Hidden
1073 );
1074 assert!(parse_style_backface_visibility("none").is_err());
1075 }
1076
1077 #[test]
1078 fn test_parse_transform_errors() {
1079 assert!(parse_style_transform("translatex(10px)").is_err());
1081 assert!(parse_style_transform("translate(1, 2, 3)").is_err());
1083 let scale1 = parse_style_transform("scale(2)").unwrap();
1085 if let StyleTransform::Scale(s) = scale1 {
1086 assert_eq!(s.x.get(), 2.0);
1087 assert_eq!(s.y.get(), 2.0);
1088 } else {
1089 panic!("Expected Scale");
1090 }
1091 let translate1 = parse_style_transform("translate(10px)").unwrap();
1092 if let StyleTransform::Translate(t) = translate1 {
1093 assert_eq!(t.x, PixelValue::px(10.0));
1094 assert_eq!(t.y, PixelValue::px(0.0));
1095 } else {
1096 panic!("Expected Translate");
1097 }
1098 let skew1 = parse_style_transform("skew(20deg)").unwrap();
1099 if let StyleTransform::Skew(s) = skew1 {
1100 assert_eq!(s.x, AngleValue::deg(20.0));
1101 assert_eq!(s.y, AngleValue::deg(0.0));
1102 } else {
1103 panic!("Expected Skew");
1104 }
1105 let rot3d = parse_style_transform("rotate3d(1, 0, 0, 45deg)").unwrap();
1107 if let StyleTransform::Rotate3D(r) = rot3d {
1108 assert_eq!(r.x.get(), 1.0);
1109 assert_eq!(r.angle, AngleValue::deg(45.0));
1110 } else {
1111 panic!("Expected Rotate3D");
1112 }
1113 assert!(parse_style_transform("rotate(10px)").is_err());
1115 assert!(parse_style_transform("translateX(auto)").is_err());
1116 }
1117}