1use alloc::{
4 string::{String, ToString},
5 vec::Vec,
6};
7use core::{fmt, num::ParseFloatError};
8
9#[cfg(feature = "parser")]
10use crate::props::basic::{
11 error::{InvalidValueErr, InvalidValueErrOwned},
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::{AngleValue, parse_angle_value, CssAngleValueParseError, CssAngleValueParseErrorOwned},
20 color::{parse_css_color, ColorU, CssColorParseError, CssColorParseErrorOwned},
21 length::{FloatValue, PercentageParseError, PercentageValue},
22 pixel::{
23 parse_pixel_value, CssPixelValueParseError, CssPixelValueParseErrorOwned,
24 PixelValue,
25 },
26 },
27 formatter::PrintAsCssValue,
28 style::{
29 box_shadow::{
30 parse_style_box_shadow, CssShadowParseError, CssShadowParseErrorOwned,
31 StyleBoxShadow,
32 },
33 effects::{parse_style_mix_blend_mode, MixBlendModeParseError, StyleMixBlendMode},
34 },
35 },
36};
37
38#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[repr(C, u8)]
42pub enum StyleFilter {
43 Blend(StyleMixBlendMode),
44 Flood(ColorU),
45 Blur(StyleBlur),
46 Opacity(PercentageValue),
47 ColorMatrix(StyleColorMatrix),
48 DropShadow(StyleBoxShadow),
49 ComponentTransfer,
50 Offset(StyleFilterOffset),
51 Composite(StyleCompositeFilter),
52 Brightness(PercentageValue),
54 Contrast(PercentageValue),
55 Grayscale(PercentageValue),
56 HueRotate(AngleValue),
57 Invert(PercentageValue),
58 Saturate(PercentageValue),
59 Sepia(PercentageValue),
60}
61
62impl_option!(
63 StyleFilter,
64 OptionStyleFilter,
65 copy = false,
66 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
67);
68
69impl_vec!(StyleFilter, StyleFilterVec, StyleFilterVecDestructor, StyleFilterVecDestructorType, StyleFilterVecSlice, OptionStyleFilter);
70impl_vec_clone!(StyleFilter, StyleFilterVec, StyleFilterVecDestructor);
71impl_vec_debug!(StyleFilter, StyleFilterVec);
72impl_vec_eq!(StyleFilter, StyleFilterVec);
73impl_vec_ord!(StyleFilter, StyleFilterVec);
74impl_vec_hash!(StyleFilter, StyleFilterVec);
75impl_vec_partialeq!(StyleFilter, StyleFilterVec);
76impl_vec_partialord!(StyleFilter, StyleFilterVec);
77
78#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
79#[repr(C)]
80pub struct StyleBlur {
81 pub width: PixelValue,
82 pub height: PixelValue,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
88#[repr(C)]
89pub struct StyleColorMatrix {
90 pub m0: FloatValue,
91 pub m1: FloatValue,
92 pub m2: FloatValue,
93 pub m3: FloatValue,
94 pub m4: FloatValue,
95 pub m5: FloatValue,
96 pub m6: FloatValue,
97 pub m7: FloatValue,
98 pub m8: FloatValue,
99 pub m9: FloatValue,
100 pub m10: FloatValue,
101 pub m11: FloatValue,
102 pub m12: FloatValue,
103 pub m13: FloatValue,
104 pub m14: FloatValue,
105 pub m15: FloatValue,
106 pub m16: FloatValue,
107 pub m17: FloatValue,
108 pub m18: FloatValue,
109 pub m19: FloatValue,
110}
111
112impl StyleColorMatrix {
113 pub fn as_slice(&self) -> [FloatValue; 20] {
115 [
116 self.m0, self.m1, self.m2, self.m3, self.m4, self.m5, self.m6, self.m7, self.m8,
117 self.m9, self.m10, self.m11, self.m12, self.m13, self.m14, self.m15, self.m16,
118 self.m17, self.m18, self.m19,
119 ]
120 }
121}
122
123#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
124#[repr(C)]
125pub struct StyleFilterOffset {
126 pub x: PixelValue,
127 pub y: PixelValue,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
133#[repr(C)]
134pub struct ArithmeticCoefficients {
135 pub k1: FloatValue,
136 pub k2: FloatValue,
137 pub k3: FloatValue,
138 pub k4: FloatValue,
139}
140
141#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
142#[repr(C, u8)]
143pub enum StyleCompositeFilter {
144 Over,
145 In,
146 Atop,
147 Out,
148 Xor,
149 Lighter,
150 Arithmetic(ArithmeticCoefficients),
151}
152
153impl PrintAsCssValue for StyleFilterVec {
156 fn print_as_css_value(&self) -> String {
157 self.as_ref()
158 .iter()
159 .map(|f| f.print_as_css_value())
160 .collect::<Vec<_>>()
161 .join(" ")
162 }
163}
164
165impl crate::format_rust_code::FormatAsRustCode for StyleFilterVec {
167 fn format_as_rust_code(&self, _tabs: usize) -> String {
168 format!(
169 "StyleFilterVec::from_const_slice(STYLE_FILTER_{}_ITEMS)",
170 self.get_hash()
171 )
172 }
173}
174
175impl PrintAsCssValue for StyleFilter {
176 fn print_as_css_value(&self) -> String {
177 match self {
178 StyleFilter::Blend(mode) => format!("blend({})", mode.print_as_css_value()),
179 StyleFilter::Flood(c) => format!("flood({})", c.to_hash()),
180 StyleFilter::Blur(c) => {
181 if c.width == c.height {
182 format!("blur({})", c.width)
183 } else {
184 format!("blur({} {})", c.width, c.height)
185 }
186 }
187 StyleFilter::Opacity(c) => format!("opacity({})", c),
188 StyleFilter::ColorMatrix(c) => format!(
189 "color-matrix({})",
190 c.as_slice()
191 .iter()
192 .map(|s| format!("{}", s))
193 .collect::<Vec<_>>()
194 .join(" ")
195 ),
196 StyleFilter::DropShadow(shadow) => {
197 format!("drop-shadow({})", shadow.print_as_css_value())
198 }
199 StyleFilter::ComponentTransfer => "component-transfer".to_string(),
200 StyleFilter::Offset(o) => format!("offset({} {})", o.x, o.y),
201 StyleFilter::Composite(c) => format!("composite({})", c.print_as_css_value()),
202 StyleFilter::Brightness(v) => format!("brightness({})", v),
203 StyleFilter::Contrast(v) => format!("contrast({})", v),
204 StyleFilter::Grayscale(v) => format!("grayscale({})", v),
205 StyleFilter::HueRotate(a) => format!("hue-rotate({})", a),
206 StyleFilter::Invert(v) => format!("invert({})", v),
207 StyleFilter::Saturate(v) => format!("saturate({})", v),
208 StyleFilter::Sepia(v) => format!("sepia({})", v),
209 }
210 }
211}
212
213impl PrintAsCssValue for StyleCompositeFilter {
214 fn print_as_css_value(&self) -> String {
215 match self {
216 StyleCompositeFilter::Over => "over".to_string(),
217 StyleCompositeFilter::In => "in".to_string(),
218 StyleCompositeFilter::Atop => "atop".to_string(),
219 StyleCompositeFilter::Out => "out".to_string(),
220 StyleCompositeFilter::Xor => "xor".to_string(),
221 StyleCompositeFilter::Lighter => "lighter".to_string(),
222 StyleCompositeFilter::Arithmetic(fv) => {
223 format!("arithmetic {} {} {} {}", fv.k1, fv.k2, fv.k3, fv.k4)
224 }
225 }
226 }
227}
228
229#[cfg(feature = "parser")]
232mod parser {
233 use super::*;
234 use crate::props::basic::parse_percentage_value;
235
236 #[derive(Clone, PartialEq)]
239 pub enum CssStyleFilterParseError<'a> {
240 InvalidFilter(&'a str),
241 InvalidParenthesis(ParenthesisParseError<'a>),
242 Shadow(CssShadowParseError<'a>),
243 BlendMode(InvalidValueErr<'a>),
244 Color(CssColorParseError<'a>),
245 Opacity(PercentageParseError),
246 Blur(CssStyleBlurParseError<'a>),
247 ColorMatrix(CssStyleColorMatrixParseError<'a>),
248 Offset(CssStyleFilterOffsetParseError<'a>),
249 Composite(CssStyleCompositeFilterParseError<'a>),
250 Angle(CssAngleValueParseError<'a>),
251 }
252
253 impl_debug_as_display!(CssStyleFilterParseError<'a>);
254 impl_display! { CssStyleFilterParseError<'a>, {
255 InvalidFilter(e) => format!("Invalid filter function: \"{}\"", e),
256 InvalidParenthesis(e) => format!("Invalid filter syntax - parenthesis error: {}", e),
257 Shadow(e) => format!("Error parsing drop-shadow(): {}", e),
258 BlendMode(e) => format!("Error parsing blend(): invalid value \"{}\"", e.0),
259 Color(e) => format!("Error parsing flood(): {}", e),
260 Opacity(e) => format!("Error parsing opacity(): {}", e),
261 Blur(e) => format!("Error parsing blur(): {}", e),
262 ColorMatrix(e) => format!("Error parsing color-matrix(): {}", e),
263 Offset(e) => format!("Error parsing offset(): {}", e),
264 Composite(e) => format!("Error parsing composite(): {}", e),
265 Angle(e) => format!("Error parsing hue-rotate(): {}", e),
266 }}
267
268 impl_from!(
269 ParenthesisParseError<'a>,
270 CssStyleFilterParseError::InvalidParenthesis
271 );
272 impl_from!(InvalidValueErr<'a>, CssStyleFilterParseError::BlendMode);
273 impl_from!(CssStyleBlurParseError<'a>, CssStyleFilterParseError::Blur);
274 impl_from!(CssColorParseError<'a>, CssStyleFilterParseError::Color);
275 impl_from!(
276 CssStyleColorMatrixParseError<'a>,
277 CssStyleFilterParseError::ColorMatrix
278 );
279 impl_from!(
280 CssStyleFilterOffsetParseError<'a>,
281 CssStyleFilterParseError::Offset
282 );
283 impl_from!(
284 CssStyleCompositeFilterParseError<'a>,
285 CssStyleFilterParseError::Composite
286 );
287 impl_from!(CssShadowParseError<'a>, CssStyleFilterParseError::Shadow);
288 impl_from!(CssAngleValueParseError<'a>, CssStyleFilterParseError::Angle);
289
290 impl<'a> From<PercentageParseError> for CssStyleFilterParseError<'a> {
291 fn from(p: PercentageParseError) -> Self {
292 Self::Opacity(p)
293 }
294 }
295
296 impl<'a> From<MixBlendModeParseError<'a>> for CssStyleFilterParseError<'a> {
297 fn from(e: MixBlendModeParseError<'a>) -> Self {
298 match e {
300 MixBlendModeParseError::InvalidValue(err) => Self::BlendMode(err),
301 }
302 }
303 }
304
305 #[derive(Debug, Clone, PartialEq)]
306 pub enum CssStyleFilterParseErrorOwned {
307 InvalidFilter(String),
308 InvalidParenthesis(ParenthesisParseErrorOwned),
309 Shadow(CssShadowParseErrorOwned),
310 BlendMode(InvalidValueErrOwned),
311 Color(CssColorParseErrorOwned),
312 Opacity(PercentageParseError),
313 Blur(CssStyleBlurParseErrorOwned),
314 ColorMatrix(CssStyleColorMatrixParseErrorOwned),
315 Offset(CssStyleFilterOffsetParseErrorOwned),
316 Composite(CssStyleCompositeFilterParseErrorOwned),
317 Angle(CssAngleValueParseErrorOwned),
318 }
319
320 impl<'a> CssStyleFilterParseError<'a> {
321 pub fn to_contained(&self) -> CssStyleFilterParseErrorOwned {
322 match self {
323 Self::InvalidFilter(s) => {
324 CssStyleFilterParseErrorOwned::InvalidFilter(s.to_string())
325 }
326 Self::InvalidParenthesis(e) => {
327 CssStyleFilterParseErrorOwned::InvalidParenthesis(e.to_contained())
328 }
329 Self::Shadow(e) => CssStyleFilterParseErrorOwned::Shadow(e.to_contained()),
330 Self::BlendMode(e) => CssStyleFilterParseErrorOwned::BlendMode(e.to_contained()),
331 Self::Color(e) => CssStyleFilterParseErrorOwned::Color(e.to_contained()),
332 Self::Opacity(e) => CssStyleFilterParseErrorOwned::Opacity(e.clone()),
333 Self::Blur(e) => CssStyleFilterParseErrorOwned::Blur(e.to_contained()),
334 Self::ColorMatrix(e) => {
335 CssStyleFilterParseErrorOwned::ColorMatrix(e.to_contained())
336 }
337 Self::Offset(e) => CssStyleFilterParseErrorOwned::Offset(e.to_contained()),
338 Self::Composite(e) => CssStyleFilterParseErrorOwned::Composite(e.to_contained()),
339 Self::Angle(e) => CssStyleFilterParseErrorOwned::Angle(e.to_contained()),
340 }
341 }
342 }
343
344 impl CssStyleFilterParseErrorOwned {
345 pub fn to_shared<'a>(&'a self) -> CssStyleFilterParseError<'a> {
346 match self {
347 Self::InvalidFilter(s) => CssStyleFilterParseError::InvalidFilter(s),
348 Self::InvalidParenthesis(e) => {
349 CssStyleFilterParseError::InvalidParenthesis(e.to_shared())
350 }
351 Self::Shadow(e) => CssStyleFilterParseError::Shadow(e.to_shared()),
352 Self::BlendMode(e) => CssStyleFilterParseError::BlendMode(e.to_shared()),
353 Self::Color(e) => CssStyleFilterParseError::Color(e.to_shared()),
354 Self::Opacity(e) => CssStyleFilterParseError::Opacity(e.clone()),
355 Self::Blur(e) => CssStyleFilterParseError::Blur(e.to_shared()),
356 Self::ColorMatrix(e) => CssStyleFilterParseError::ColorMatrix(e.to_shared()),
357 Self::Offset(e) => CssStyleFilterParseError::Offset(e.to_shared()),
358 Self::Composite(e) => CssStyleFilterParseError::Composite(e.to_shared()),
359 Self::Angle(e) => CssStyleFilterParseError::Angle(e.to_shared()),
360 }
361 }
362 }
363
364 #[derive(Clone, PartialEq)]
367 pub enum CssStyleBlurParseError<'a> {
368 Pixel(CssPixelValueParseError<'a>),
369 TooManyComponents(&'a str),
370 }
371
372 impl_debug_as_display!(CssStyleBlurParseError<'a>);
373 impl_display! { CssStyleBlurParseError<'a>, {
374 Pixel(e) => format!("Invalid pixel value: {}", e),
375 TooManyComponents(input) => format!("Expected 1 or 2 components, got more: \"{}\"", input),
376 }}
377 impl_from!(CssPixelValueParseError<'a>, CssStyleBlurParseError::Pixel);
378
379 #[derive(Debug, Clone, PartialEq)]
380 pub enum CssStyleBlurParseErrorOwned {
381 Pixel(CssPixelValueParseErrorOwned),
382 TooManyComponents(String),
383 }
384
385 impl<'a> CssStyleBlurParseError<'a> {
386 pub fn to_contained(&self) -> CssStyleBlurParseErrorOwned {
387 match self {
388 Self::Pixel(e) => CssStyleBlurParseErrorOwned::Pixel(e.to_contained()),
389 Self::TooManyComponents(s) => {
390 CssStyleBlurParseErrorOwned::TooManyComponents(s.to_string())
391 }
392 }
393 }
394 }
395
396 impl CssStyleBlurParseErrorOwned {
397 pub fn to_shared<'a>(&'a self) -> CssStyleBlurParseError<'a> {
398 match self {
399 Self::Pixel(e) => CssStyleBlurParseError::Pixel(e.to_shared()),
400 Self::TooManyComponents(s) => CssStyleBlurParseError::TooManyComponents(s),
401 }
402 }
403 }
404
405 #[derive(Clone, PartialEq)]
406 pub enum CssStyleColorMatrixParseError<'a> {
407 Float(ParseFloatError),
408 WrongNumberOfComponents {
409 expected: usize,
410 got: usize,
411 input: &'a str,
412 },
413 }
414
415 impl_debug_as_display!(CssStyleColorMatrixParseError<'a>);
416 impl_display! { CssStyleColorMatrixParseError<'a>, {
417 Float(e) => format!("Error parsing floating-point value: {}", e),
418 WrongNumberOfComponents { expected, got, input } => format!("Expected {} components, got {}: \"{}\"", expected, got, input),
419 }}
420 impl<'a> From<ParseFloatError> for CssStyleColorMatrixParseError<'a> {
421 fn from(p: ParseFloatError) -> Self {
422 Self::Float(p)
423 }
424 }
425
426 #[derive(Debug, Clone, PartialEq)]
427 pub enum CssStyleColorMatrixParseErrorOwned {
428 Float(ParseFloatError),
429 WrongNumberOfComponents {
430 expected: usize,
431 got: usize,
432 input: String,
433 },
434 }
435
436 impl<'a> CssStyleColorMatrixParseError<'a> {
437 pub fn to_contained(&self) -> CssStyleColorMatrixParseErrorOwned {
438 match self {
439 Self::Float(e) => CssStyleColorMatrixParseErrorOwned::Float(e.clone()),
440 Self::WrongNumberOfComponents {
441 expected,
442 got,
443 input,
444 } => CssStyleColorMatrixParseErrorOwned::WrongNumberOfComponents {
445 expected: *expected,
446 got: *got,
447 input: input.to_string(),
448 },
449 }
450 }
451 }
452
453 impl CssStyleColorMatrixParseErrorOwned {
454 pub fn to_shared<'a>(&'a self) -> CssStyleColorMatrixParseError<'a> {
455 match self {
456 Self::Float(e) => CssStyleColorMatrixParseError::Float(e.clone()),
457 Self::WrongNumberOfComponents {
458 expected,
459 got,
460 input,
461 } => CssStyleColorMatrixParseError::WrongNumberOfComponents {
462 expected: *expected,
463 got: *got,
464 input,
465 },
466 }
467 }
468 }
469
470 #[derive(Clone, PartialEq)]
471 pub enum CssStyleFilterOffsetParseError<'a> {
472 Pixel(CssPixelValueParseError<'a>),
473 WrongNumberOfComponents {
474 expected: usize,
475 got: usize,
476 input: &'a str,
477 },
478 }
479
480 impl_debug_as_display!(CssStyleFilterOffsetParseError<'a>);
481 impl_display! { CssStyleFilterOffsetParseError<'a>, {
482 Pixel(e) => format!("Invalid pixel value: {}", e),
483 WrongNumberOfComponents { expected, got, input } => format!("Expected {} components, got {}: \"{}\"", expected, got, input),
484 }}
485 impl_from!(
486 CssPixelValueParseError<'a>,
487 CssStyleFilterOffsetParseError::Pixel
488 );
489
490 #[derive(Debug, Clone, PartialEq)]
491 pub enum CssStyleFilterOffsetParseErrorOwned {
492 Pixel(CssPixelValueParseErrorOwned),
493 WrongNumberOfComponents {
494 expected: usize,
495 got: usize,
496 input: String,
497 },
498 }
499
500 impl<'a> CssStyleFilterOffsetParseError<'a> {
501 pub fn to_contained(&self) -> CssStyleFilterOffsetParseErrorOwned {
502 match self {
503 Self::Pixel(e) => CssStyleFilterOffsetParseErrorOwned::Pixel(e.to_contained()),
504 Self::WrongNumberOfComponents {
505 expected,
506 got,
507 input,
508 } => CssStyleFilterOffsetParseErrorOwned::WrongNumberOfComponents {
509 expected: *expected,
510 got: *got,
511 input: input.to_string(),
512 },
513 }
514 }
515 }
516
517 impl CssStyleFilterOffsetParseErrorOwned {
518 pub fn to_shared<'a>(&'a self) -> CssStyleFilterOffsetParseError<'a> {
519 match self {
520 Self::Pixel(e) => CssStyleFilterOffsetParseError::Pixel(e.to_shared()),
521 Self::WrongNumberOfComponents {
522 expected,
523 got,
524 input,
525 } => CssStyleFilterOffsetParseError::WrongNumberOfComponents {
526 expected: *expected,
527 got: *got,
528 input,
529 },
530 }
531 }
532 }
533
534 #[derive(Clone, PartialEq)]
535 pub enum CssStyleCompositeFilterParseError<'a> {
536 Invalid(InvalidValueErr<'a>),
537 Float(ParseFloatError),
538 WrongNumberOfComponents {
539 expected: usize,
540 got: usize,
541 input: &'a str,
542 },
543 }
544
545 impl_debug_as_display!(CssStyleCompositeFilterParseError<'a>);
546 impl_display! { CssStyleCompositeFilterParseError<'a>, {
547 Invalid(s) => format!("Invalid composite operator: {}", s.0),
548 Float(e) => format!("Error parsing floating-point value for arithmetic(): {}", e),
549 WrongNumberOfComponents { expected, got, input } => format!("Expected {} components for arithmetic(), got {}: \"{}\"", expected, got, input),
550 }}
551 impl_from!(
552 InvalidValueErr<'a>,
553 CssStyleCompositeFilterParseError::Invalid
554 );
555 impl<'a> From<ParseFloatError> for CssStyleCompositeFilterParseError<'a> {
556 fn from(p: ParseFloatError) -> Self {
557 Self::Float(p)
558 }
559 }
560
561 #[derive(Debug, Clone, PartialEq)]
562 pub enum CssStyleCompositeFilterParseErrorOwned {
563 Invalid(InvalidValueErrOwned),
564 Float(ParseFloatError),
565 WrongNumberOfComponents {
566 expected: usize,
567 got: usize,
568 input: String,
569 },
570 }
571
572 impl<'a> CssStyleCompositeFilterParseError<'a> {
573 pub fn to_contained(&self) -> CssStyleCompositeFilterParseErrorOwned {
574 match self {
575 Self::Invalid(e) => {
576 CssStyleCompositeFilterParseErrorOwned::Invalid(e.to_contained())
577 }
578 Self::Float(e) => CssStyleCompositeFilterParseErrorOwned::Float(e.clone()),
579 Self::WrongNumberOfComponents {
580 expected,
581 got,
582 input,
583 } => CssStyleCompositeFilterParseErrorOwned::WrongNumberOfComponents {
584 expected: *expected,
585 got: *got,
586 input: input.to_string(),
587 },
588 }
589 }
590 }
591
592 impl CssStyleCompositeFilterParseErrorOwned {
593 pub fn to_shared<'a>(&'a self) -> CssStyleCompositeFilterParseError<'a> {
594 match self {
595 Self::Invalid(e) => CssStyleCompositeFilterParseError::Invalid(e.to_shared()),
596 Self::Float(e) => CssStyleCompositeFilterParseError::Float(e.clone()),
597 Self::WrongNumberOfComponents {
598 expected,
599 got,
600 input,
601 } => CssStyleCompositeFilterParseError::WrongNumberOfComponents {
602 expected: *expected,
603 got: *got,
604 input,
605 },
606 }
607 }
608 }
609
610 pub fn parse_style_filter_vec<'a>(
614 input: &'a str,
615 ) -> Result<StyleFilterVec, CssStyleFilterParseError<'a>> {
616 let mut filters = Vec::new();
617 let mut remaining = input.trim();
618 while !remaining.is_empty() {
619 let (filter, rest) = parse_one_filter_function(remaining)?;
620 filters.push(filter);
621 remaining = rest.trim_start();
622 }
623 Ok(filters.into())
624 }
625
626 fn parse_one_filter_function<'a>(
629 input: &'a str,
630 ) -> Result<(StyleFilter, &'a str), CssStyleFilterParseError<'a>> {
631 let open_paren = input
632 .find('(')
633 .ok_or(CssStyleFilterParseError::InvalidFilter(input))?;
634 let func_name = &input[..open_paren];
635
636 let mut balance = 1;
637 let mut close_paren = 0;
638 for (i, c) in input.char_indices().skip(open_paren + 1) {
639 if c == '(' {
640 balance += 1;
641 } else if c == ')' {
642 balance -= 1;
643 if balance == 0 {
644 close_paren = i;
645 break;
646 }
647 }
648 }
649
650 if balance != 0 {
651 return Err(ParenthesisParseError::UnclosedBraces.into());
652 }
653
654 let full_function = &input[..=close_paren];
655 let rest = &input[(close_paren + 1)..];
656
657 let filter = parse_style_filter(full_function)?;
658 Ok((filter, rest))
659 }
660
661 pub fn parse_style_filter<'a>(
663 input: &'a str,
664 ) -> Result<StyleFilter, CssStyleFilterParseError<'a>> {
665 let (filter_type, filter_values) = parse_parentheses(
666 input,
667 &[
668 "blend",
669 "flood",
670 "blur",
671 "opacity",
672 "color-matrix",
673 "drop-shadow",
674 "component-transfer",
675 "offset",
676 "composite",
677 "brightness",
678 "contrast",
679 "grayscale",
680 "hue-rotate",
681 "invert",
682 "saturate",
683 "sepia",
684 ],
685 )?;
686
687 match filter_type {
688 "blend" => Ok(StyleFilter::Blend(parse_style_mix_blend_mode(
689 filter_values,
690 )?)),
691 "flood" => Ok(StyleFilter::Flood(parse_css_color(filter_values)?)),
692 "blur" => Ok(StyleFilter::Blur(parse_style_blur(filter_values)?)),
693 "opacity" => {
694 let val = parse_percentage_value(filter_values)?;
695 let normalized = val.normalized();
697 if normalized < 0.0 || normalized > 1.0 {
698 return Err(CssStyleFilterParseError::Opacity(
699 PercentageParseError::InvalidUnit(filter_values.to_string().into()),
700 ));
701 }
702 Ok(StyleFilter::Opacity(val))
703 }
704 "color-matrix" => Ok(StyleFilter::ColorMatrix(parse_color_matrix(filter_values)?)),
705 "drop-shadow" => Ok(StyleFilter::DropShadow(parse_style_box_shadow(
706 filter_values,
707 )?)),
708 "component-transfer" => Ok(StyleFilter::ComponentTransfer),
709 "offset" => Ok(StyleFilter::Offset(parse_filter_offset(filter_values)?)),
710 "composite" => Ok(StyleFilter::Composite(parse_filter_composite(
711 filter_values,
712 )?)),
713 "brightness" => Ok(StyleFilter::Brightness(parse_percentage_value(filter_values)?)),
714 "contrast" => Ok(StyleFilter::Contrast(parse_percentage_value(filter_values)?)),
715 "grayscale" => Ok(StyleFilter::Grayscale(parse_percentage_value(filter_values)?)),
716 "hue-rotate" => Ok(StyleFilter::HueRotate(parse_angle_value(filter_values)?)),
717 "invert" => Ok(StyleFilter::Invert(parse_percentage_value(filter_values)?)),
718 "saturate" => Ok(StyleFilter::Saturate(parse_percentage_value(filter_values)?)),
719 "sepia" => Ok(StyleFilter::Sepia(parse_percentage_value(filter_values)?)),
720 _ => unreachable!(),
721 }
722 }
723
724 fn parse_style_blur<'a>(input: &'a str) -> Result<StyleBlur, CssStyleBlurParseError<'a>> {
725 let mut iter = input.split_whitespace();
726 let width_str = iter.next().unwrap_or("");
727 let height_str = iter.next();
728
729 if iter.next().is_some() {
730 return Err(CssStyleBlurParseError::TooManyComponents(input));
731 }
732
733 let width = parse_pixel_value(width_str)?;
734 let height = match height_str {
735 Some(s) => parse_pixel_value(s)?,
736 None => width, };
738
739 Ok(StyleBlur { width, height })
740 }
741
742 fn parse_color_matrix<'a>(
743 input: &'a str,
744 ) -> Result<StyleColorMatrix, CssStyleColorMatrixParseError<'a>> {
745 let components: Vec<_> = input.split_whitespace().collect();
746 if components.len() != 20 {
747 return Err(CssStyleColorMatrixParseError::WrongNumberOfComponents {
748 expected: 20,
749 got: components.len(),
750 input,
751 });
752 }
753
754 let mut values = [FloatValue::const_new(0); 20];
755 for (i, comp) in components.iter().enumerate() {
756 values[i] = parse_float_value(comp)?;
757 }
758
759 Ok(StyleColorMatrix {
760 m0: values[0],
761 m1: values[1],
762 m2: values[2],
763 m3: values[3],
764 m4: values[4],
765 m5: values[5],
766 m6: values[6],
767 m7: values[7],
768 m8: values[8],
769 m9: values[9],
770 m10: values[10],
771 m11: values[11],
772 m12: values[12],
773 m13: values[13],
774 m14: values[14],
775 m15: values[15],
776 m16: values[16],
777 m17: values[17],
778 m18: values[18],
779 m19: values[19],
780 })
781 }
782
783 fn parse_filter_offset<'a>(
784 input: &'a str,
785 ) -> Result<StyleFilterOffset, CssStyleFilterOffsetParseError<'a>> {
786 let components: Vec<_> = input.split_whitespace().collect();
787 if components.len() != 2 {
788 return Err(CssStyleFilterOffsetParseError::WrongNumberOfComponents {
789 expected: 2,
790 got: components.len(),
791 input,
792 });
793 }
794
795 let x = parse_pixel_value(components[0])?;
796 let y = parse_pixel_value(components[1])?;
797
798 Ok(StyleFilterOffset { x, y })
799 }
800
801 fn parse_filter_composite<'a>(
802 input: &'a str,
803 ) -> Result<StyleCompositeFilter, CssStyleCompositeFilterParseError<'a>> {
804 let mut iter = input.split_whitespace();
805 let operator = iter.next().unwrap_or("");
806
807 match operator {
808 "over" => Ok(StyleCompositeFilter::Over),
809 "in" => Ok(StyleCompositeFilter::In),
810 "atop" => Ok(StyleCompositeFilter::Atop),
811 "out" => Ok(StyleCompositeFilter::Out),
812 "xor" => Ok(StyleCompositeFilter::Xor),
813 "lighter" => Ok(StyleCompositeFilter::Lighter),
814 "arithmetic" => {
815 let mut values = [FloatValue::const_new(0); 4];
816 for (i, val) in values.iter_mut().enumerate() {
817 let s = iter.next().ok_or(
818 CssStyleCompositeFilterParseError::WrongNumberOfComponents {
819 expected: 4,
820 got: i,
821 input,
822 },
823 )?;
824 *val = parse_float_value(s)?;
825 }
826 Ok(StyleCompositeFilter::Arithmetic(ArithmeticCoefficients {
827 k1: values[0],
828 k2: values[1],
829 k3: values[2],
830 k4: values[3],
831 }))
832 }
833 _ => Err(InvalidValueErr(operator).into()),
834 }
835 }
836}
837#[cfg(feature = "parser")]
838pub use parser::{
839 parse_style_filter_vec, CssStyleBlurParseError, CssStyleBlurParseErrorOwned,
840 CssStyleColorMatrixParseError, CssStyleColorMatrixParseErrorOwned,
841 CssStyleCompositeFilterParseError, CssStyleCompositeFilterParseErrorOwned,
842 CssStyleFilterOffsetParseError, CssStyleFilterOffsetParseErrorOwned, CssStyleFilterParseError,
843 CssStyleFilterParseErrorOwned,
844};
845
846#[cfg(all(test, feature = "parser"))]
847mod tests {
848 use super::*;
849 use crate::props::style::filter::parser::parse_style_filter;
850
851 #[test]
852 fn test_parse_single_filter_functions() {
853 let blur = parse_style_filter("blur(5px)").unwrap();
855 assert!(matches!(blur, StyleFilter::Blur(_)));
856 if let StyleFilter::Blur(b) = blur {
857 assert_eq!(b.width, PixelValue::px(5.0));
858 assert_eq!(b.height, PixelValue::px(5.0));
859 }
860
861 let blur2 = parse_style_filter("blur(2px 4px)").unwrap();
863 if let StyleFilter::Blur(b) = blur2 {
864 assert_eq!(b.width, PixelValue::px(2.0));
865 assert_eq!(b.height, PixelValue::px(4.0));
866 }
867
868 let shadow = parse_style_filter("drop-shadow(10px 5px 5px #888)").unwrap();
870 assert!(matches!(shadow, StyleFilter::DropShadow(_)));
871 if let StyleFilter::DropShadow(s) = shadow {
872 assert_eq!(s.offset_x.inner, PixelValue::px(10.0));
873 assert_eq!(s.blur_radius.inner, PixelValue::px(5.0));
874 assert_eq!(s.color, ColorU::new_rgb(0x88, 0x88, 0x88));
875 }
876
877 let opacity = parse_style_filter("opacity(50%)").unwrap();
879 assert!(matches!(opacity, StyleFilter::Opacity(_)));
880 if let StyleFilter::Opacity(p) = opacity {
881 assert_eq!(p.normalized(), 0.5);
882 }
883
884 let flood = parse_style_filter("flood(red)").unwrap();
886 assert_eq!(flood, StyleFilter::Flood(ColorU::RED));
887
888 let composite = parse_style_filter("composite(in)").unwrap();
890 assert_eq!(composite, StyleFilter::Composite(StyleCompositeFilter::In));
891
892 let offset = parse_style_filter("offset(10px 20%)").unwrap();
894 if let StyleFilter::Offset(o) = offset {
895 assert_eq!(o.x, PixelValue::px(10.0));
896 assert_eq!(o.y, PixelValue::percent(20.0));
897 }
898 }
899
900 #[test]
901 fn test_parse_filter_vec() {
902 let filters =
903 parse_style_filter_vec("blur(5px) drop-shadow(10px 5px #888) opacity(0.8)").unwrap();
904 assert_eq!(filters.len(), 3);
905 assert!(matches!(filters.as_slice()[0], StyleFilter::Blur(_)));
906 assert!(matches!(filters.as_slice()[1], StyleFilter::DropShadow(_)));
907 assert!(matches!(filters.as_slice()[2], StyleFilter::Opacity(_)));
908 }
909
910 #[test]
911 fn test_parse_filter_errors() {
912 assert!(parse_style_filter_vec("blurry(5px)").is_err());
914 assert!(parse_style_filter_vec("blur(5px 10px 15px)").is_err());
916 assert!(parse_style_filter_vec("opacity(2)").is_err()); assert!(parse_style_filter_vec("blur(5px").is_err());
919 }
920}