1use crate::corety::AzString;
4use alloc::string::{String, ToString};
5use core::fmt;
6
7use crate::{codegen::format::FormatAsRustCode, props::formatter::PrintAsCssValue};
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[repr(C)]
14#[derive(Default)]
15pub enum StyleListStyleType {
16 None,
17 #[default]
18 Disc,
19 Circle,
20 Square,
21 Decimal,
22 DecimalLeadingZero,
23 LowerRoman,
24 UpperRoman,
25 LowerGreek,
26 UpperGreek,
27 LowerAlpha,
28 UpperAlpha,
29}
30
31impl PrintAsCssValue for StyleListStyleType {
32 fn print_as_css_value(&self) -> String {
33 use StyleListStyleType::{
34 Circle, Decimal, DecimalLeadingZero, Disc, LowerAlpha, LowerGreek, LowerRoman, None,
35 Square, UpperAlpha, UpperGreek, UpperRoman,
36 };
37 String::from(match self {
38 None => "none",
39 Disc => "disc",
40 Circle => "circle",
41 Square => "square",
42 Decimal => "decimal",
43 DecimalLeadingZero => "decimal-leading-zero",
44 LowerRoman => "lower-roman",
45 UpperRoman => "upper-roman",
46 LowerGreek => "lower-greek",
47 UpperGreek => "upper-greek",
48 LowerAlpha => "lower-alpha",
49 UpperAlpha => "upper-alpha",
50 })
51 }
52}
53
54impl FormatAsRustCode for StyleListStyleType {
55 fn format_as_rust_code(&self, _tabs: usize) -> String {
56 use StyleListStyleType::{
57 Circle, Decimal, DecimalLeadingZero, Disc, LowerAlpha, LowerGreek, LowerRoman, None,
58 Square, UpperAlpha, UpperGreek, UpperRoman,
59 };
60 format!(
61 "StyleListStyleType::{}",
62 match self {
63 None => "None",
64 Disc => "Disc",
65 Circle => "Circle",
66 Square => "Square",
67 Decimal => "Decimal",
68 DecimalLeadingZero => "DecimalLeadingZero",
69 LowerRoman => "LowerRoman",
70 UpperRoman => "UpperRoman",
71 LowerGreek => "LowerGreek",
72 UpperGreek => "UpperGreek",
73 LowerAlpha => "LowerAlpha",
74 UpperAlpha => "UpperAlpha",
75 }
76 )
77 }
78}
79
80impl fmt::Display for StyleListStyleType {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{}", self.print_as_css_value())
83 }
84}
85
86#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
90#[repr(C)]
91#[derive(Default)]
92pub enum StyleListStylePosition {
93 Inside,
94 #[default]
95 Outside,
96}
97
98impl PrintAsCssValue for StyleListStylePosition {
99 fn print_as_css_value(&self) -> String {
100 use StyleListStylePosition::{Inside, Outside};
101 String::from(match self {
102 Inside => "inside",
103 Outside => "outside",
104 })
105 }
106}
107
108impl FormatAsRustCode for StyleListStylePosition {
109 fn format_as_rust_code(&self, _tabs: usize) -> String {
110 use StyleListStylePosition::{Inside, Outside};
111 format!(
112 "StyleListStylePosition::{}",
113 match self {
114 Inside => "Inside",
115 Outside => "Outside",
116 }
117 )
118 }
119}
120
121impl fmt::Display for StyleListStylePosition {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(f, "{}", self.print_as_css_value())
124 }
125}
126
127#[cfg(feature = "parser")]
130#[derive(Clone, PartialEq, Eq)]
131pub enum StyleListStyleTypeParseError<'a> {
132 InvalidValue(&'a str),
133}
134
135#[cfg(feature = "parser")]
136impl_debug_as_display!(StyleListStyleTypeParseError<'a>);
137
138#[cfg(feature = "parser")]
139impl_display! { StyleListStyleTypeParseError<'a>, {
140 InvalidValue(val) => format!("Invalid list-style-type value: \"{}\"", val),
141}}
142
143#[cfg(feature = "parser")]
144#[derive(Debug, Clone, PartialEq, Eq)]
145#[repr(C, u8)]
146pub enum StyleListStyleTypeParseErrorOwned {
147 InvalidValue(AzString),
148}
149
150#[cfg(feature = "parser")]
151impl StyleListStyleTypeParseError<'_> {
152 #[must_use]
153 pub fn to_contained(&self) -> StyleListStyleTypeParseErrorOwned {
154 match self {
155 Self::InvalidValue(s) => {
156 StyleListStyleTypeParseErrorOwned::InvalidValue((*s).to_string().into())
157 }
158 }
159 }
160}
161
162#[cfg(feature = "parser")]
163impl StyleListStyleTypeParseErrorOwned {
164 #[must_use]
165 pub fn to_shared(&self) -> StyleListStyleTypeParseError<'_> {
166 match self {
167 Self::InvalidValue(s) => StyleListStyleTypeParseError::InvalidValue(s.as_str()),
168 }
169 }
170}
171
172#[cfg(feature = "parser")]
174pub fn parse_style_list_style_type(
178 input: &str,
179) -> Result<StyleListStyleType, StyleListStyleTypeParseError<'_>> {
180 let input = input.trim();
181 match input {
182 "none" => Ok(StyleListStyleType::None),
183 "disc" => Ok(StyleListStyleType::Disc),
184 "circle" => Ok(StyleListStyleType::Circle),
185 "square" => Ok(StyleListStyleType::Square),
186 "decimal" => Ok(StyleListStyleType::Decimal),
187 "decimal-leading-zero" => Ok(StyleListStyleType::DecimalLeadingZero),
188 "lower-roman" => Ok(StyleListStyleType::LowerRoman),
189 "upper-roman" => Ok(StyleListStyleType::UpperRoman),
190 "lower-greek" => Ok(StyleListStyleType::LowerGreek),
191 "upper-greek" => Ok(StyleListStyleType::UpperGreek),
192 "lower-alpha" | "lower-latin" => Ok(StyleListStyleType::LowerAlpha),
193 "upper-alpha" | "upper-latin" => Ok(StyleListStyleType::UpperAlpha),
194 _ => Err(StyleListStyleTypeParseError::InvalidValue(input)),
195 }
196}
197
198#[cfg(feature = "parser")]
199#[derive(Clone, PartialEq, Eq)]
200pub enum StyleListStylePositionParseError<'a> {
201 InvalidValue(&'a str),
202}
203
204#[cfg(feature = "parser")]
205impl_debug_as_display!(StyleListStylePositionParseError<'a>);
206
207#[cfg(feature = "parser")]
208impl_display! { StyleListStylePositionParseError<'a>, {
209 InvalidValue(val) => format!("Invalid list-style-position value: \"{}\"", val),
210}}
211
212#[cfg(feature = "parser")]
213#[derive(Debug, Clone, PartialEq, Eq)]
214#[repr(C, u8)]
215pub enum StyleListStylePositionParseErrorOwned {
216 InvalidValue(AzString),
217}
218
219#[cfg(feature = "parser")]
220impl StyleListStylePositionParseError<'_> {
221 #[must_use]
222 pub fn to_contained(&self) -> StyleListStylePositionParseErrorOwned {
223 match self {
224 Self::InvalidValue(s) => {
225 StyleListStylePositionParseErrorOwned::InvalidValue((*s).to_string().into())
226 }
227 }
228 }
229}
230
231#[cfg(feature = "parser")]
232impl StyleListStylePositionParseErrorOwned {
233 #[must_use]
234 pub fn to_shared(&self) -> StyleListStylePositionParseError<'_> {
235 match self {
236 Self::InvalidValue(s) => StyleListStylePositionParseError::InvalidValue(s.as_str()),
237 }
238 }
239}
240
241#[cfg(feature = "parser")]
243pub fn parse_style_list_style_position(
247 input: &str,
248) -> Result<StyleListStylePosition, StyleListStylePositionParseError<'_>> {
249 let input = input.trim();
250 match input {
251 "inside" => Ok(StyleListStylePosition::Inside),
252 "outside" => Ok(StyleListStylePosition::Outside),
253 _ => Err(StyleListStylePositionParseError::InvalidValue(input)),
254 }
255}
256
257#[cfg(test)]
258mod autotest_generated {
259 use std::collections::BTreeSet;
271
272 use super::*;
273
274 const ALL_TYPES: [StyleListStyleType; 12] = [
275 StyleListStyleType::None,
276 StyleListStyleType::Disc,
277 StyleListStyleType::Circle,
278 StyleListStyleType::Square,
279 StyleListStyleType::Decimal,
280 StyleListStyleType::DecimalLeadingZero,
281 StyleListStyleType::LowerRoman,
282 StyleListStyleType::UpperRoman,
283 StyleListStyleType::LowerGreek,
284 StyleListStyleType::UpperGreek,
285 StyleListStyleType::LowerAlpha,
286 StyleListStyleType::UpperAlpha,
287 ];
288
289 const ALL_POSITIONS: [StyleListStylePosition; 2] = [
290 StyleListStylePosition::Inside,
291 StyleListStylePosition::Outside,
292 ];
293
294 #[cfg(feature = "parser")]
296 fn hostile_inputs() -> Vec<String> {
297 let mut v = vec![
298 String::new(),
299 " ".to_string(),
300 " \t\n\r".to_string(),
301 "\u{0c}\u{0b}".to_string(),
302 "\0".to_string(),
303 "disc\0".to_string(),
304 "\0disc".to_string(),
305 "-".to_string(),
306 "--".to_string(),
307 ";".to_string(),
308 "{}".to_string(),
309 "/* disc */".to_string(),
310 "disc;garbage".to_string(),
311 "disc disc".to_string(),
312 "disc,circle".to_string(),
313 "disc!important".to_string(),
314 "inside;".to_string(),
315 "list-style-type: disc".to_string(),
316 "lower_roman".to_string(),
317 "lower - roman".to_string(),
318 "lowerroman".to_string(),
319 "0".to_string(),
321 "-0".to_string(),
322 "NaN".to_string(),
323 "nan".to_string(),
324 "inf".to_string(),
325 "-inf".to_string(),
326 "infinity".to_string(),
327 i64::MAX.to_string(),
328 i64::MIN.to_string(),
329 u64::MAX.to_string(),
330 f64::MAX.to_string(),
331 f64::MIN_POSITIVE.to_string(),
332 "1e308".to_string(),
333 "-1e-308".to_string(),
334 "\u{1F600}".to_string(),
336 "disc\u{301}".to_string(),
337 "\u{301}".to_string(),
338 "\u{202E}disc".to_string(),
339 "di\u{200B}sc".to_string(),
340 "DISС".to_string(), "disc".to_string(), "круг".to_string(),
343 "\u{FFFD}".to_string(),
344 ];
345 v.push("(".repeat(10_000));
348 v.push("[".repeat(10_000));
349 v.push("disc(".repeat(10_000));
350 v.push(format!("{}disc{}", "(".repeat(10_000), ")".repeat(10_000)));
351 v
352 }
353
354 #[test]
357 fn css_values_are_well_formed_for_every_type() {
358 for v in ALL_TYPES {
359 let s = v.print_as_css_value();
360 assert!(!s.is_empty(), "{v:?} serialized to an empty CSS value");
361 assert!(
362 s.chars().all(|c| c.is_ascii_lowercase() || c == '-'),
363 "{v:?} serialized to {s:?}, which is not a bare lowercase CSS keyword"
364 );
365 assert!(!s.starts_with('-') && !s.ends_with('-'), "{v:?} -> {s:?}");
366 }
367 }
368
369 #[test]
370 fn css_values_are_well_formed_for_every_position() {
371 for v in ALL_POSITIONS {
372 let s = v.print_as_css_value();
373 assert!(!s.is_empty(), "{v:?} serialized to an empty CSS value");
374 assert!(s.chars().all(|c| c.is_ascii_lowercase()), "{v:?} -> {s:?}");
375 }
376 }
377
378 #[test]
379 fn css_values_are_unique() {
380 let types: BTreeSet<String> = ALL_TYPES
381 .iter()
382 .map(PrintAsCssValue::print_as_css_value)
383 .collect();
384 assert_eq!(
385 types.len(),
386 ALL_TYPES.len(),
387 "two list-style-type variants share a CSS keyword"
388 );
389
390 let positions: BTreeSet<String> = ALL_POSITIONS
391 .iter()
392 .map(PrintAsCssValue::print_as_css_value)
393 .collect();
394 assert_eq!(positions.len(), ALL_POSITIONS.len());
395 }
396
397 #[test]
398 fn display_agrees_with_print_as_css_value() {
399 for v in ALL_TYPES {
400 assert_eq!(
401 v.to_string(),
402 v.print_as_css_value(),
403 "Display diverged for {v:?}"
404 );
405 }
406 for v in ALL_POSITIONS {
407 assert_eq!(
408 v.to_string(),
409 v.print_as_css_value(),
410 "Display diverged for {v:?}"
411 );
412 }
413 }
414
415 #[test]
416 fn display_of_default_is_the_css_initial_value() {
417 assert_eq!(StyleListStyleType::default(), StyleListStyleType::Disc);
419 assert_eq!(StyleListStyleType::default().to_string(), "disc");
420 assert_eq!(
421 StyleListStylePosition::default(),
422 StyleListStylePosition::Outside
423 );
424 assert_eq!(StyleListStylePosition::default().to_string(), "outside");
425 }
426
427 #[test]
428 fn rust_code_names_the_variant_and_ignores_the_tab_argument() {
429 for v in ALL_TYPES {
430 let expected = format!("StyleListStyleType::{v:?}");
432 assert_eq!(v.format_as_rust_code(0), expected);
433 assert_eq!(v.format_as_rust_code(usize::MAX), expected);
435 }
436 for v in ALL_POSITIONS {
437 let expected = format!("StyleListStylePosition::{v:?}");
438 assert_eq!(v.format_as_rust_code(0), expected);
439 assert_eq!(v.format_as_rust_code(usize::MAX), expected);
440 }
441 }
442
443 #[test]
446 #[cfg(feature = "parser")]
447 fn every_type_round_trips_through_its_css_value() {
448 for v in ALL_TYPES {
449 let printed = v.print_as_css_value();
450 assert_eq!(
451 parse_style_list_style_type(&printed),
452 Ok(v),
453 "{v:?} serialized to {printed:?}, which does not parse back"
454 );
455 }
456 }
457
458 #[test]
459 #[cfg(feature = "parser")]
460 fn every_position_round_trips_through_its_css_value() {
461 for v in ALL_POSITIONS {
462 let printed = v.print_as_css_value();
463 assert_eq!(
464 parse_style_list_style_position(&printed),
465 Ok(v),
466 "{v:?} -> {printed:?}"
467 );
468 }
469 }
470
471 #[test]
472 #[cfg(feature = "parser")]
473 fn parsing_is_idempotent_through_reserialization() {
474 for input in [
477 "disc",
478 "none",
479 "circle",
480 "square",
481 "decimal",
482 "decimal-leading-zero",
483 "lower-roman",
484 "upper-roman",
485 "lower-greek",
486 "upper-greek",
487 "lower-alpha",
488 "upper-alpha",
489 "lower-latin",
490 "upper-latin",
491 ] {
492 let first = parse_style_list_style_type(input).expect("known-good keyword");
493 let printed = first.print_as_css_value();
494 let second =
495 parse_style_list_style_type(&printed).expect("reserialized value must reparse");
496 assert_eq!(
497 first, second,
498 "{input:?} was not idempotent (printed {printed:?})"
499 );
500 }
501 }
502
503 #[test]
506 #[cfg(feature = "parser")]
507 fn valid_keywords_map_to_the_expected_variants() {
508 let table = [
509 ("none", StyleListStyleType::None),
510 ("disc", StyleListStyleType::Disc),
511 ("circle", StyleListStyleType::Circle),
512 ("square", StyleListStyleType::Square),
513 ("decimal", StyleListStyleType::Decimal),
514 (
515 "decimal-leading-zero",
516 StyleListStyleType::DecimalLeadingZero,
517 ),
518 ("lower-roman", StyleListStyleType::LowerRoman),
519 ("upper-roman", StyleListStyleType::UpperRoman),
520 ("lower-greek", StyleListStyleType::LowerGreek),
521 ("upper-greek", StyleListStyleType::UpperGreek),
522 ("lower-alpha", StyleListStyleType::LowerAlpha),
523 ("upper-alpha", StyleListStyleType::UpperAlpha),
524 ("lower-latin", StyleListStyleType::LowerAlpha),
526 ("upper-latin", StyleListStyleType::UpperAlpha),
527 ];
528 for (input, expected) in table {
529 assert_eq!(
530 parse_style_list_style_type(input),
531 Ok(expected),
532 "input {input:?}"
533 );
534 }
535
536 assert_eq!(
537 parse_style_list_style_position("inside"),
538 Ok(StyleListStylePosition::Inside)
539 );
540 assert_eq!(
541 parse_style_list_style_position("outside"),
542 Ok(StyleListStylePosition::Outside)
543 );
544 }
545
546 #[test]
547 #[cfg(feature = "parser")]
548 fn ascii_whitespace_padding_is_trimmed() {
549 for padded in [" disc", "disc\n", "\t disc \r\n", "\u{0c}disc\u{0c}"] {
550 assert_eq!(
551 parse_style_list_style_type(padded),
552 Ok(StyleListStyleType::Disc),
553 "padding was not trimmed from {padded:?}"
554 );
555 }
556 assert_eq!(
557 parse_style_list_style_position("\n\t outside \t\n"),
558 Ok(StyleListStylePosition::Outside)
559 );
560 }
561
562 #[test]
563 #[cfg(feature = "parser")]
564 fn parsers_agree_with_str_trim_on_unicode_whitespace() {
565 for pad in ["\u{a0}", "\u{2003}", "\u{3000}"] {
570 let padded = format!("{pad}disc{pad}");
571 assert_eq!(
572 parse_style_list_style_type(&padded).is_ok(),
573 padded.trim() == "disc",
574 "parser and str::trim disagree on {padded:?}"
575 );
576 }
577 }
578
579 #[test]
582 #[cfg(feature = "parser")]
583 fn hostile_input_is_rejected_without_panicking() {
584 for input in hostile_inputs() {
585 let ty = parse_style_list_style_type(&input);
586 assert!(
587 ty.is_err(),
588 "list-style-type accepted hostile input {input:?} as {ty:?}"
589 );
590
591 let pos = parse_style_list_style_position(&input);
592 assert!(
593 pos.is_err(),
594 "list-style-position accepted hostile input {input:?} as {pos:?}"
595 );
596 }
597 }
598
599 #[test]
600 #[cfg(feature = "parser")]
601 fn keyword_matching_is_case_sensitive() {
602 for input in [
606 "Disc",
607 "DISC",
608 "dIsC",
609 "LOWER-ROMAN",
610 "Decimal-Leading-Zero",
611 "NONE",
612 ] {
613 assert!(
614 parse_style_list_style_type(input).is_err(),
615 "unexpectedly case-insensitive for {input:?}"
616 );
617 }
618 for input in ["Inside", "OUTSIDE", "OuTsIdE"] {
619 assert!(
620 parse_style_list_style_position(input).is_err(),
621 "unexpectedly case-insensitive for {input:?}"
622 );
623 }
624 }
625
626 #[test]
627 #[cfg(feature = "parser")]
628 fn megabyte_sized_input_terminates_and_is_rejected() {
629 let long = "a".repeat(1_000_000);
630 assert!(parse_style_list_style_type(&long).is_err());
631 assert!(parse_style_list_style_position(&long).is_err());
632
633 let repeated = "disc".repeat(250_000);
635 assert!(parse_style_list_style_type(&repeated).is_err());
636
637 let padded = format!("{}disc{}", " ".repeat(1_000_000), "\n".repeat(1_000_000));
639 assert_eq!(
640 parse_style_list_style_type(&padded),
641 Ok(StyleListStyleType::Disc)
642 );
643 }
644
645 #[test]
646 #[cfg(feature = "parser")]
647 fn multibyte_input_is_rejected_without_slicing_panics() {
648 for input in [
651 "🙂",
652 " 🙂 ",
653 "日本語",
654 "e\u{301}",
655 "\u{1F600}\u{1F600}\u{1F600}",
656 ] {
657 match parse_style_list_style_type(input) {
658 Ok(v) => panic!("multibyte input {input:?} parsed as {v:?}"),
659 Err(StyleListStyleTypeParseError::InvalidValue(s)) => {
660 assert_eq!(s, input.trim(), "error payload was mangled for {input:?}");
661 }
662 }
663 }
664 }
665
666 #[test]
669 #[cfg(feature = "parser")]
670 fn error_payload_is_the_trimmed_input() {
671 match parse_style_list_style_type(" bogus-keyword ") {
672 Err(StyleListStyleTypeParseError::InvalidValue(s)) => assert_eq!(s, "bogus-keyword"),
673 other => panic!("expected InvalidValue, got {other:?}"),
674 }
675 match parse_style_list_style_position("\t bogus \n") {
676 Err(StyleListStylePositionParseError::InvalidValue(s)) => assert_eq!(s, "bogus"),
677 other => panic!("expected InvalidValue, got {other:?}"),
678 }
679 match parse_style_list_style_type(" \t\n") {
681 Err(StyleListStyleTypeParseError::InvalidValue(s)) => assert!(s.is_empty()),
682 other => panic!("expected InvalidValue(\"\"), got {other:?}"),
683 }
684 }
685
686 #[test]
687 #[cfg(feature = "parser")]
688 fn error_display_quotes_the_offending_value() {
689 let err = parse_style_list_style_type("🙂").unwrap_err();
690 let msg = err.to_string();
691 assert!(msg.contains("list-style-type"), "{msg:?}");
692 assert!(
693 msg.contains('🙂'),
694 "error message dropped the offending value: {msg:?}"
695 );
696
697 let err = parse_style_list_style_position("").unwrap_err();
698 let msg = err.to_string();
699 assert!(msg.contains("list-style-position"), "{msg:?}");
700 assert!(!msg.is_empty());
701 }
702
703 #[test]
706 #[cfg(feature = "parser")]
707 fn type_error_survives_a_contained_shared_round_trip() {
708 let payloads = [
709 "",
710 " ",
711 "bogus",
712 "🙂 combining\u{301}",
713 "\0embedded nul\0",
714 "line\nbreak\r\n",
715 "-",
716 ];
717 for payload in payloads {
718 let shared = StyleListStyleTypeParseError::InvalidValue(payload);
719 let owned = shared.to_contained();
720 match &owned {
721 StyleListStyleTypeParseErrorOwned::InvalidValue(s) => {
722 assert_eq!(s.as_str(), payload, "to_contained mangled {payload:?}");
723 }
724 }
725 assert_eq!(
726 owned.to_shared(),
727 shared,
728 "round-trip lost data for {payload:?}"
729 );
730 }
731 }
732
733 #[test]
734 #[cfg(feature = "parser")]
735 fn position_error_survives_a_contained_shared_round_trip() {
736 for payload in ["", "bogus", "🙂", "\0", " interior spaces "] {
737 let shared = StyleListStylePositionParseError::InvalidValue(payload);
738 let owned = shared.to_contained();
739 match &owned {
740 StyleListStylePositionParseErrorOwned::InvalidValue(s) => {
741 assert_eq!(s.as_str(), payload);
742 }
743 }
744 assert_eq!(
745 owned.to_shared(),
746 shared,
747 "round-trip lost data for {payload:?}"
748 );
749 }
750 }
751
752 #[test]
753 #[cfg(feature = "parser")]
754 fn owned_errors_constructed_directly_convert_back_to_shared() {
755 let owned = StyleListStyleTypeParseErrorOwned::InvalidValue(String::new().into());
756 match owned.to_shared() {
757 StyleListStyleTypeParseError::InvalidValue(s) => assert_eq!(s, ""),
758 }
759
760 let owned = StyleListStylePositionParseErrorOwned::InvalidValue(String::from("x").into());
761 match owned.to_shared() {
762 StyleListStylePositionParseError::InvalidValue(s) => assert_eq!(s, "x"),
763 }
764 }
765
766 #[test]
767 #[cfg(feature = "parser")]
768 fn huge_error_payload_is_carried_without_truncation() {
769 let huge = "z".repeat(100_000);
770 let err = parse_style_list_style_type(&huge).unwrap_err();
771 let owned = err.to_contained();
772 match &owned {
773 StyleListStyleTypeParseErrorOwned::InvalidValue(s) => {
774 assert_eq!(s.as_str().len(), 100_000, "payload was truncated");
775 }
776 }
777 assert_eq!(
778 owned.to_shared(),
779 StyleListStyleTypeParseError::InvalidValue(&huge)
780 );
781 }
782
783 #[test]
784 #[cfg(feature = "parser")]
785 fn to_contained_is_repeatable_and_does_not_consume_the_error() {
786 let err = parse_style_list_style_position("nope").unwrap_err();
787 let a = err.to_contained();
788 let b = err.to_contained();
789 assert_eq!(a, b);
790 assert_eq!(err, StyleListStylePositionParseError::InvalidValue("nope"));
791 }
792}