Skip to main content

winer/host/
setup.rs

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    /// Returns computed debug operation.
531    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}