1use thiserror::Error;
2
3#[derive(Clone, Debug, Eq, Hash, PartialEq)]
5pub struct TagName(String);
6
7impl TagName {
8 pub fn new(s: String) -> Option<Self> {
10 if is_tag_name(&s) {
11 Some(TagName(s))
12 } else {
13 None
14 }
15 }
16
17 pub fn into_string(self) -> String {
19 self.0
20 }
21}
22
23impl std::fmt::Display for TagName {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}", self.0)
26 }
27}
28
29pub fn is_tag_name<T: AsRef<str>>(s: T) -> bool {
31 let s = s.as_ref();
32 if s.is_empty() {
33 false
34 } else {
35 let chars = s.chars().enumerate();
36 let mut is_tag_name = true;
37 for (index, c) in chars {
38 if index == 0 {
39 if !c.is_ascii_lowercase() {
40 is_tag_name = false;
41 break;
42 }
43 } else if !(c.is_ascii_alphanumeric() || c == '_') {
44 is_tag_name = false;
45 break;
46 };
47 }
48 is_tag_name
49 }
50}
51
52impl std::str::FromStr for TagName {
53 type Err = ParseTagNameError;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 if is_tag_name(s) {
57 Ok(TagName(s.to_owned()))
58 } else {
59 let unparsable_tag_name = s.to_owned();
60 Err(ParseTagNameError {
61 unparsable_tag_name,
62 })
63 }
64 }
65}
66
67impl std::convert::AsRef<str> for TagName {
68 fn as_ref(&self) -> &str {
69 &self.0
70 }
71}
72
73impl std::convert::AsRef<[u8]> for TagName {
74 fn as_ref(&self) -> &[u8] {
75 self.0.as_ref()
76 }
77}
78
79#[derive(Clone, Debug, Eq, Error, PartialEq)]
81#[error("Could not parse a tag name from the string {unparsable_tag_name}")]
82pub struct ParseTagNameError {
83 unparsable_tag_name: String,
84}
85
86#[cfg(test)]
87mod tests {
88 #[test]
89 fn is_tag_name() {
90 use super::is_tag_name;
91 assert_eq!(is_tag_name("siteRef"), true);
92 assert_eq!(is_tag_name("s"), true);
93 assert_eq!(is_tag_name("s1"), true);
94 assert_eq!(is_tag_name(""), false);
95 assert_eq!(is_tag_name("1s"), false);
96 assert_eq!(is_tag_name("s%"), false);
97 assert_eq!(is_tag_name("s-"), false);
98 assert_eq!(is_tag_name("s_s"), true);
99 assert_eq!(is_tag_name("_s"), false);
100 assert_eq!(is_tag_name("s_"), true);
101 }
102}