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]
61 pub const fn as_str(&self) -> &'static str {
62 match self {
63 Self::User => USER,
64 Self::Machine => MACHINE,
65 }
66 }
67}
68
69impl AsRef<str> for Scope {
70 #[inline]
71 fn as_ref(&self) -> &str {
72 self.as_str()
73 }
74}
75
76impl fmt::Display for Scope {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 self.as_str().fmt(f)
79 }
80}
81
82#[derive(Error, Debug, Eq, PartialEq)]
83#[error("Scope did not match either `{USER}` or `{MACHINE}`")]
84pub struct ScopeParseError;
85
86impl FromStr for Scope {
87 type Err = ScopeParseError;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 match s {
91 USER => Ok(Self::User),
92 MACHINE => Ok(Self::Machine),
93 _ => Err(ScopeParseError),
94 }
95 }
96}