1use bitflags::bitflags;
2use core::convert::Infallible;
3use core::fmt;
4use std::str::FromStr;
5use std::{ffi::OsStr, path::PathBuf};
6
7use typed_path::WindowsPathBuf;
8
9use super::convert::{ArchError, DllOverrideError, LogError};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13
14#[allow(unused_macros)]
15macro_rules! str_serde {
16 ($ident:ident) => {
17 #[cfg(feature = "serde")]
18 impl Serialize for $ident {
19 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
20 where
21 S: Serializer,
22 {
23 serializer.serialize_str(&self.to_string())
24 }
25 }
26
27 #[cfg(feature = "serde")]
28 impl<'de> Deserialize<'de> for $ident {
29 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
30 where
31 D: Deserializer<'de>,
32 {
33 let s = String::deserialize(deserializer)?;
34 s.parse().map_err(serde::de::Error::custom)
35 }
36 }
37 };
38}
39
40#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41#[cfg_attr(feature = "serde", serde(transparent))]
42#[derive(Debug, Clone)]
43pub struct Paths {
44 #[cfg_attr(feature = "serde", serde(with = "crate::serde::windows_pathbuf_vec"))]
45 inner: Vec<WindowsPathBuf>,
46}
47
48impl Paths {
49 const SEPARATOR: char = ';';
50}
51
52impl fmt::Display for Paths {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 self.inner.iter().enumerate().try_for_each(|(i, path)| {
55 if i != self.inner.len() - 1 {
56 write!(f, "{}{}", path.display(), Self::SEPARATOR)
57 } else {
58 write!(f, "{}", path.display())
59 }
60 })
61 }
62}
63
64impl FromStr for Paths {
65 type Err = Infallible;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 Ok(s.split(Self::SEPARATOR)
69 .map(WindowsPathBuf::from)
70 .collect::<Vec<_>>()
71 .into())
72 }
73}
74
75impl From<Vec<WindowsPathBuf>> for Paths {
76 fn from(inner: Vec<WindowsPathBuf>) -> Self {
77 Self { inner }
78 }
79}
80
81#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
82#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
83#[derive(Debug, Clone, Copy)]
84pub enum Arch {
85 Win32,
86 Win64,
87 Wow64,
88}
89
90impl fmt::Display for Arch {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 Self::Win32 => write!(f, "win32"),
94 Self::Win64 => write!(f, "win64"),
95 Self::Wow64 => write!(f, "wow64"),
96 }
97 }
98}
99
100impl FromStr for Arch {
101 type Err = ArchError;
102
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 match s {
105 "win32" => Ok(Self::Win32),
106 "win64" => Ok(Self::Win64),
107 "wow64" => Ok(Self::Wow64),
108 _ => Err(ArchError),
109 }
110 }
111}
112
113#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
114#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
115#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
116pub enum DllLoadOrder {
117 Disabled,
118 BuiltinOnly,
119 NativeOnly,
120 #[default]
121 BuiltinNative,
122 NativeBuiltin,
123}
124
125impl DllLoadOrder {
126 #[inline]
127 pub fn is_disabled(&self) -> bool {
128 *self == Self::Disabled
129 }
130
131 #[inline]
132 pub fn has_native(&self) -> bool {
133 matches!(
134 self,
135 Self::NativeOnly | Self::BuiltinNative | Self::NativeBuiltin
136 )
137 }
138
139 #[inline]
140 pub fn prefers_native(&self) -> bool {
141 matches!(self, Self::NativeOnly | Self::NativeBuiltin)
142 }
143
144 #[inline]
145 pub fn is_native_only(&self) -> bool {
146 matches!(self, Self::NativeOnly)
147 }
148
149 #[inline]
150 pub fn has_builtin(&self) -> bool {
151 matches!(
152 self,
153 Self::BuiltinOnly | Self::BuiltinNative | Self::NativeBuiltin
154 )
155 }
156
157 #[inline]
158 pub fn prefers_builtin(&self) -> bool {
159 matches!(self, Self::BuiltinOnly | Self::BuiltinNative)
160 }
161
162 #[inline]
163 pub fn is_builtin_only(&self) -> bool {
164 matches!(self, Self::BuiltinOnly)
165 }
166}
167
168impl fmt::Display for DllLoadOrder {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::Disabled => write!(f, ""),
172 Self::BuiltinOnly => write!(f, "b"),
173 Self::NativeOnly => write!(f, "n"),
174 Self::BuiltinNative => write!(f, "b,n"),
175 Self::NativeBuiltin => write!(f, "n,b"),
176 }
177 }
178}
179
180impl<T> From<T> for DllLoadOrder
181where
182 T: AsRef<str>,
183{
184 fn from(value: T) -> Self {
185 let s = value.as_ref();
186 let mut state = Self::Disabled;
187
188 for order in s.split(DllOverride::ITEM_SEPARATORS) {
189 state = match order.chars().next() {
190 Some('N') | Some('n') => match state {
191 Self::Disabled => Self::NativeOnly,
192 Self::BuiltinOnly => Self::BuiltinNative,
193 _ => state,
194 },
195 Some('B') | Some('b') => match state {
196 Self::Disabled => Self::BuiltinOnly,
197 Self::NativeOnly => Self::NativeBuiltin,
198 _ => state,
199 },
200 _ => state,
201 };
202
203 if matches!(state, Self::BuiltinNative | Self::NativeBuiltin) {
204 return state;
205 }
206 }
207
208 state
209 }
210}
211
212impl FromStr for DllLoadOrder {
213 type Err = DllOverrideError;
214
215 fn from_str(s: &str) -> Result<Self, Self::Err> {
216 let mut state = Self::Disabled;
217
218 for order in s.split(DllOverride::ITEM_SEPARATORS) {
219 state = match order.chars().next() {
220 Some('N') | Some('n') => match state {
221 Self::Disabled => Self::NativeOnly,
222 Self::BuiltinOnly => Self::BuiltinNative,
223 _ => state,
224 },
225 Some('B') | Some('b') => match state {
226 Self::Disabled => Self::BuiltinOnly,
227 Self::NativeOnly => Self::NativeBuiltin,
228 _ => state,
229 },
230 Some(c) => return Err(DllOverrideError::InvalidLoadOrder { state, char: c }),
231 _ => state,
232 };
233
234 if matches!(state, Self::BuiltinNative | Self::NativeBuiltin) {
235 return Ok(state);
236 }
237 }
238
239 Ok(state)
240 }
241}
242
243#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
244#[derive(Debug, Clone)]
245pub struct DllOverride {
246 modules: Vec<PathBuf>,
247 load_order: DllLoadOrder,
248}
249
250impl DllOverride {
251 const ITEM_SEPARATORS: [char; 2] = [',', '\t'];
252 const KV_SEPARATOR: char = '=';
253
254 pub fn modules(&self) -> &[PathBuf] {
255 self.modules.as_slice()
256 }
257
258 pub fn load_order(&self) -> DllLoadOrder {
259 self.load_order
260 }
261}
262
263impl FromStr for DllOverride {
264 type Err = DllOverrideError;
265
266 fn from_str(s: &str) -> Result<Self, Self::Err> {
267 if let Some((modules, load_order)) = s.split_once(DllOverride::KV_SEPARATOR) {
268 let load_order: DllLoadOrder = load_order.parse()?;
269 let modules = modules
270 .split(DllOverride::ITEM_SEPARATORS)
271 .map(PathBuf::from)
272 .collect::<Vec<_>>();
273 Ok(Self {
274 modules,
275 load_order,
276 })
277 } else {
278 Err(DllOverrideError::InvalldOverride)
279 }
280 }
281}
282
283impl fmt::Display for DllOverride {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 write!(
286 f,
287 "{}={}",
288 self.modules
289 .iter()
290 .map(|m| m.display().to_string())
291 .collect::<Vec<_>>()
292 .join(","),
293 self.load_order
294 )
295 }
296}
297
298#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
299#[cfg_attr(feature = "serde", serde(transparent))]
300#[derive(Debug, Clone)]
301pub struct DllOverrides {
302 inner: Vec<DllOverride>,
303}
304
305impl DllOverrides {
306 const SEPARATOR: char = ';';
307
308 pub fn inner(&self) -> &[DllOverride] {
309 &self.inner
310 }
311
312 pub fn into_inner(self) -> Vec<DllOverride> {
313 self.inner
314 }
315}
316
317impl FromStr for DllOverrides {
318 type Err = DllOverrideError;
319
320 fn from_str(s: &str) -> Result<Self, Self::Err> {
321 let overrides = s
322 .split(Self::SEPARATOR)
323 .map(|o| o.parse::<DllOverride>())
324 .collect::<Result<Vec<_>, _>>()?;
325 Ok(Self { inner: overrides })
326 }
327}
328
329impl fmt::Display for DllOverrides {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 self.inner
332 .iter()
333 .enumerate()
334 .try_for_each(|(i, dll_override)| {
335 if i != self.inner.len() - 1 {
336 write!(f, "{}{}", dll_override, Self::SEPARATOR)
337 } else {
338 write!(f, "{}", dll_override)
339 }
340 })
341 }
342}
343
344#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
346pub enum LogClass {
347 FixMe,
348 Error,
349 Warning,
350 Trace,
351}
352
353impl FromStr for LogClass {
354 type Err = LogError;
355
356 fn from_str(s: &str) -> Result<Self, Self::Err> {
357 match s {
358 "fixme" => Ok(Self::FixMe),
359 "err" => Ok(Self::Error),
360 "warn" => Ok(Self::Warning),
361 "trace" => Ok(Self::Trace),
362 _ => Err(LogError::InvalidClass),
363 }
364 }
365}
366
367impl fmt::Display for LogClass {
368 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369 match self {
370 Self::FixMe => write!(f, "fixme"),
371 Self::Error => write!(f, "err"),
372 Self::Warning => write!(f, "warn"),
373 Self::Trace => write!(f, "trace"),
374 }
375 }
376}
377
378bitflags! {
379 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
380 pub struct LogClassSet: u8 {
381 const FIXME = 0b00000001;
382 const ERROR = 0b00000010;
383 const WARNING = 0b00000100;
384 const TRACE = 0b00001000;
385 }
386}
387
388impl From<LogClass> for LogClassSet {
389 fn from(class: LogClass) -> Self {
390 match class {
391 LogClass::FixMe => Self::FIXME,
392 LogClass::Error => Self::ERROR,
393 LogClass::Warning => Self::WARNING,
394 LogClass::Trace => Self::TRACE,
395 }
396 }
397}
398
399#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
400#[cfg_attr(feature = "serde", serde(untagged))]
401#[derive(Debug, Clone, Eq)]
402pub enum LogChannel {
403 All,
404 Specific(String),
405}
406
407impl LogChannel {
408 pub fn is_all(&self) -> bool {
409 match self {
410 Self::All => true,
411 Self::Specific(s) if s == "all" => true,
412 _ => false,
413 }
414 }
415
416 pub fn is_specific(&self) -> bool {
417 !self.is_all()
418 }
419
420 pub fn specific(&self) -> Option<&str> {
421 match self {
422 Self::All => None,
423 Self::Specific(s) => {
424 if s == "all" {
425 None
426 } else {
427 Some(s)
428 }
429 }
430 }
431 }
432}
433
434impl From<&str> for LogChannel {
435 fn from(value: &str) -> Self {
436 if value == "all" {
437 Self::All
438 } else {
439 Self::Specific(value.to_string())
440 }
441 }
442}
443
444impl FromStr for LogChannel {
445 type Err = Infallible;
446
447 fn from_str(s: &str) -> Result<Self, Self::Err> {
448 Ok(Self::from(s))
449 }
450}
451
452impl fmt::Display for LogChannel {
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 match self {
455 Self::All => write!(f, "all"),
456 Self::Specific(s) => write!(f, "{}", s),
457 }
458 }
459}
460
461impl PartialEq for LogChannel {
462 fn eq(&self, other: &Self) -> bool {
463 self.specific() == other.specific()
464 }
465}
466
467impl PartialEq<&LogChannel> for LogChannel {
468 fn eq(&self, other: &&LogChannel) -> bool {
469 self.eq(*other)
470 }
471}
472
473impl PartialEq<LogChannel> for &LogChannel {
474 fn eq(&self, other: &LogChannel) -> bool {
475 (*self).eq(other)
476 }
477}
478
479#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
480#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
481#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
482pub enum LogOperation {
483 #[default]
484 Set,
485 Clear,
486}
487
488impl fmt::Display for LogOperation {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 match self {
491 Self::Set => write!(f, "+"),
492 Self::Clear => write!(f, "-"),
493 }
494 }
495}
496
497impl TryFrom<char> for LogOperation {
498 type Error = LogError;
499
500 fn try_from(value: char) -> Result<Self, Self::Error> {
501 match value {
502 '+' => Ok(Self::Set),
503 '-' => Ok(Self::Clear),
504 _ => Err(LogError::InvalidOperation),
505 }
506 }
507}
508
509impl FromStr for LogOperation {
510 type Err = LogError;
511
512 fn from_str(s: &str) -> Result<Self, Self::Err> {
513 if s.len() == 1 {
514 Self::try_from(s.chars().next().ok_or(LogError::InvalidOperation)?)
515 } else {
516 Err(LogError::InvalidOperation)
517 }
518 }
519}
520
521#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
522#[derive(Debug, Clone, PartialEq, Eq)]
523pub struct LogOption {
524 operation: Option<LogOperation>,
525 class: Option<LogClass>,
526 channel: LogChannel,
527}
528
529impl LogOption {
530 pub fn operation(&self) -> LogOperation {
532 self.operation.unwrap_or_default()
533 }
534
535 pub fn operation_real(&self) -> Option<LogOperation> {
536 self.operation
537 }
538
539 pub fn class(&self) -> Option<LogClass> {
540 self.class
541 }
542
543 pub fn classes(&self) -> LogClassSet {
544 if let Some(class) = self.class {
545 class.into()
546 } else {
547 LogClassSet::all()
548 }
549 }
550
551 pub fn channel(&self) -> &LogChannel {
552 &self.channel
553 }
554}
555
556impl fmt::Display for LogOption {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 match (self.class, self.operation) {
559 (None, None) => {
560 write!(f, "{}", self.channel)
561 }
562 (None, Some(operation)) => {
563 write!(f, "{}{}", operation, self.channel)
564 }
565 (Some(class), _) => {
566 write!(f, "{}{}{}", class, self.operation(), self.channel)
567 }
568 }
569 }
570}
571
572impl FromStr for LogOption {
573 type Err = LogError;
574
575 fn from_str(s: &str) -> Result<Self, Self::Err> {
576 if let Some(op) = s.chars().find(|&c| c == '+' || c == '-') {
577 let operation = LogOperation::try_from(op)?;
578 let (class, channel) = s.split_once(['+', '-']).unwrap();
579 Ok(Self {
580 operation: Some(operation),
581 class: if class.is_empty() {
582 None
583 } else {
584 Some(class.parse()?)
585 },
586 channel: LogChannel::from(channel),
587 })
588 } else {
589 Ok(Self {
590 operation: None,
591 class: None,
592 channel: LogChannel::from(s),
593 })
594 }
595 }
596}
597
598#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
599#[cfg_attr(feature = "serde", serde(transparent))]
600#[derive(Debug, Clone)]
601pub struct LogOptions {
602 inner: Vec<LogOption>,
603}
604
605impl LogOptions {
606 pub fn inner(&self) -> &[LogOption] {
607 &self.inner
608 }
609}
610
611impl fmt::Display for LogOptions {
612 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
613 self.inner.iter().enumerate().try_for_each(|(i, option)| {
614 if i != self.inner.len() - 1 {
615 write!(f, "{},", option)
616 } else {
617 write!(f, "{}", option)
618 }
619 })
620 }
621}
622
623impl FromStr for LogOptions {
624 type Err = Infallible;
625
626 fn from_str(s: &str) -> Result<Self, Self::Err> {
627 let options = s
628 .split(',')
629 .filter_map(|option| option.parse::<LogOption>().ok())
630 .collect::<Vec<_>>();
631 Ok(Self::from(options))
632 }
633}
634
635impl FromIterator<LogOption> for LogOptions {
636 fn from_iter<T: IntoIterator<Item = LogOption>>(iter: T) -> Self {
637 Self::from(Vec::from_iter(iter))
638 }
639}
640
641impl From<Vec<LogOption>> for LogOptions {
642 fn from(options: Vec<LogOption>) -> Self {
643 Self { inner: options }
644 }
645}
646
647#[cfg(test)]
648mod tests {
649 use super::*;
650
651 #[test]
652 fn log_options_simple() {
653 let fixme_clear_all = "fixme-all".parse::<LogOption>().unwrap();
654 assert_eq!(fixme_clear_all.channel(), LogChannel::All);
655 assert_eq!(fixme_clear_all.operation(), LogOperation::Clear);
656 assert_eq!(fixme_clear_all.classes(), LogClassSet::FIXME);
657 let warn_set_ntdll = "warn+ntdll".parse::<LogOption>().unwrap();
658 assert_eq!(
659 warn_set_ntdll.channel(),
660 LogChannel::Specific("ntdll".to_string())
661 );
662 assert_eq!(warn_set_ntdll.operation(), LogOperation::Set);
663 assert_eq!(warn_set_ntdll.classes(), LogClassSet::WARNING);
664 let no_class_set = "+ntdll".parse::<LogOption>().unwrap();
665 assert_eq!(
666 no_class_set.channel(),
667 LogChannel::Specific("ntdll".to_string())
668 );
669 assert_eq!(no_class_set.operation(), LogOperation::Set);
670 assert_eq!(no_class_set.classes(), LogClassSet::all());
671 let no_class_clear = "-ntdll".parse::<LogOption>().unwrap();
672 assert_eq!(
673 no_class_clear.channel(),
674 LogChannel::Specific("ntdll".to_string())
675 );
676 assert_eq!(no_class_clear.operation(), LogOperation::Clear);
677 assert_eq!(no_class_clear.classes(), LogClassSet::all());
678 let channel_only = "ntdll".parse::<LogOption>().unwrap();
679 assert_eq!(
680 channel_only.channel(),
681 LogChannel::Specific("ntdll".to_string())
682 );
683 assert_eq!(channel_only.operation(), LogOperation::Set);
684 assert_eq!(channel_only.classes(), LogClassSet::all());
685 }
686
687 #[test]
688 fn log_options_reflexive() {
689 let testcases = ["fixme-all", "warn+ntdll", "+ntdll", "-ntdll", "ntdll"];
690 for testcase in testcases {
691 let option = testcase.parse::<LogOption>().unwrap();
692 assert_eq!(format!("{}", option), testcase);
693 }
694 }
695
696 #[cfg(feature = "serde")]
697 #[test]
698 fn log_options_serde() {
699 use serde_test::{Token, assert_tokens};
700 let testcases = ["fixme-all", "warn+ntdll", "+ntdll", "-ntdll", "ntdll"];
701 for testcase in testcases {
702 let option = testcase.parse::<LogOption>().unwrap();
703 assert_tokens(&option, &[Token::String(testcase)]);
704 }
705 }
706}