1use std::{fmt, str::FromStr};
9
10use crate::{DEFAULT_VALUE, FieldError};
11#[cfg(doc)]
12use crate::{ENV_VAR, Rule};
13
14fn without_nul(text: String) -> Result<String, FieldError> {
16 if text.contains('\0') {
17 Err(FieldError::Nul)
18 } else {
19 Ok(text)
20 }
21}
22
23#[derive(Clone, Debug, PartialEq, Eq, Hash)]
26pub enum Exe {
27 Any,
29 Name(ExeName),
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, Hash)]
36pub struct ExeName(String);
37
38impl Exe {
39 pub const ANY: &'static str = "*";
41
42 pub fn new(text: impl Into<String>) -> Result<Self, FieldError> {
50 let text = without_nul(text.into())?;
51 match text.as_str() {
52 "" => Err(FieldError::MissingExe),
53 Self::ANY => Ok(Self::Any),
54 _ => Ok(Self::Name(ExeName(text))),
55 }
56 }
57
58 pub(crate) fn literal(text: &str) -> Self {
61 if text == Self::ANY {
62 Self::Any
63 } else {
64 Self::Name(ExeName(text.to_owned()))
65 }
66 }
67
68 #[must_use]
70 pub fn as_str(&self) -> &str {
71 match self {
72 Self::Any => Self::ANY,
73 Self::Name(name) => name.as_str(),
74 }
75 }
76
77 #[must_use]
79 pub const fn is_any(&self) -> bool {
80 matches!(self, Self::Any)
81 }
82
83 #[must_use]
85 pub fn matches(&self, basename: &str) -> bool {
86 match self {
87 Self::Any => true,
88 Self::Name(name) => basename.eq_ignore_ascii_case(name.as_str()),
89 }
90 }
91}
92
93impl ExeName {
94 #[must_use]
96 pub fn as_str(&self) -> &str {
97 &self.0
98 }
99}
100
101impl FromStr for Exe {
102 type Err = FieldError;
103
104 fn from_str(s: &str) -> Result<Self, Self::Err> {
106 Self::new(s)
107 }
108}
109
110impl fmt::Display for Exe {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 f.write_str(self.as_str())
113 }
114}
115
116impl fmt::Display for ExeName {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.write_str(self.as_str())
119 }
120}
121
122impl PartialEq<&str> for Exe {
123 fn eq(&self, other: &&str) -> bool {
124 self.as_str() == *other
125 }
126}
127
128#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
132pub enum Specificity {
133 Any,
135 AnyWithFingerprint,
137 Exe,
139 ExeWithFingerprint,
141}
142
143#[derive(Clone, Debug, PartialEq, Eq, Hash)]
147pub struct Fingerprint(String);
148
149impl Fingerprint {
150 pub fn new(text: impl Into<String>) -> Result<Self, FieldError> {
156 without_nul(text.into()).map(Self)
157 }
158
159 pub(crate) fn literal(text: &str) -> Self {
162 Self(text.to_owned())
163 }
164
165 #[must_use]
167 pub fn as_str(&self) -> &str {
168 &self.0
169 }
170
171 #[must_use]
174 pub fn matches(&self, actual: &str) -> bool {
175 actual
176 .to_ascii_lowercase()
177 .contains(&self.0.to_ascii_lowercase())
178 }
179}
180
181#[derive(Clone, Debug, PartialEq, Eq, Hash)]
183pub struct Argument(String);
184
185impl Argument {
186 pub fn new(text: impl Into<String>) -> Result<Self, FieldError> {
193 let text = without_nul(text.into())?;
194 if text.is_empty() {
195 Err(FieldError::EmptyArgument)
196 } else {
197 Ok(Self(text))
198 }
199 }
200
201 pub(crate) fn literal(text: &str) -> Self {
204 Self(text.to_owned())
205 }
206
207 #[must_use]
209 pub fn as_str(&self) -> &str {
210 &self.0
211 }
212}
213
214#[derive(Clone, Debug, PartialEq, Eq, Hash)]
222pub struct DebugChannel(String);
223
224impl DebugChannel {
225 pub fn new(text: impl Into<String>) -> Result<Self, FieldError> {
233 let text = without_nul(text.into())?;
234 if text.is_empty() {
235 Err(FieldError::EmptyDebugChannel)
236 } else if text.contains(',') {
237 Err(FieldError::CommaInDebugChannel(text))
238 } else {
239 Ok(Self(text))
240 }
241 }
242
243 #[must_use]
245 pub fn as_str(&self) -> &str {
246 &self.0
247 }
248}
249
250#[derive(Clone, Debug, PartialEq, Eq, Hash)]
256pub struct DllOverride(String);
257
258impl DllOverride {
259 pub fn new(text: impl Into<String>) -> Result<Self, FieldError> {
266 let text = without_nul(text.into())?;
267 if text.contains('=') {
268 Ok(Self(text))
269 } else {
270 Err(FieldError::InvalidDllOverride(text))
271 }
272 }
273
274 pub(crate) fn literal(text: &str) -> Self {
278 Self(text.to_owned())
279 }
280
281 #[must_use]
283 pub fn as_str(&self) -> &str {
284 &self.0
285 }
286
287 pub fn modules(&self) -> impl Iterator<Item = &str> {
289 self.names()
290 .split(',')
291 .map(str::trim)
292 .filter(|name| !name.is_empty())
293 }
294
295 #[must_use]
297 pub fn order(&self) -> &str {
298 self.0.split_once('=').map_or("", |(_, order)| order)
299 }
300
301 #[must_use]
305 pub fn is_default(&self) -> bool {
306 self.order().trim().eq_ignore_ascii_case(DEFAULT_VALUE)
307 }
308
309 fn names(&self) -> &str {
311 self.0.split_once('=').map_or("", |(names, _)| names)
312 }
313}
314
315#[derive(Clone, Debug, PartialEq, Eq, Hash)]
319pub struct EnvName(String);
320
321impl EnvName {
322 pub fn new(text: impl Into<String>) -> Result<Self, FieldError> {
329 let text = without_nul(text.into())?;
330 if text.is_empty() || text.contains('=') {
331 Err(FieldError::InvalidEnvName(text))
332 } else {
333 Ok(Self(text))
334 }
335 }
336
337 #[must_use]
339 pub fn as_str(&self) -> &str {
340 &self.0
341 }
342}
343
344#[derive(Clone, Debug, PartialEq, Eq, Hash)]
347pub struct EnvEntry {
348 name: EnvName,
350 value: Option<String>,
352}
353
354impl EnvEntry {
355 pub fn new(name: EnvName, value: impl Into<String>) -> Result<Self, FieldError> {
362 let value = without_nul(value.into())?;
363 Ok(Self {
364 name,
365 value: (!value.is_empty()).then_some(value),
366 })
367 }
368
369 #[must_use]
371 pub const fn remove(name: EnvName) -> Self {
372 Self { name, value: None }
373 }
374
375 pub(crate) fn literal(name: &str, value: &str) -> Self {
379 Self {
380 name: EnvName(name.to_owned()),
381 value: (!value.is_empty()).then(|| value.to_owned()),
382 }
383 }
384
385 #[must_use]
387 pub const fn name(&self) -> &EnvName {
388 &self.name
389 }
390
391 #[must_use]
393 pub fn value(&self) -> Option<&str> {
394 self.value.as_deref()
395 }
396}
397
398impl fmt::Display for EnvEntry {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 write!(f, "{}={}", self.name, self.value().unwrap_or_default())
402 }
403}
404
405impl FromStr for Fingerprint {
406 type Err = FieldError;
407
408 fn from_str(s: &str) -> Result<Self, Self::Err> {
410 Self::new(s)
411 }
412}
413
414impl FromStr for Argument {
415 type Err = FieldError;
416
417 fn from_str(s: &str) -> Result<Self, Self::Err> {
419 Self::new(s)
420 }
421}
422
423impl FromStr for DebugChannel {
424 type Err = FieldError;
425
426 fn from_str(s: &str) -> Result<Self, Self::Err> {
428 Self::new(s)
429 }
430}
431
432impl FromStr for DllOverride {
433 type Err = FieldError;
434
435 fn from_str(s: &str) -> Result<Self, Self::Err> {
437 Self::new(s)
438 }
439}
440
441impl FromStr for EnvName {
442 type Err = FieldError;
443
444 fn from_str(s: &str) -> Result<Self, Self::Err> {
446 Self::new(s)
447 }
448}
449
450impl fmt::Display for Fingerprint {
451 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452 f.write_str(self.as_str())
453 }
454}
455
456impl fmt::Display for Argument {
457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458 f.write_str(self.as_str())
459 }
460}
461
462impl fmt::Display for DebugChannel {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 f.write_str(self.as_str())
465 }
466}
467
468impl fmt::Display for DllOverride {
469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470 f.write_str(self.as_str())
471 }
472}
473
474impl fmt::Display for EnvName {
475 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476 f.write_str(self.as_str())
477 }
478}
479
480impl AsRef<str> for Argument {
481 fn as_ref(&self) -> &str {
482 self.as_str()
483 }
484}
485
486impl PartialEq<&str> for Fingerprint {
487 fn eq(&self, other: &&str) -> bool {
488 self.0 == *other
489 }
490}
491
492impl PartialEq<&str> for Argument {
493 fn eq(&self, other: &&str) -> bool {
494 self.0 == *other
495 }
496}
497
498impl PartialEq<&str> for DebugChannel {
499 fn eq(&self, other: &&str) -> bool {
500 self.0 == *other
501 }
502}
503
504impl PartialEq<&str> for DllOverride {
505 fn eq(&self, other: &&str) -> bool {
506 self.0 == *other
507 }
508}
509
510impl PartialEq<&str> for EnvName {
511 fn eq(&self, other: &&str) -> bool {
512 self.0 == *other
513 }
514}
515
516#[cfg(test)]
517mod tests;