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