1#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
7pub struct ExtensionId(String);
8
9impl ExtensionId {
10 #[must_use]
12 pub fn as_str(&self) -> &str {
13 &self.0
14 }
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ExtensionIdError(String);
23
24impl std::fmt::Display for ExtensionIdError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 self.0.fmt(f)
27 }
28}
29
30impl std::error::Error for ExtensionIdError {}
31
32impl TryFrom<String> for ExtensionId {
33 type Error = ExtensionIdError;
34 fn try_from(s: String) -> Result<Self, Self::Error> {
35 let trimmed = s.trim();
36 if trimmed.is_empty() {
37 return Err(ExtensionIdError(
38 "extension id must not be empty or whitespace".into(),
39 ));
40 }
41 Ok(Self(trimmed.to_owned()))
42 }
43}
44
45impl std::fmt::Display for ExtensionId {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 self.0.fmt(f)
48 }
49}
50
51impl AsRef<str> for ExtensionId {
52 fn as_ref(&self) -> &str {
53 &self.0
54 }
55}
56
57impl PartialEq<str> for ExtensionId {
58 fn eq(&self, other: &str) -> bool {
59 self.0 == other
60 }
61}
62
63impl PartialEq<&str> for ExtensionId {
64 fn eq(&self, other: &&str) -> bool {
65 self.0 == *other
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize)]
71pub struct ArgName(String);
72
73impl ArgName {
74 #[must_use]
78 pub fn new(name: impl Into<String>) -> Option<Self> {
79 Self::try_from(name.into()).ok()
80 }
81
82 #[must_use]
84 pub fn as_str(&self) -> &str {
85 &self.0
86 }
87}
88
89impl TryFrom<String> for ArgName {
90 type Error = ExtensionIdError;
91 fn try_from(s: String) -> Result<Self, Self::Error> {
92 let trimmed = s.trim();
93 if trimmed.is_empty() {
94 return Err(ExtensionIdError(
95 "arg name must not be empty or whitespace".into(),
96 ));
97 }
98 Ok(Self(trimmed.to_owned()))
99 }
100}
101
102impl std::fmt::Display for ArgName {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 self.0.fmt(f)
105 }
106}
107
108impl AsRef<str> for ArgName {
109 fn as_ref(&self) -> &str {
110 &self.0
111 }
112}
113
114impl std::borrow::Borrow<str> for ArgName {
115 fn borrow(&self) -> &str {
116 &self.0
117 }
118}
119
120impl PartialEq<str> for ArgName {
121 fn eq(&self, other: &str) -> bool {
122 self.0 == other
123 }
124}
125
126impl PartialEq<&str> for ArgName {
127 fn eq(&self, other: &&str) -> bool {
128 self.0 == *other
129 }
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize)]
136pub struct BinaryName(String);
137
138impl BinaryName {
139 #[must_use]
141 pub fn as_str(&self) -> &str {
142 &self.0
143 }
144}
145
146impl std::fmt::Display for BinaryName {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 self.0.fmt(f)
149 }
150}
151
152impl AsRef<str> for BinaryName {
153 fn as_ref(&self) -> &str {
154 &self.0
155 }
156}
157
158impl std::borrow::Borrow<str> for BinaryName {
159 fn borrow(&self) -> &str {
160 &self.0
161 }
162}
163
164impl PartialEq<str> for BinaryName {
165 fn eq(&self, other: &str) -> bool {
166 self.0 == other
167 }
168}
169
170impl PartialEq<&str> for BinaryName {
171 fn eq(&self, other: &&str) -> bool {
172 self.0 == *other
173 }
174}
175
176impl TryFrom<String> for BinaryName {
177 type Error = ExtensionIdError;
178 fn try_from(s: String) -> Result<Self, Self::Error> {
179 let trimmed = s.trim();
180 if trimmed.is_empty() {
181 return Err(ExtensionIdError(
182 "binary name must not be empty or whitespace".into(),
183 ));
184 }
185 if trimmed.contains('/') || trimmed.contains('\\') {
186 return Err(ExtensionIdError(format!(
187 "'{trimmed}' looks like a path; use bare binary name"
188 )));
189 }
190 Ok(Self(trimmed.to_owned()))
191 }
192}
193
194impl<'de> serde::Deserialize<'de> for BinaryName {
195 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
196 let s = String::deserialize(d)?;
197 Self::try_from(s).map_err(serde::de::Error::custom)
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq, Hash)]
203pub struct MatchToken(String);
204
205impl MatchToken {
206 #[must_use]
208 pub fn as_str(&self) -> &str {
209 &self.0
210 }
211}
212
213impl std::fmt::Display for MatchToken {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 self.0.fmt(f)
216 }
217}
218
219impl AsRef<str> for MatchToken {
220 fn as_ref(&self) -> &str {
221 &self.0
222 }
223}
224
225impl TryFrom<String> for MatchToken {
226 type Error = ExtensionIdError;
227 fn try_from(s: String) -> Result<Self, Self::Error> {
228 let trimmed = s.trim();
229 if trimmed.is_empty() {
230 return Err(ExtensionIdError(
231 "match token must not be empty or whitespace".into(),
232 ));
233 }
234 Ok(Self(trimmed.to_owned()))
235 }
236}
237
238impl<'de> serde::Deserialize<'de> for MatchToken {
239 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
240 let s = String::deserialize(d)?;
241 Self::try_from(s).map_err(serde::de::Error::custom)
242 }
243}
244
245impl<'de> serde::Deserialize<'de> for ExtensionId {
246 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
247 let s = String::deserialize(d)?;
248 Self::try_from(s).map_err(serde::de::Error::custom)
249 }
250}