vue_sfc/ast/block/
name.rs1use std::{
2 borrow::{Borrow, Cow},
3 fmt::Display,
4 ops::Deref,
5};
6
7pub use self::error::InvalidBlockName;
8use self::error::InvalidBlockNameKind;
9
10mod error {
11 use std::error::Error;
12 use std::fmt::Display;
13
14 #[derive(Debug)]
16 pub struct InvalidBlockName(pub(super) InvalidBlockNameKind);
17
18 impl Display for InvalidBlockName {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Self(InvalidBlockNameKind::StartsWithNonAsciiAlpha) => {
22 write!(f, "block name must start with ASCII alpha")
23 }
24 Self(InvalidBlockNameKind::IllegalChar(ch)) => {
25 write!(f, "block name cannot contain `{ch}`")
26 }
27 }
28 }
29 }
30
31 impl Error for InvalidBlockName {}
32
33 #[derive(Debug)]
34 pub(super) enum InvalidBlockNameKind {
35 IllegalChar(char),
36 StartsWithNonAsciiAlpha,
37 }
38}
39
40#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
42#[must_use]
43pub struct BlockName<'a>(Cow<'a, str>);
44
45impl<'a> BlockName<'a> {
46 pub fn from_cow(mut src: Cow<'a, str>) -> Result<Self, InvalidBlockName> {
59 if !src.starts_with(|ch: char| ch.is_ascii_alphabetic()) {
60 return Err(InvalidBlockName(
61 InvalidBlockNameKind::StartsWithNonAsciiAlpha,
62 ));
63 }
64
65 if let Some(ch) = src.chars().find(|ch| {
66 matches!(
67 ch,
68 '\u{0009}' | '\u{000A}' | '\u{000C}' | '\u{0020}' | '\u{002F}' | '\u{003E}'
69 )
70 }) {
71 return Err(InvalidBlockName(InvalidBlockNameKind::IllegalChar(ch)));
72 }
73
74 if src.contains(|ch: char| ch.is_ascii_uppercase()) {
75 src.to_mut().make_ascii_lowercase();
76 }
77
78 Ok(Self(src))
79 }
80
81 pub unsafe fn from_cow_unchecked(src: Cow<'a, str>) -> Self {
90 if cfg!(debug_assertions) {
91 match Self::from_cow(src) {
92 Ok(val) => val,
93 Err(err) => {
94 panic!("BlockName::from_cow_unchecked(): {err}")
95 }
96 }
97 } else {
98 Self(src)
99 }
100 }
101
102 #[must_use]
103 pub fn as_str(&self) -> &str {
104 &self.0
105 }
106}
107
108impl Display for BlockName<'_> {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 self.as_str().fmt(f)
111 }
112}
113
114impl Deref for BlockName<'_> {
115 type Target = str;
116
117 fn deref(&self) -> &Self::Target {
118 self.as_str()
119 }
120}
121
122impl Borrow<str> for BlockName<'_> {
123 fn borrow(&self) -> &str {
124 self.as_str()
125 }
126}
127
128impl<'a> TryFrom<&'a str> for BlockName<'a> {
129 type Error = InvalidBlockName;
130 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
131 Self::from_cow(Cow::Borrowed(value))
132 }
133}
134
135impl<'a> TryFrom<String> for BlockName<'a> {
136 type Error = InvalidBlockName;
137 fn try_from(value: String) -> Result<Self, Self::Error> {
138 Self::from_cow(Cow::Owned(value))
139 }
140}
141
142impl<'a> TryFrom<Cow<'a, str>> for BlockName<'a> {
143 type Error = InvalidBlockName;
144 fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
145 Self::from_cow(value)
146 }
147}