winget_types/installer/
scope.rs1use core::{fmt, str::FromStr};
2
3use thiserror::Error;
4
5use crate::utils::RelativeDir;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
10pub enum Scope {
11 User,
12 Machine,
13}
14
15const USER: &str = "user";
16const MACHINE: &str = "machine";
17
18impl Scope {
19 #[must_use]
20 pub fn find_in<T: AsRef<[u8]>>(value: T) -> Option<Self> {
21 let bytes = value.as_ref();
22
23 let value_contains = |scope: Self| -> Option<Self> {
24 bytes
25 .windows(scope.as_str().len())
26 .any(|window| window.eq_ignore_ascii_case(scope.as_str().as_bytes()))
27 .then_some(scope)
28 };
29
30 value_contains(Self::User).or_else(|| value_contains(Self::Machine))
31 }
32
33 #[must_use]
34 pub fn from_install_directory<T: AsRef<str>>(install_directory: T) -> Option<Self> {
35 const USER_INSTALL_DIRS: [&str; 2] = [RelativeDir::APP_DATA, RelativeDir::LOCAL_APP_DATA];
36 const MACHINE_INSTALL_DIRS: [&str; 7] = [
37 RelativeDir::PROGRAM_FILES_64,
38 RelativeDir::PROGRAM_FILES_32,
39 RelativeDir::COMMON_FILES_64,
40 RelativeDir::COMMON_FILES_32,
41 RelativeDir::PROGRAM_DATA,
42 RelativeDir::WINDOWS_DIR,
43 RelativeDir::SYSTEM_ROOT,
44 ];
45
46 let install_directory = install_directory.as_ref();
47
48 USER_INSTALL_DIRS
49 .iter()
50 .any(|directory| install_directory.starts_with(directory))
51 .then_some(Self::User)
52 .or_else(|| {
53 MACHINE_INSTALL_DIRS
54 .iter()
55 .any(|directory| install_directory.starts_with(directory))
56 .then_some(Self::Machine)
57 })
58 }
59
60 #[must_use]
62 #[inline]
63 pub const fn is_user(&self) -> bool {
64 matches!(self, Self::User)
65 }
66
67 #[must_use]
69 #[inline]
70 pub const fn is_machine(&self) -> bool {
71 matches!(self, Self::Machine)
72 }
73
74 #[must_use]
75 pub const fn as_str(&self) -> &'static str {
76 match self {
77 Self::User => USER,
78 Self::Machine => MACHINE,
79 }
80 }
81}
82
83impl AsRef<str> for Scope {
84 #[inline]
85 fn as_ref(&self) -> &str {
86 self.as_str()
87 }
88}
89
90impl fmt::Display for Scope {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 self.as_str().fmt(f)
93 }
94}
95
96#[derive(Error, Debug, Eq, PartialEq)]
97#[error("Scope did not match either `{USER}` or `{MACHINE}`")]
98pub struct ScopeParseError;
99
100impl FromStr for Scope {
101 type Err = ScopeParseError;
102
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 match s {
105 USER => Ok(Self::User),
106 MACHINE => Ok(Self::Machine),
107 _ => Err(ScopeParseError),
108 }
109 }
110}