bitwarden_access_connector/
sys.rs1use std::{ffi::OsString, path::Path, sync::Arc};
4
5pub(crate) trait EnvSource: Send + Sync {
7 fn var(&self, key: &str) -> Option<OsString>;
9
10 fn vars(&self) -> Vec<(String, String)>;
13}
14
15pub(crate) struct SystemEnv;
17
18impl EnvSource for SystemEnv {
19 fn var(&self, key: &str) -> Option<OsString> {
20 std::env::var_os(key)
21 }
22
23 fn vars(&self) -> Vec<(String, String)> {
24 std::env::vars().collect()
25 }
26}
27
28#[cfg(test)]
30pub(crate) struct FakeEnv {
31 vars: std::collections::HashMap<String, String>,
32}
33
34#[cfg(test)]
35impl FakeEnv {
36 pub(crate) fn empty() -> Self {
38 Self {
39 vars: std::collections::HashMap::new(),
40 }
41 }
42
43 pub(crate) fn with(mut self, key: &str, value: &str) -> Self {
45 self.vars.insert(key.to_string(), value.to_string());
46 self
47 }
48}
49
50#[cfg(test)]
51impl<const N: usize> From<[(&str, &str); N]> for FakeEnv {
52 fn from(pairs: [(&str, &str); N]) -> Self {
53 pairs
54 .into_iter()
55 .fold(FakeEnv::empty(), |env, (k, v)| env.with(k, v))
56 }
57}
58
59#[cfg(test)]
60impl EnvSource for FakeEnv {
61 fn var(&self, key: &str) -> Option<OsString> {
62 self.vars.get(key).map(OsString::from)
63 }
64
65 fn vars(&self) -> Vec<(String, String)> {
66 self.vars
67 .iter()
68 .map(|(k, v)| (k.clone(), v.clone()))
69 .collect()
70 }
71}
72
73pub(crate) trait FileSystem: Send + Sync {
75 fn is_file(&self, path: &Path) -> bool;
77
78 fn canonicalize(&self, path: &Path) -> std::io::Result<std::path::PathBuf>;
80}
81
82pub(crate) struct SystemFs;
84
85impl FileSystem for SystemFs {
86 fn is_file(&self, path: &Path) -> bool {
87 path.is_file()
88 }
89
90 fn canonicalize(&self, path: &Path) -> std::io::Result<std::path::PathBuf> {
91 path.canonicalize()
92 }
93}
94
95#[cfg(test)]
97pub(crate) struct FakeFs {
98 files: std::collections::HashSet<std::path::PathBuf>,
99 canonical: std::collections::HashMap<std::path::PathBuf, std::path::PathBuf>,
100}
101
102#[cfg(test)]
103impl FakeFs {
104 pub(crate) fn empty() -> Self {
106 Self {
107 files: std::collections::HashSet::new(),
108 canonical: std::collections::HashMap::new(),
109 }
110 }
111
112 pub(crate) fn with_file(mut self, path: impl Into<std::path::PathBuf>) -> Self {
114 let path = path.into();
115 self.files.insert(path.clone());
116 self.canonical.insert(path.clone(), path);
117 self
118 }
119
120 pub(crate) fn with_dir(mut self, path: impl Into<std::path::PathBuf>) -> Self {
122 let path = path.into();
123 self.canonical.insert(path.clone(), path);
124 self
125 }
126
127 pub(crate) fn with_link(
129 mut self,
130 from: impl Into<std::path::PathBuf>,
131 to: impl Into<std::path::PathBuf>,
132 ) -> Self {
133 let (from, to) = (from.into(), to.into());
134 self.files.insert(from.clone());
135 self.canonical.insert(from, to);
136 self
137 }
138}
139
140#[cfg(test)]
141impl FileSystem for FakeFs {
142 fn is_file(&self, path: &Path) -> bool {
143 self.files.contains(path)
144 }
145
146 fn canonicalize(&self, path: &Path) -> std::io::Result<std::path::PathBuf> {
147 self.canonical.get(path).cloned().ok_or_else(|| {
148 std::io::Error::new(std::io::ErrorKind::NotFound, "no such path in FakeFs")
149 })
150 }
151}
152
153#[derive(Clone)]
156pub(crate) struct Platform {
157 pub(crate) env: Arc<dyn EnvSource>,
158 pub(crate) fs: Arc<dyn FileSystem>,
159}
160
161impl Platform {
162 pub(crate) fn system() -> Self {
164 Self {
165 env: Arc::new(SystemEnv),
166 fs: Arc::new(SystemFs),
167 }
168 }
169}
170
171#[cfg(test)]
172impl Platform {
173 pub(crate) fn fake(env: FakeEnv, fs: FakeFs) -> Self {
174 Self {
175 env: Arc::new(env),
176 fs: Arc::new(fs),
177 }
178 }
179
180 pub(crate) fn blank() -> Self {
182 Self::fake(FakeEnv::empty(), FakeFs::empty())
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn fake_env_returns_only_what_it_was_given() {
192 let env = FakeEnv::from([("A", "1"), ("B", "2")]);
193 assert_eq!(env.var("A"), Some(OsString::from("1")));
194 assert_eq!(env.var("MISSING"), None);
195
196 let mut names: Vec<String> = env.vars().into_iter().map(|(k, _)| k).collect();
197 names.sort();
198 assert_eq!(names, vec!["A", "B"]);
199 }
200
201 #[test]
202 fn fake_env_does_not_see_the_real_environment() {
203 assert!(SystemEnv.var("PATH").is_some());
205 assert_eq!(FakeEnv::empty().var("PATH"), None);
206 }
207}