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 format_rust_code::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 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 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::format_rust_code::FormatAsRustCode for StylePerspectiveOrigin {
95 fn format_as_rust_code(&self, _tabs: usize) -> String {
96 format!(
97 "StylePerspectiveOrigin {{ x: {}, y: {} }}",
98 crate::format_rust_code::format_pixel_value(&self.x),
99 crate::format_rust_code::format_pixel_value(&self.y)
100 )
101 }
102}
103
104impl crate::format_rust_code::FormatAsRustCode for StyleTransformOrigin {
106 fn format_as_rust_code(&self, _tabs: usize) -> String {
107 format!(
108 "StyleTransformOrigin {{ x: {}, y: {} }}",
109 crate::format_rust_code::format_pixel_value(&self.x),
110 crate::format_rust_code::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 StyleBackfaceVisibility::Hidden => "hidden",
128 StyleBackfaceVisibility::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, 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(|f| f.print_as_css_value())
184 .collect::<Vec<_>>()
185 .join(" ")
186 }
187}
188
189impl crate::format_rust_code::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 StyleTransform::Matrix(m) => format!(
203 "matrix({}, {}, {}, {}, {}, {})",
204 m.a, m.b, m.c, m.d, m.tx, m.ty
205 ),
206 StyleTransform::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 StyleTransform::Translate(t) => format!("translate({}, {})", t.x, t.y),
226 StyleTransform::Translate3D(t) => format!("translate3d({}, {}, {})", t.x, t.y, t.z),
227 StyleTransform::TranslateX(x) => format!("translateX({})", x),
228 StyleTransform::TranslateY(y) => format!("translateY({})", y),
229 StyleTransform::TranslateZ(z) => format!("translateZ({})", z),
230 StyleTransform::Rotate(r) => format!("rotate({})", r),
231 StyleTransform::Rotate3D(r) => {
232 format!("rotate3d({}, {}, {}, {})", r.x, r.y, r.z, r.angle)
233 }
234 StyleTransform::RotateX(x) => format!("rotateX({})", x),
235 StyleTransform::RotateY(y) => format!("rotateY({})", y),
236 StyleTransform::RotateZ(z) => format!("rotateZ({})", z),
237 StyleTransform::Scale(s) => format!("scale({}, {})", s.x, s.y),
238 StyleTransform::Scale3D(s) => format!("scale3d({}, {}, {})", s.x, s.y, s.z),
239 StyleTransform::ScaleX(x) => format!("scaleX({})", x),
240 StyleTransform::ScaleY(y) => format!("scaleY({})", y),
241 StyleTransform::ScaleZ(z) => format!("scaleZ({})", z),
242 StyleTransform::Skew(sk) => format!("skew({}, {})", sk.x, sk.y),
243 StyleTransform::SkewX(x) => format!("skewX({})", x),
244 StyleTransform::SkewY(y) => format!("skewY({})", y),
245 StyleTransform::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)]
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(core::num::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, CssStyleTransformParseError<'a>::NumberParseError }
404
405impl<'a> From<PercentageParseError> for CssStyleTransformParseError<'a> {
406 fn from(p: PercentageParseError) -> Self {
407 Self::PercentageValueParseError(p)
408 }
409}
410
411#[derive(Debug, Clone, PartialEq)]
412#[repr(C, u8)]
413pub enum CssStyleTransformParseErrorOwned {
414 InvalidTransform(AzString),
415 InvalidParenthesis(ParenthesisParseErrorOwned),
416 WrongNumberOfComponents(WrongComponentCountError),
417 NumberParseError(crate::props::basic::error::ParseFloatError),
418 PixelValueParseError(CssPixelValueParseErrorOwned),
419 AngleValueParseError(CssAngleValueParseErrorOwned),
420 PercentageValueParseError(PercentageParseError),
421}
422
423impl<'a> CssStyleTransformParseError<'a> {
424 pub fn to_contained(&self) -> CssStyleTransformParseErrorOwned {
425 match self {
426 Self::InvalidTransform(s) => {
427 CssStyleTransformParseErrorOwned::InvalidTransform(s.to_string().into())
428 }
429 Self::InvalidParenthesis(e) => {
430 CssStyleTransformParseErrorOwned::InvalidParenthesis(e.to_contained())
431 }
432 Self::WrongNumberOfComponents {
433 expected,
434 got,
435 input,
436 } => CssStyleTransformParseErrorOwned::WrongNumberOfComponents(WrongComponentCountError {
437 expected: *expected,
438 got: *got,
439 input: input.to_string().into(),
440 }),
441 Self::NumberParseError(e) => {
442 CssStyleTransformParseErrorOwned::NumberParseError(e.clone().into())
443 }
444 Self::PixelValueParseError(e) => {
445 CssStyleTransformParseErrorOwned::PixelValueParseError(e.to_contained())
446 }
447 Self::AngleValueParseError(e) => {
448 CssStyleTransformParseErrorOwned::AngleValueParseError(e.to_contained())
449 }
450 Self::PercentageValueParseError(e) => {
451 CssStyleTransformParseErrorOwned::PercentageValueParseError(e.clone())
452 }
453 }
454 }
455}
456
457impl CssStyleTransformParseErrorOwned {
458 pub fn to_shared<'a>(&'a self) -> CssStyleTransformParseError<'a> {
459 match self {
460 Self::InvalidTransform(s) => CssStyleTransformParseError::InvalidTransform(s),
461 Self::InvalidParenthesis(e) => {
462 CssStyleTransformParseError::InvalidParenthesis(e.to_shared())
463 }
464 Self::WrongNumberOfComponents(e) => CssStyleTransformParseError::WrongNumberOfComponents {
465 expected: e.expected,
466 got: e.got,
467 input: e.input.as_str(),
468 },
469 Self::NumberParseError(e) => CssStyleTransformParseError::NumberParseError(e.to_std()),
470 Self::PixelValueParseError(e) => {
471 CssStyleTransformParseError::PixelValueParseError(e.to_shared())
472 }
473 Self::AngleValueParseError(e) => {
474 CssStyleTransformParseError::AngleValueParseError(e.to_shared())
475 }
476 Self::PercentageValueParseError(e) => {
477 CssStyleTransformParseError::PercentageValueParseError(e.clone())
478 }
479 }
480 }
481}
482
483#[derive(Clone, PartialEq)]
484pub enum CssStyleTransformOriginParseError<'a> {
485 WrongNumberOfComponents {
486 expected: usize,
487 got: usize,
488 input: &'a str,
489 },
490 PixelValueParseError(CssPixelValueParseError<'a>),
491}
492
493impl_debug_as_display!(CssStyleTransformOriginParseError<'a>);
494impl_display! { CssStyleTransformOriginParseError<'a>, {
495 WrongNumberOfComponents { expected, got, input } => format!("Invalid number of components: expected {}, got {}: \"{}\"", expected, got, input),
496 PixelValueParseError(e) => format!("Invalid pixel value: {}", e),
497}}
498impl_from! { CssPixelValueParseError<'a>, CssStyleTransformOriginParseError::PixelValueParseError }
499
500#[derive(Debug, Clone, PartialEq)]
501#[repr(C, u8)]
502pub enum CssStyleTransformOriginParseErrorOwned {
503 WrongNumberOfComponents(WrongComponentCountError),
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(WrongComponentCountError {
515 expected: *expected,
516 got: *got,
517 input: input.to_string().into(),
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(e) => CssStyleTransformOriginParseError::WrongNumberOfComponents {
530 expected: e.expected,
531 got: e.got,
532 input: e.input.as_str(),
533 },
534 Self::PixelValueParseError(e) => {
535 CssStyleTransformOriginParseError::PixelValueParseError(e.to_shared())
536 }
537 }
538 }
539}
540
541#[derive(Clone, PartialEq)]
542pub enum CssStylePerspectiveOriginParseError<'a> {
543 WrongNumberOfComponents {
544 expected: usize,
545 got: usize,
546 input: &'a str,
547 },
548 PixelValueParseError(CssPixelValueParseError<'a>),
549}
550
551impl_debug_as_display!(CssStylePerspectiveOriginParseError<'a>);
552impl_display! { CssStylePerspectiveOriginParseError<'a>, {
553 WrongNumberOfComponents { expected, got, input } => format!("Invalid number of components: expected {}, got {}: \"{}\"", expected, got, input),
554 PixelValueParseError(e) => format!("Invalid pixel value: {}", e),
555}}
556impl_from! { CssPixelValueParseError<'a>, CssStylePerspectiveOriginParseError::PixelValueParseError }
557
558#[derive(Debug, Clone, PartialEq)]
559#[repr(C, u8)]
560pub enum CssStylePerspectiveOriginParseErrorOwned {
561 WrongNumberOfComponents(WrongComponentCountError),
562 PixelValueParseError(CssPixelValueParseErrorOwned),
563}
564
565impl<'a> CssStylePerspectiveOriginParseError<'a> {
566 pub fn to_contained(&self) -> CssStylePerspectiveOriginParseErrorOwned {
567 match self {
568 Self::WrongNumberOfComponents {
569 expected,
570 got,
571 input,
572 } => CssStylePerspectiveOriginParseErrorOwned::WrongNumberOfComponents(WrongComponentCountError {
573 expected: *expected,
574 got: *got,
575 input: input.to_string().into(),
576 }),
577 Self::PixelValueParseError(e) => {
578 CssStylePerspectiveOriginParseErrorOwned::PixelValueParseError(e.to_contained())
579 }
580 }
581 }
582}
583
584impl CssStylePerspectiveOriginParseErrorOwned {
585 pub fn to_shared<'a>(&'a self) -> CssStylePerspectiveOriginParseError<'a> {
586 match self {
587 Self::WrongNumberOfComponents(e) => CssStylePerspectiveOriginParseError::WrongNumberOfComponents {
588 expected: e.expected,
589 got: e.got,
590 input: e.input.as_str(),
591 },
592 Self::PixelValueParseError(e) => {
593 CssStylePerspectiveOriginParseError::PixelValueParseError(e.to_shared())
594 }
595 }
596 }
597}
598
599#[derive(Clone, PartialEq)]
600pub enum CssBackfaceVisibilityParseError<'a> {
601 InvalidValue(&'a str),
602}
603
604impl_debug_as_display!(CssBackfaceVisibilityParseError<'a>);
605impl_display! { CssBackfaceVisibilityParseError<'a>, {
606 InvalidValue(s) => format!("Invalid value for backface-visibility: \"{}\", expected \"visible\" or \"hidden\"", s),
607}}
608
609#[derive(Debug, Clone, PartialEq)]
610#[repr(C, u8)]
611pub enum CssBackfaceVisibilityParseErrorOwned {
612 InvalidValue(AzString),
613}
614
615impl<'a> CssBackfaceVisibilityParseError<'a> {
616 pub fn to_contained(&self) -> CssBackfaceVisibilityParseErrorOwned {
617 match self {
618 Self::InvalidValue(s) => {
619 CssBackfaceVisibilityParseErrorOwned::InvalidValue(s.to_string().into())
620 }
621 }
622 }
623}
624
625impl CssBackfaceVisibilityParseErrorOwned {
626 pub fn to_shared<'a>(&'a self) -> CssBackfaceVisibilityParseError<'a> {
627 match self {
628 Self::InvalidValue(s) => CssBackfaceVisibilityParseError::InvalidValue(s),
629 }
630 }
631}
632
633#[cfg(feature = "parser")]
636pub fn parse_style_transform_vec<'a>(
637 input: &'a str,
638) -> Result<StyleTransformVec, CssStyleTransformParseError<'a>> {
639 crate::props::basic::parse::split_string_respect_whitespace(input)
640 .iter()
641 .map(|i| parse_style_transform(i))
642 .collect::<Result<Vec<_>, _>>()
643 .map(Into::into)
644}
645
646#[cfg(feature = "parser")]
647pub fn parse_style_transform<'a>(
648 input: &'a str,
649) -> Result<StyleTransform, CssStyleTransformParseError<'a>> {
650 let (transform_type, transform_values) = parse_parentheses(
651 input,
652 &[
653 "matrix",
654 "matrix3d",
655 "translate",
656 "translate3d",
657 "translateX",
658 "translateY",
659 "translateZ",
660 "rotate",
661 "rotate3d",
662 "rotateX",
663 "rotateY",
664 "rotateZ",
665 "scale",
666 "scale3d",
667 "scaleX",
668 "scaleY",
669 "scaleZ",
670 "skew",
671 "skewX",
672 "skewY",
673 "perspective",
674 ],
675 )?;
676
677 fn get_numbers<'a>(
678 input: &'a str,
679 expected: usize,
680 ) -> Result<Vec<f32>, CssStyleTransformParseError<'a>> {
681 let numbers: Vec<_> = input
682 .split(',')
683 .map(|s| s.trim().parse::<f32>())
684 .collect::<Result<_, _>>()?;
685 if numbers.len() != expected {
686 Err(CssStyleTransformParseError::WrongNumberOfComponents {
687 expected,
688 got: numbers.len(),
689 input,
690 })
691 } else {
692 Ok(numbers)
693 }
694 }
695
696 match transform_type {
697 "matrix" => {
698 let nums = get_numbers(transform_values, 6)?;
699 Ok(StyleTransform::Matrix(StyleTransformMatrix2D {
700 a: FloatValue::new(nums[0]),
701 b: FloatValue::new(nums[1]),
702 c: FloatValue::new(nums[2]),
703 d: FloatValue::new(nums[3]),
704 tx: FloatValue::new(nums[4]),
705 ty: FloatValue::new(nums[5]),
706 }))
707 }
708 "matrix3d" => {
709 let nums = get_numbers(transform_values, 16)?;
710 Ok(StyleTransform::Matrix3D(StyleTransformMatrix3D {
711 m11: FloatValue::new(nums[0]),
712 m12: FloatValue::new(nums[1]),
713 m13: FloatValue::new(nums[2]),
714 m14: FloatValue::new(nums[3]),
715 m21: FloatValue::new(nums[4]),
716 m22: FloatValue::new(nums[5]),
717 m23: FloatValue::new(nums[6]),
718 m24: FloatValue::new(nums[7]),
719 m31: FloatValue::new(nums[8]),
720 m32: FloatValue::new(nums[9]),
721 m33: FloatValue::new(nums[10]),
722 m34: FloatValue::new(nums[11]),
723 m41: FloatValue::new(nums[12]),
724 m42: FloatValue::new(nums[13]),
725 m43: FloatValue::new(nums[14]),
726 m44: FloatValue::new(nums[15]),
727 }))
728 }
729 "translate" => {
730 let components: Vec<_> = transform_values.split(',').collect();
731
732 if components.len() > 2 {
734 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
735 expected: 2,
736 got: components.len(),
737 input: transform_values,
738 });
739 }
740
741 let x = parse_pixel_value(
742 components.first()
743 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
744 expected: 2,
745 got: 0,
746 input: transform_values,
747 })?
748 .trim(),
749 )?;
750 let y = match components.get(1) {
751 Some(c) => parse_pixel_value(c.trim())?,
752 None => PixelValue::px(0.0),
753 };
754 Ok(StyleTransform::Translate(StyleTransformTranslate2D {
755 x,
756 y,
757 }))
758 }
759 "translate3d" => {
760 let components: Vec<_> = transform_values.split(',').collect();
761 let x = parse_pixel_value(
762 components.first()
763 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
764 expected: 3,
765 got: 0,
766 input: transform_values,
767 })?
768 .trim(),
769 )?;
770 let y = parse_pixel_value(
771 components
772 .get(1)
773 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
774 expected: 3,
775 got: 1,
776 input: transform_values,
777 })?
778 .trim(),
779 )?;
780 let z = parse_pixel_value(
781 components
782 .get(2)
783 .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
784 expected: 3,
785 got: 2,
786 input: transform_values,
787 })?
788 .trim(),
789 )?;
790 Ok(StyleTransform::Translate3D(StyleTransformTranslate3D {
791 x,
792 y,
793 z,
794 }))
795 }
796 "translateX" => Ok(StyleTransform::TranslateX(parse_pixel_value(
797 transform_values,
798 )?)),
799 "translateY" => Ok(StyleTransform::TranslateY(parse_pixel_value(
800 transform_values,
801 )?)),
802 "translateZ" => Ok(StyleTransform::TranslateZ(parse_pixel_value(
803 transform_values,
804 )?)),
805 "rotate" => Ok(StyleTransform::Rotate(parse_angle_value(transform_values)?)),
806 "rotate3d" => {
807 let parts: Vec<_> = transform_values.splitn(4, ',').collect();
808 if parts.len() != 4 {
809 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
810 expected: 4,
811 got: parts.len(),
812 input: transform_values,
813 });
814 }
815 let x = parts[0].trim().parse::<f32>()?;
816 let y = parts[1].trim().parse::<f32>()?;
817 let z = parts[2].trim().parse::<f32>()?;
818 let angle = parse_angle_value(parts[3].trim())?;
819 Ok(StyleTransform::Rotate3D(StyleTransformRotate3D {
820 x: FloatValue::new(x),
821 y: FloatValue::new(y),
822 z: FloatValue::new(z),
823 angle,
824 }))
825 }
826 "rotateX" => Ok(StyleTransform::RotateX(parse_angle_value(
827 transform_values,
828 )?)),
829 "rotateY" => Ok(StyleTransform::RotateY(parse_angle_value(
830 transform_values,
831 )?)),
832 "rotateZ" => Ok(StyleTransform::RotateZ(parse_angle_value(
833 transform_values,
834 )?)),
835 "scale" => {
836 let parts: Vec<_> = transform_values.split(',').collect();
837 if parts.is_empty() || parts.len() > 2 {
838 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
839 expected: 2,
840 got: parts.len(),
841 input: transform_values,
842 });
843 }
844 let x = parts[0].trim().parse::<f32>()?;
845 let y = if parts.len() == 2 {
846 parts[1].trim().parse::<f32>()?
847 } else {
848 x
849 };
850 Ok(StyleTransform::Scale(StyleTransformScale2D {
851 x: FloatValue::new(x),
852 y: FloatValue::new(y),
853 }))
854 }
855 "scale3d" => {
856 let nums = get_numbers(transform_values, 3)?;
857 Ok(StyleTransform::Scale3D(StyleTransformScale3D {
858 x: FloatValue::new(nums[0]),
859 y: FloatValue::new(nums[1]),
860 z: FloatValue::new(nums[2]),
861 }))
862 }
863 "scaleX" => Ok(StyleTransform::ScaleX(PercentageValue::new(
864 transform_values.trim().parse::<f32>()? * 100.0,
865 ))),
866 "scaleY" => Ok(StyleTransform::ScaleY(PercentageValue::new(
867 transform_values.trim().parse::<f32>()? * 100.0,
868 ))),
869 "scaleZ" => Ok(StyleTransform::ScaleZ(PercentageValue::new(
870 transform_values.trim().parse::<f32>()? * 100.0,
871 ))),
872 "skew" => {
873 let components: Vec<_> = transform_values.split(',').collect();
874 if components.is_empty() || components.len() > 2 {
875 return Err(CssStyleTransformParseError::WrongNumberOfComponents {
876 expected: 2,
877 got: components.len(),
878 input: transform_values,
879 });
880 }
881 let x = parse_angle_value(components[0].trim())?;
882 let y = match components.get(1) {
883 Some(c) => parse_angle_value(c.trim())?,
884 None => AngleValue::deg(0.0),
885 };
886 Ok(StyleTransform::Skew(StyleTransformSkew2D { x, y }))
887 }
888 "skewX" => Ok(StyleTransform::SkewX(parse_angle_value(transform_values)?)),
889 "skewY" => Ok(StyleTransform::SkewY(parse_angle_value(transform_values)?)),
890 "perspective" => Ok(StyleTransform::Perspective(parse_pixel_value(
891 transform_values,
892 )?)),
893 _ => unreachable!(),
894 }
895}
896
897#[cfg(feature = "parser")]
898pub fn parse_style_transform_origin<'a>(
899 input: &'a str,
900) -> Result<StyleTransformOrigin, CssStyleTransformOriginParseError<'a>> {
901 let components: Vec<_> = input.split_whitespace().collect();
902 if components.len() != 2 {
903 return Err(CssStyleTransformOriginParseError::WrongNumberOfComponents {
904 expected: 2,
905 got: components.len(),
906 input,
907 });
908 }
909
910 fn parse_position_component(
912 s: &str,
913 is_horizontal: bool,
914 ) -> Result<PixelValue, CssPixelValueParseError<'_>> {
915 match s.trim() {
916 "left" if is_horizontal => Ok(PixelValue::percent(0.0)),
917 "center" => Ok(PixelValue::percent(50.0)),
918 "right" if is_horizontal => Ok(PixelValue::percent(100.0)),
919 "top" if !is_horizontal => Ok(PixelValue::percent(0.0)),
920 "bottom" if !is_horizontal => Ok(PixelValue::percent(100.0)),
921 _ => parse_pixel_value(s),
922 }
923 }
924
925 let x = parse_position_component(components[0], true)?;
926 let y = parse_position_component(components[1], false)?;
927 Ok(StyleTransformOrigin { x, y })
928}
929
930#[cfg(feature = "parser")]
931pub fn parse_style_perspective_origin<'a>(
932 input: &'a str,
933) -> Result<StylePerspectiveOrigin, CssStylePerspectiveOriginParseError<'a>> {
934 let components: Vec<_> = input.split_whitespace().collect();
935 if components.len() != 2 {
936 return Err(
937 CssStylePerspectiveOriginParseError::WrongNumberOfComponents {
938 expected: 2,
939 got: components.len(),
940 input,
941 },
942 );
943 }
944 let x = parse_pixel_value(components[0])?;
945 let y = parse_pixel_value(components[1])?;
946 Ok(StylePerspectiveOrigin { x, y })
947}
948
949#[cfg(feature = "parser")]
950pub fn parse_style_backface_visibility<'a>(
951 input: &'a str,
952) -> Result<StyleBackfaceVisibility, CssBackfaceVisibilityParseError<'a>> {
953 match input.trim() {
954 "visible" => Ok(StyleBackfaceVisibility::Visible),
955 "hidden" => Ok(StyleBackfaceVisibility::Hidden),
956 _ => Err(CssBackfaceVisibilityParseError::InvalidValue(input)),
957 }
958}
959
960#[cfg(all(test, feature = "parser"))]
961mod tests {
962 use super::*;
963
964 #[test]
965 fn test_parse_transform_vec() {
966 let result =
967 parse_style_transform_vec("translateX(10px) rotate(90deg) scale(0.5, 0.5)").unwrap();
968 assert_eq!(result.len(), 3);
969 assert!(matches!(
970 result.as_slice()[0],
971 StyleTransform::TranslateX(_)
972 ));
973 assert!(matches!(result.as_slice()[1], StyleTransform::Rotate(_)));
974 assert!(matches!(result.as_slice()[2], StyleTransform::Scale(_)));
975 }
976
977 #[test]
978 fn test_parse_transform_functions() {
979 assert_eq!(
981 parse_style_transform("translateX(50%)").unwrap(),
982 StyleTransform::TranslateX(PixelValue::percent(50.0))
983 );
984 let translate = parse_style_transform("translate(10px, -20px)").unwrap();
985 if let StyleTransform::Translate(t) = translate {
986 assert_eq!(t.x, PixelValue::px(10.0));
987 assert_eq!(t.y, PixelValue::px(-20.0));
988 } else {
989 panic!("Expected Translate");
990 }
991
992 assert_eq!(
994 parse_style_transform("scaleY(1.2)").unwrap(),
995 StyleTransform::ScaleY(PercentageValue::new(120.0))
996 );
997 let scale = parse_style_transform("scale(2, 0.5)").unwrap();
998 if let StyleTransform::Scale(s) = scale {
999 assert_eq!(s.x.get(), 2.0);
1000 assert_eq!(s.y.get(), 0.5);
1001 } else {
1002 panic!("Expected Scale");
1003 }
1004
1005 assert_eq!(
1007 parse_style_transform("rotate(0.25turn)").unwrap(),
1008 StyleTransform::Rotate(AngleValue::turn(0.25))
1009 );
1010
1011 assert_eq!(
1013 parse_style_transform("skewX(-10deg)").unwrap(),
1014 StyleTransform::SkewX(AngleValue::deg(-10.0))
1015 );
1016 let skew = parse_style_transform("skew(20deg, 30deg)").unwrap();
1017 if let StyleTransform::Skew(s) = skew {
1018 assert_eq!(s.x, AngleValue::deg(20.0));
1019 assert_eq!(s.y, AngleValue::deg(30.0));
1020 } else {
1021 panic!("Expected Skew");
1022 }
1023 }
1024
1025 #[test]
1026 fn test_parse_transform_origin() {
1027 let result = parse_style_transform_origin("50% 50%").unwrap();
1028 assert_eq!(result.x, PixelValue::percent(50.0));
1029 assert_eq!(result.y, PixelValue::percent(50.0));
1030
1031 let result = parse_style_transform_origin("left top").unwrap();
1032 assert_eq!(result.x, PixelValue::percent(0.0));
1033 assert_eq!(result.y, PixelValue::percent(0.0));
1034
1035 let result = parse_style_transform_origin("20px bottom").unwrap();
1036 assert_eq!(result.x, PixelValue::px(20.0));
1037 assert_eq!(result.y, PixelValue::percent(100.0));
1038 }
1039
1040 #[test]
1041 fn test_parse_backface_visibility() {
1042 assert_eq!(
1043 parse_style_backface_visibility("visible").unwrap(),
1044 StyleBackfaceVisibility::Visible
1045 );
1046 assert_eq!(
1047 parse_style_backface_visibility("hidden").unwrap(),
1048 StyleBackfaceVisibility::Hidden
1049 );
1050 assert!(parse_style_backface_visibility("none").is_err());
1051 }
1052
1053 #[test]
1054 fn test_parse_transform_errors() {
1055 assert!(parse_style_transform("translatex(10px)").is_err());
1057 assert!(parse_style_transform("translate(1, 2, 3)").is_err());
1059 let scale1 = parse_style_transform("scale(2)").unwrap();
1061 if let StyleTransform::Scale(s) = scale1 {
1062 assert_eq!(s.x.get(), 2.0);
1063 assert_eq!(s.y.get(), 2.0);
1064 } else {
1065 panic!("Expected Scale");
1066 }
1067 let translate1 = parse_style_transform("translate(10px)").unwrap();
1068 if let StyleTransform::Translate(t) = translate1 {
1069 assert_eq!(t.x, PixelValue::px(10.0));
1070 assert_eq!(t.y, PixelValue::px(0.0));
1071 } else {
1072 panic!("Expected Translate");
1073 }
1074 let skew1 = parse_style_transform("skew(20deg)").unwrap();
1075 if let StyleTransform::Skew(s) = skew1 {
1076 assert_eq!(s.x, AngleValue::deg(20.0));
1077 assert_eq!(s.y, AngleValue::deg(0.0));
1078 } else {
1079 panic!("Expected Skew");
1080 }
1081 let rot3d = parse_style_transform("rotate3d(1, 0, 0, 45deg)").unwrap();
1083 if let StyleTransform::Rotate3D(r) = rot3d {
1084 assert_eq!(r.x.get(), 1.0);
1085 assert_eq!(r.angle, AngleValue::deg(45.0));
1086 } else {
1087 panic!("Expected Rotate3D");
1088 }
1089 assert!(parse_style_transform("rotate(10px)").is_err());
1091 assert!(parse_style_transform("translateX(auto)").is_err());
1092 }
1093}