1use crate::parser::errors::ParserError;
2use std::str::FromStr;
3use tinystr::TinyStr8;
4
5#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Copy)]
6pub struct Variant(TinyStr8);
7
8impl Variant {
9 pub fn from_bytes(v: &[u8]) -> Result<Self, ParserError> {
10 let slen = v.len();
11
12 if !(4..=8).contains(&slen) {
13 return Err(ParserError::InvalidSubtag);
14 }
15
16 let s = TinyStr8::from_bytes(v).map_err(|_| ParserError::InvalidSubtag)?;
17
18 if (slen >= 5 && !s.is_ascii_alphanumeric())
19 || (slen == 4
20 && !v[0].is_ascii_digit()
21 && v[1..].iter().any(|c: &u8| !c.is_ascii_alphanumeric()))
22 {
23 return Err(ParserError::InvalidSubtag);
24 }
25
26 Ok(Self(s.to_ascii_lowercase()))
27 }
28
29 pub fn as_str(&self) -> &str {
30 self.0.as_str()
31 }
32
33 /// # Safety
34 ///
35 /// This function accepts any u64 that is exected to be a valid
36 /// `TinyStr8` and a valid `Variant` subtag.
37 pub const unsafe fn from_raw_unchecked(v: u64) -> Self {
38 Self(TinyStr8::from_bytes_unchecked(v.to_le_bytes()))
39 }
40}
41
42impl From<Variant> for u64 {
43 fn from(input: Variant) -> Self {
44 u64::from_le_bytes(*input.0.all_bytes())
45 }
46}
47
48impl From<&Variant> for u64 {
49 fn from(input: &Variant) -> Self {
50 u64::from_le_bytes(*input.0.all_bytes())
51 }
52}
53
54impl FromStr for Variant {
55 type Err = ParserError;
56
57 fn from_str(source: &str) -> Result<Self, Self::Err> {
58 Self::from_bytes(source.as_bytes())
59 }
60}
61
62impl std::fmt::Display for Variant {
63 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
64 f.write_str(&self.0)
65 }
66}
67
68impl PartialEq<&str> for Variant {
69 fn eq(&self, other: &&str) -> bool {
70 self.as_str() == *other
71 }
72}
73
74impl PartialEq<str> for Variant {
75 fn eq(&self, other: &str) -> bool {
76 self.as_str() == other
77 }
78}
79