vue_sfc/ast/block/attribute/
value.rs

1use std::{
2    borrow::{Borrow, Cow},
3    fmt::Display,
4    ops::Deref,
5};
6
7pub use self::error::InvalidAttributeValue;
8
9mod error {
10    use std::error::Error;
11    use std::fmt::Display;
12
13    /// Returned when a function was unable to convert a string to an
14    /// [`AttributeValue`][super::AttributeValue].
15    #[derive(Debug)]
16    pub struct InvalidAttributeValue(pub(super) char);
17
18    impl Display for InvalidAttributeValue {
19        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20            write!(f, "illegal char: `{}`", self.0)
21        }
22    }
23
24    impl Error for InvalidAttributeValue {}
25}
26
27/// The value of an attribute, i.e: `ts` in `<script lang="ts">`.
28#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
29#[must_use]
30pub struct AttributeValue<'a>(Cow<'a, str>);
31
32impl<'a> AttributeValue<'a> {
33    /// Attempts to convert a string to an [`AttributeValue`].
34    ///
35    /// # Errors
36    /// If the string contains both a `U+0022 QUOTATION MARK (")` and an `U+0027 APOSTROPHE (')`,
37    /// it also cannot contain any of the following characters:
38    /// - `U+0009 CHARACTER TABULATION`
39    /// - `U+000A LINE FEED`
40    /// - `U+000C FORM FEED`
41    /// - `U+0020 SPACE`
42    /// - `U+003E GREATER-THAN SIGN (>)`.
43    pub fn from_cow(src: Cow<'a, str>) -> Result<Self, InvalidAttributeValue> {
44        if src.contains('\u{0022}') && src.contains('\u{0027}') {
45            if let Some(ch) = src.chars().find(|ch| {
46                matches!(
47                    ch,
48                    '\u{0009}' | '\u{000A}' | '\u{000C}' | '\u{0020}' | '\u{003E}'
49                )
50            }) {
51                return Err(InvalidAttributeValue(ch));
52            }
53        }
54
55        Ok(Self(src))
56    }
57
58    /// Convert a string into an [`AttributeValue`] **without** validating
59    /// (unless `debug_assertions` is enabled).
60    ///
61    /// # Panics
62    /// If `debug_assertions` is enabled, validate the input and panic on failure.
63    ///
64    /// # Safety
65    /// See string prerequisites of [`AttributeValue::from_cow`].
66    pub unsafe fn from_cow_unchecked(src: Cow<'a, str>) -> Self {
67        if cfg!(debug_assertions) {
68            match Self::from_cow(src) {
69                Ok(val) => val,
70                Err(err) => {
71                    panic!("AttributeValue::from_cow_unchecked(): {err}")
72                }
73            }
74        } else {
75            Self(src)
76        }
77    }
78
79    #[must_use]
80    pub fn as_str(&self) -> &str {
81        &self.0
82    }
83}
84
85impl Deref for AttributeValue<'_> {
86    type Target = str;
87
88    fn deref(&self) -> &Self::Target {
89        self.as_str()
90    }
91}
92
93impl Borrow<str> for AttributeValue<'_> {
94    fn borrow(&self) -> &str {
95        self.as_str()
96    }
97}
98
99impl Display for AttributeValue<'_> {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        self.as_str().fmt(f)
102    }
103}
104
105impl<'a> TryFrom<Cow<'a, str>> for AttributeValue<'a> {
106    type Error = InvalidAttributeValue;
107    fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
108        Self::from_cow(value)
109    }
110}
111
112impl<'a> TryFrom<&'a str> for AttributeValue<'a> {
113    type Error = InvalidAttributeValue;
114    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
115        Self::from_cow(Cow::Borrowed(value))
116    }
117}
118
119impl<'a> TryFrom<String> for AttributeValue<'a> {
120    type Error = InvalidAttributeValue;
121    fn try_from(value: String) -> Result<Self, Self::Error> {
122        Self::from_cow(Cow::Owned(value))
123    }
124}