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