1
//! CSS properties for 2D and 3D transformations.
2

            
3
use alloc::{
4
    string::{String, ToString},
5
    vec::Vec,
6
};
7
use core::fmt;
8
use std::num::ParseFloatError;
9
use crate::corety::AzString;
10

            
11
#[cfg(feature = "parser")]
12
use crate::props::basic::{
13
    error::WrongComponentCountError,
14
    length::parse_float_value,
15
    parse::{parse_parentheses, ParenthesisParseError, ParenthesisParseErrorOwned},
16
};
17
use 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
// -- Data Structures --
37

            
38
/// Represents a `perspective-origin` attribute
39
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40
#[repr(C)]
41
pub struct StylePerspectiveOrigin {
42
    pub x: PixelValue,
43
    pub y: PixelValue,
44
}
45

            
46
impl 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

            
55
impl PrintAsCssValue for StylePerspectiveOrigin {
56
    fn print_as_css_value(&self) -> String {
57
        format!("{} {}", self.x, self.y)
58
    }
59
}
60

            
61
/// Represents a `transform-origin` attribute
62
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
63
#[repr(C)]
64
pub struct StyleTransformOrigin {
65
    pub x: PixelValue,
66
    pub y: PixelValue,
67
}
68

            
69
impl Default for StyleTransformOrigin {
70
2730
    fn default() -> Self {
71
2730
        Self {
72
2730
            x: PixelValue::const_percent(50),
73
2730
            y: PixelValue::const_percent(50),
74
2730
        }
75
2730
    }
76
}
77

            
78
impl 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

            
87
impl PrintAsCssValue for StyleTransformOrigin {
88
    fn print_as_css_value(&self) -> String {
89
        format!("{} {}", self.x, self.y)
90
    }
91
}
92

            
93
// Formatting to Rust code
94
impl 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

            
104
// Formatting to Rust code for StyleTransformOrigin
105
impl 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
/// Represents a `backface-visibility` attribute
116
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
117
#[repr(C)]
118
pub enum StyleBackfaceVisibility {
119
    #[default]
120
    Visible,
121
    Hidden,
122
}
123

            
124
impl 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
/// Represents one component of a `transform` attribute
134
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
135
#[repr(C, u8)]
136
pub 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

            
160
impl_option!(
161
    StyleTransform,
162
    OptionStyleTransform,
163
    [Debug, Copy, Clone, PartialEq, PartialOrd]
164
);
165

            
166
impl_vec!(StyleTransform, StyleTransformVec, StyleTransformVecDestructor, StyleTransformVecDestructorType, StyleTransformVecSlice, OptionStyleTransform);
167
impl_vec_debug!(StyleTransform, StyleTransformVec);
168
impl_vec_partialord!(StyleTransform, StyleTransformVec);
169
impl_vec_ord!(StyleTransform, StyleTransformVec);
170
impl_vec_clone!(
171
    StyleTransform,
172
    StyleTransformVec,
173
    StyleTransformVecDestructor
174
);
175
impl_vec_partialeq!(StyleTransform, StyleTransformVec);
176
impl_vec_eq!(StyleTransform, StyleTransformVec);
177
impl_vec_hash!(StyleTransform, StyleTransformVec);
178

            
179
impl 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

            
189
// Formatting to Rust code for StyleTransformVec
190
impl 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

            
199
impl 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
/// Represents a CSS `matrix(a, b, c, d, tx, ty)` 2D transform function.
251
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
252
#[repr(C)]
253
pub 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

            
262
impl 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
/// Represents a CSS `matrix3d(...)` 3D transform function (4x4 matrix).
276
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
277
#[repr(C)]
278
pub 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

            
297
impl 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
/// Represents a CSS `translate(x, y)` 2D translation.
321
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
322
#[repr(C)]
323
pub struct StyleTransformTranslate2D {
324
    pub x: PixelValue,
325
    pub y: PixelValue,
326
}
327

            
328
/// Represents a CSS `translate3d(x, y, z)` 3D translation.
329
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
330
#[repr(C)]
331
pub struct StyleTransformTranslate3D {
332
    pub x: PixelValue,
333
    pub y: PixelValue,
334
    pub z: PixelValue,
335
}
336

            
337
/// Represents a CSS `rotate3d(x, y, z, angle)` 3D rotation.
338
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
339
#[repr(C)]
340
pub struct StyleTransformRotate3D {
341
    pub x: FloatValue,
342
    pub y: FloatValue,
343
    pub z: FloatValue,
344
    pub angle: AngleValue,
345
}
346

            
347
/// Represents a CSS `scale(x, y)` 2D scaling.
348
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
349
#[repr(C)]
350
pub struct StyleTransformScale2D {
351
    pub x: FloatValue,
352
    pub y: FloatValue,
353
}
354

            
355
/// Represents a CSS `scale3d(x, y, z)` 3D scaling.
356
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
357
#[repr(C)]
358
pub struct StyleTransformScale3D {
359
    pub x: FloatValue,
360
    pub y: FloatValue,
361
    pub z: FloatValue,
362
}
363

            
364
/// Represents a CSS `skew(x, y)` 2D skew transformation.
365
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
366
#[repr(C)]
367
pub struct StyleTransformSkew2D {
368
    pub x: AngleValue,
369
    pub y: AngleValue,
370
}
371

            
372
// -- Errors --
373

            
374
#[derive(Clone, PartialEq)]
375
pub 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

            
389
impl_debug_as_display!(CssStyleTransformParseError<'a>);
390
impl_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

            
400
impl_from! { ParenthesisParseError<'a>, CssStyleTransformParseError::InvalidParenthesis }
401
impl_from! { CssPixelValueParseError<'a>, CssStyleTransformParseError::PixelValueParseError }
402
impl_from! { CssAngleValueParseError<'a>, CssStyleTransformParseError::AngleValueParseError }
403
impl_from! { ParseFloatError, CssStyleTransformParseError<'a>::NumberParseError }
404

            
405
impl<'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)]
413
pub 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

            
423
impl<'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

            
457
impl 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)]
484
pub enum CssStyleTransformOriginParseError<'a> {
485
    WrongNumberOfComponents {
486
        expected: usize,
487
        got: usize,
488
        input: &'a str,
489
    },
490
    PixelValueParseError(CssPixelValueParseError<'a>),
491
}
492

            
493
impl_debug_as_display!(CssStyleTransformOriginParseError<'a>);
494
impl_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
}}
498
impl_from! { CssPixelValueParseError<'a>, CssStyleTransformOriginParseError::PixelValueParseError }
499

            
500
#[derive(Debug, Clone, PartialEq)]
501
#[repr(C, u8)]
502
pub enum CssStyleTransformOriginParseErrorOwned {
503
    WrongNumberOfComponents(WrongComponentCountError),
504
    PixelValueParseError(CssPixelValueParseErrorOwned),
505
}
506

            
507
impl<'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

            
526
impl 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)]
542
pub enum CssStylePerspectiveOriginParseError<'a> {
543
    WrongNumberOfComponents {
544
        expected: usize,
545
        got: usize,
546
        input: &'a str,
547
    },
548
    PixelValueParseError(CssPixelValueParseError<'a>),
549
}
550

            
551
impl_debug_as_display!(CssStylePerspectiveOriginParseError<'a>);
552
impl_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
}}
556
impl_from! { CssPixelValueParseError<'a>, CssStylePerspectiveOriginParseError::PixelValueParseError }
557

            
558
#[derive(Debug, Clone, PartialEq)]
559
#[repr(C, u8)]
560
pub enum CssStylePerspectiveOriginParseErrorOwned {
561
    WrongNumberOfComponents(WrongComponentCountError),
562
    PixelValueParseError(CssPixelValueParseErrorOwned),
563
}
564

            
565
impl<'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

            
584
impl 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)]
600
pub enum CssBackfaceVisibilityParseError<'a> {
601
    InvalidValue(&'a str),
602
}
603

            
604
impl_debug_as_display!(CssBackfaceVisibilityParseError<'a>);
605
impl_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)]
611
pub enum CssBackfaceVisibilityParseErrorOwned {
612
    InvalidValue(AzString),
613
}
614

            
615
impl<'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

            
625
impl 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
// -- Parsers --
634

            
635
#[cfg(feature = "parser")]
636
134
pub fn parse_style_transform_vec<'a>(
637
134
    input: &'a str,
638
134
) -> Result<StyleTransformVec, CssStyleTransformParseError<'a>> {
639
134
    crate::props::basic::parse::split_string_respect_whitespace(input)
640
134
        .iter()
641
150
        .map(|i| parse_style_transform(i))
642
134
        .collect::<Result<Vec<_>, _>>()
643
134
        .map(Into::into)
644
134
}
645

            
646
#[cfg(feature = "parser")]
647
165
pub fn parse_style_transform<'a>(
648
165
    input: &'a str,
649
165
) -> Result<StyleTransform, CssStyleTransformParseError<'a>> {
650
165
    let (transform_type, transform_values) = parse_parentheses(
651
165
        input,
652
165
        &[
653
165
            "matrix",
654
165
            "matrix3d",
655
165
            "translate",
656
165
            "translate3d",
657
165
            "translateX",
658
165
            "translateY",
659
165
            "translateZ",
660
165
            "rotate",
661
165
            "rotate3d",
662
165
            "rotateX",
663
165
            "rotateY",
664
165
            "rotateZ",
665
165
            "scale",
666
165
            "scale3d",
667
165
            "scaleX",
668
165
            "scaleY",
669
165
            "scaleZ",
670
165
            "skew",
671
165
            "skewX",
672
165
            "skewY",
673
165
            "perspective",
674
165
        ],
675
1
    )?;
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
164
    match transform_type {
697
164
        "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
164
        "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
164
        "translate" => {
730
10
            let components: Vec<_> = transform_values.split(',').collect();
731

            
732
            // translate() takes exactly 1 or 2 parameters (x, or x and y)
733
10
            if components.len() > 2 {
734
1
                return Err(CssStyleTransformParseError::WrongNumberOfComponents {
735
1
                    expected: 2,
736
1
                    got: components.len(),
737
1
                    input: transform_values,
738
1
                });
739
9
            }
740

            
741
9
            let x = parse_pixel_value(
742
9
                components.first()
743
9
                    .ok_or(CssStyleTransformParseError::WrongNumberOfComponents {
744
9
                        expected: 2,
745
9
                        got: 0,
746
9
                        input: transform_values,
747
9
                    })?
748
9
                    .trim(),
749
            )?;
750
9
            let y = match components.get(1) {
751
8
                Some(c) => parse_pixel_value(c.trim())?,
752
1
                None => PixelValue::px(0.0),
753
            };
754
9
            Ok(StyleTransform::Translate(StyleTransformTranslate2D {
755
9
                x,
756
9
                y,
757
9
            }))
758
        }
759
154
        "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
154
        "translateX" => Ok(StyleTransform::TranslateX(parse_pixel_value(
797
129
            transform_values,
798
1
        )?)),
799
25
        "translateY" => Ok(StyleTransform::TranslateY(parse_pixel_value(
800
            transform_values,
801
        )?)),
802
25
        "translateZ" => Ok(StyleTransform::TranslateZ(parse_pixel_value(
803
            transform_values,
804
        )?)),
805
25
        "rotate" => Ok(StyleTransform::Rotate(parse_angle_value(transform_values)?)),
806
15
        "rotate3d" => {
807
1
            let parts: Vec<_> = transform_values.splitn(4, ',').collect();
808
1
            if parts.len() != 4 {
809
                return Err(CssStyleTransformParseError::WrongNumberOfComponents {
810
                    expected: 4,
811
                    got: parts.len(),
812
                    input: transform_values,
813
                });
814
1
            }
815
1
            let x = parts[0].trim().parse::<f32>()?;
816
1
            let y = parts[1].trim().parse::<f32>()?;
817
1
            let z = parts[2].trim().parse::<f32>()?;
818
1
            let angle = parse_angle_value(parts[3].trim())?;
819
1
            Ok(StyleTransform::Rotate3D(StyleTransformRotate3D {
820
1
                x: FloatValue::new(x),
821
1
                y: FloatValue::new(y),
822
1
                z: FloatValue::new(z),
823
1
                angle,
824
1
            }))
825
        }
826
14
        "rotateX" => Ok(StyleTransform::RotateX(parse_angle_value(
827
            transform_values,
828
        )?)),
829
14
        "rotateY" => Ok(StyleTransform::RotateY(parse_angle_value(
830
            transform_values,
831
        )?)),
832
14
        "rotateZ" => Ok(StyleTransform::RotateZ(parse_angle_value(
833
            transform_values,
834
        )?)),
835
14
        "scale" => {
836
10
            let parts: Vec<_> = transform_values.split(',').collect();
837
10
            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
10
            }
844
10
            let x = parts[0].trim().parse::<f32>()?;
845
10
            let y = if parts.len() == 2 {
846
2
                parts[1].trim().parse::<f32>()?
847
            } else {
848
8
                x
849
            };
850
10
            Ok(StyleTransform::Scale(StyleTransformScale2D {
851
10
                x: FloatValue::new(x),
852
10
                y: FloatValue::new(y),
853
10
            }))
854
        }
855
4
        "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
4
        "scaleX" => Ok(StyleTransform::ScaleX(PercentageValue::new(
864
            transform_values.trim().parse::<f32>()? * 100.0,
865
        ))),
866
4
        "scaleY" => Ok(StyleTransform::ScaleY(PercentageValue::new(
867
1
            transform_values.trim().parse::<f32>()? * 100.0,
868
        ))),
869
3
        "scaleZ" => Ok(StyleTransform::ScaleZ(PercentageValue::new(
870
            transform_values.trim().parse::<f32>()? * 100.0,
871
        ))),
872
3
        "skew" => {
873
2
            let components: Vec<_> = transform_values.split(',').collect();
874
2
            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
2
            }
881
2
            let x = parse_angle_value(components[0].trim())?;
882
2
            let y = match components.get(1) {
883
1
                Some(c) => parse_angle_value(c.trim())?,
884
1
                None => AngleValue::deg(0.0),
885
            };
886
2
            Ok(StyleTransform::Skew(StyleTransformSkew2D { x, y }))
887
        }
888
1
        "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
165
}
896

            
897
#[cfg(feature = "parser")]
898
3
pub fn parse_style_transform_origin<'a>(
899
3
    input: &'a str,
900
3
) -> Result<StyleTransformOrigin, CssStyleTransformOriginParseError<'a>> {
901
3
    let components: Vec<_> = input.split_whitespace().collect();
902
3
    if components.len() != 2 {
903
        return Err(CssStyleTransformOriginParseError::WrongNumberOfComponents {
904
            expected: 2,
905
            got: components.len(),
906
            input,
907
        });
908
3
    }
909

            
910
    // Helper to parse position keywords or pixel values
911
6
    fn parse_position_component(
912
6
        s: &str,
913
6
        is_horizontal: bool,
914
6
    ) -> Result<PixelValue, CssPixelValueParseError<'_>> {
915
6
        match s.trim() {
916
6
            "left" if is_horizontal => Ok(PixelValue::percent(0.0)),
917
5
            "center" => Ok(PixelValue::percent(50.0)),
918
5
            "right" if is_horizontal => Ok(PixelValue::percent(100.0)),
919
5
            "top" if !is_horizontal => Ok(PixelValue::percent(0.0)),
920
4
            "bottom" if !is_horizontal => Ok(PixelValue::percent(100.0)),
921
3
            _ => parse_pixel_value(s),
922
        }
923
6
    }
924

            
925
3
    let x = parse_position_component(components[0], true)?;
926
3
    let y = parse_position_component(components[1], false)?;
927
3
    Ok(StyleTransformOrigin { x, y })
928
3
}
929

            
930
#[cfg(feature = "parser")]
931
pub 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")]
950
3
pub fn parse_style_backface_visibility<'a>(
951
3
    input: &'a str,
952
3
) -> Result<StyleBackfaceVisibility, CssBackfaceVisibilityParseError<'a>> {
953
3
    match input.trim() {
954
3
        "visible" => Ok(StyleBackfaceVisibility::Visible),
955
2
        "hidden" => Ok(StyleBackfaceVisibility::Hidden),
956
1
        _ => Err(CssBackfaceVisibilityParseError::InvalidValue(input)),
957
    }
958
3
}
959

            
960
#[cfg(all(test, feature = "parser"))]
961
mod tests {
962
    use super::*;
963

            
964
    #[test]
965
1
    fn test_parse_transform_vec() {
966
1
        let result =
967
1
            parse_style_transform_vec("translateX(10px) rotate(90deg) scale(0.5, 0.5)").unwrap();
968
1
        assert_eq!(result.len(), 3);
969
1
        assert!(matches!(
970
1
            result.as_slice()[0],
971
            StyleTransform::TranslateX(_)
972
        ));
973
1
        assert!(matches!(result.as_slice()[1], StyleTransform::Rotate(_)));
974
1
        assert!(matches!(result.as_slice()[2], StyleTransform::Scale(_)));
975
1
    }
976

            
977
    #[test]
978
1
    fn test_parse_transform_functions() {
979
        // Translate
980
1
        assert_eq!(
981
1
            parse_style_transform("translateX(50%)").unwrap(),
982
1
            StyleTransform::TranslateX(PixelValue::percent(50.0))
983
        );
984
1
        let translate = parse_style_transform("translate(10px, -20px)").unwrap();
985
1
        if let StyleTransform::Translate(t) = translate {
986
1
            assert_eq!(t.x, PixelValue::px(10.0));
987
1
            assert_eq!(t.y, PixelValue::px(-20.0));
988
        } else {
989
            panic!("Expected Translate");
990
        }
991

            
992
        // Scale
993
1
        assert_eq!(
994
1
            parse_style_transform("scaleY(1.2)").unwrap(),
995
1
            StyleTransform::ScaleY(PercentageValue::new(120.0))
996
        );
997
1
        let scale = parse_style_transform("scale(2, 0.5)").unwrap();
998
1
        if let StyleTransform::Scale(s) = scale {
999
1
            assert_eq!(s.x.get(), 2.0);
1
            assert_eq!(s.y.get(), 0.5);
        } else {
            panic!("Expected Scale");
        }
        // Rotate
1
        assert_eq!(
1
            parse_style_transform("rotate(0.25turn)").unwrap(),
1
            StyleTransform::Rotate(AngleValue::turn(0.25))
        );
        // Skew
1
        assert_eq!(
1
            parse_style_transform("skewX(-10deg)").unwrap(),
1
            StyleTransform::SkewX(AngleValue::deg(-10.0))
        );
1
        let skew = parse_style_transform("skew(20deg, 30deg)").unwrap();
1
        if let StyleTransform::Skew(s) = skew {
1
            assert_eq!(s.x, AngleValue::deg(20.0));
1
            assert_eq!(s.y, AngleValue::deg(30.0));
        } else {
            panic!("Expected Skew");
        }
1
    }
    #[test]
1
    fn test_parse_transform_origin() {
1
        let result = parse_style_transform_origin("50% 50%").unwrap();
1
        assert_eq!(result.x, PixelValue::percent(50.0));
1
        assert_eq!(result.y, PixelValue::percent(50.0));
1
        let result = parse_style_transform_origin("left top").unwrap();
1
        assert_eq!(result.x, PixelValue::percent(0.0));
1
        assert_eq!(result.y, PixelValue::percent(0.0));
1
        let result = parse_style_transform_origin("20px bottom").unwrap();
1
        assert_eq!(result.x, PixelValue::px(20.0));
1
        assert_eq!(result.y, PixelValue::percent(100.0));
1
    }
    #[test]
1
    fn test_parse_backface_visibility() {
1
        assert_eq!(
1
            parse_style_backface_visibility("visible").unwrap(),
            StyleBackfaceVisibility::Visible
        );
1
        assert_eq!(
1
            parse_style_backface_visibility("hidden").unwrap(),
            StyleBackfaceVisibility::Hidden
        );
1
        assert!(parse_style_backface_visibility("none").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_errors() {
        // Wrong function name
1
        assert!(parse_style_transform("translatex(10px)").is_err());
        // Wrong number of args
1
        assert!(parse_style_transform("translate(1, 2, 3)").is_err());
        // Single-arg forms (CSS spec compliant)
1
        let scale1 = parse_style_transform("scale(2)").unwrap();
1
        if let StyleTransform::Scale(s) = scale1 {
1
            assert_eq!(s.x.get(), 2.0);
1
            assert_eq!(s.y.get(), 2.0);
        } else {
            panic!("Expected Scale");
        }
1
        let translate1 = parse_style_transform("translate(10px)").unwrap();
1
        if let StyleTransform::Translate(t) = translate1 {
1
            assert_eq!(t.x, PixelValue::px(10.0));
1
            assert_eq!(t.y, PixelValue::px(0.0));
        } else {
            panic!("Expected Translate");
        }
1
        let skew1 = parse_style_transform("skew(20deg)").unwrap();
1
        if let StyleTransform::Skew(s) = skew1 {
1
            assert_eq!(s.x, AngleValue::deg(20.0));
1
            assert_eq!(s.y, AngleValue::deg(0.0));
        } else {
            panic!("Expected Skew");
        }
        // rotate3d with angle unit
1
        let rot3d = parse_style_transform("rotate3d(1, 0, 0, 45deg)").unwrap();
1
        if let StyleTransform::Rotate3D(r) = rot3d {
1
            assert_eq!(r.x.get(), 1.0);
1
            assert_eq!(r.angle, AngleValue::deg(45.0));
        } else {
            panic!("Expected Rotate3D");
        }
        // Invalid value
1
        assert!(parse_style_transform("rotate(10px)").is_err());
1
        assert!(parse_style_transform("translateX(auto)").is_err());
1
    }
}