mediatype/
name.rs

1use super::parse::*;
2use std::{
3    borrow::Cow,
4    cmp::Ordering,
5    fmt,
6    hash::{Hash, Hasher},
7};
8
9/// A media-type name.
10///
11/// A valid name has the following requirements:
12///
13/// - The first character must be an alphabet or a number.
14/// - The subsequent characters must be alphabets, numbers or `!#$&-^_.+%*'`.
15/// - Length of the name can not exceed 127 bytes.
16#[derive(Debug, Copy, Clone)]
17pub struct Name<'a>(&'a str);
18
19impl<'a> Name<'a> {
20    /// Constructs a `Name`.
21    ///
22    /// If the string is not valid as a name, returns `None`.
23    #[must_use]
24    pub fn new(s: &'a str) -> Option<Self> {
25        if is_restricted_name(s) {
26            Some(Self(s))
27        } else {
28            None
29        }
30    }
31
32    /// Returns the underlying string.
33    #[must_use]
34    pub const fn as_str(&self) -> &'a str {
35        self.0
36    }
37
38    /// The maximum byte length of a name.
39    pub const MAX_LENGTH: usize = 127;
40
41    /// Constructs a `Name` without validation.
42    pub const fn new_unchecked(s: &'a str) -> Self {
43        Self(s)
44    }
45}
46
47impl fmt::Display for Name<'_> {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.write_str(self.0)
50    }
51}
52
53impl AsRef<str> for Name<'_> {
54    fn as_ref(&self) -> &str {
55        self.0
56    }
57}
58
59impl PartialEq for Name<'_> {
60    fn eq(&self, other: &Self) -> bool {
61        self.0.eq_ignore_ascii_case(other.as_ref())
62    }
63}
64
65impl Eq for Name<'_> {}
66
67impl PartialOrd for Name<'_> {
68    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
69        Some(self.cmp(other))
70    }
71}
72
73impl Ord for Name<'_> {
74    fn cmp(&self, other: &Self) -> Ordering {
75        self.0
76            .chars()
77            .map(|c| c.to_ascii_lowercase())
78            .cmp(other.0.chars().map(|c| c.to_ascii_lowercase()))
79    }
80}
81
82impl Hash for Name<'_> {
83    fn hash<H: Hasher>(&self, state: &mut H) {
84        self.0.to_ascii_lowercase().hash(state);
85    }
86}
87
88impl PartialEq<String> for Name<'_> {
89    fn eq(&self, other: &String) -> bool {
90        self.eq(other.as_str())
91    }
92}
93
94impl PartialOrd<String> for Name<'_> {
95    fn partial_cmp(&self, other: &String) -> Option<Ordering> {
96        self.partial_cmp(other.as_str())
97    }
98}
99
100impl PartialEq<&String> for Name<'_> {
101    fn eq(&self, other: &&String) -> bool {
102        self.eq(other.as_str())
103    }
104}
105
106impl PartialOrd<&String> for Name<'_> {
107    fn partial_cmp(&self, other: &&String) -> Option<Ordering> {
108        self.partial_cmp(other.as_str())
109    }
110}
111
112impl PartialEq<str> for Name<'_> {
113    fn eq(&self, other: &str) -> bool {
114        self.0.eq_ignore_ascii_case(other)
115    }
116}
117
118impl PartialOrd<str> for Name<'_> {
119    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
120        Some(
121            self.0
122                .chars()
123                .map(|c| c.to_ascii_lowercase())
124                .cmp(other.chars().map(|c| c.to_ascii_lowercase())),
125        )
126    }
127}
128
129impl PartialEq<&str> for Name<'_> {
130    fn eq(&self, other: &&str) -> bool {
131        self.eq(*other)
132    }
133}
134
135impl PartialOrd<&str> for Name<'_> {
136    fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
137        self.partial_cmp(*other)
138    }
139}
140
141impl PartialEq<Cow<'_, str>> for Name<'_> {
142    fn eq(&self, other: &Cow<'_, str>) -> bool {
143        self.eq(other.as_ref())
144    }
145}
146
147impl PartialOrd<Cow<'_, str>> for Name<'_> {
148    fn partial_cmp(&self, other: &Cow<'_, str>) -> Option<Ordering> {
149        self.partial_cmp(other.as_ref())
150    }
151}
152
153impl PartialEq<&Cow<'_, str>> for Name<'_> {
154    fn eq(&self, other: &&Cow<'_, str>) -> bool {
155        self.eq(other.as_ref())
156    }
157}
158
159impl PartialOrd<&Cow<'_, str>> for Name<'_> {
160    fn partial_cmp(&self, other: &&Cow<'_, str>) -> Option<Ordering> {
161        self.partial_cmp(other.as_ref())
162    }
163}