agent_skill_installer/
env.rs1use std::collections::BTreeMap;
11use std::ffi::OsString;
12use std::path::{Path, PathBuf};
13
14#[derive(Clone, PartialEq, Eq)]
16pub struct Environment {
17 home: PathBuf,
18 vars: BTreeMap<String, OsString>,
19}
20
21impl std::fmt::Debug for Environment {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 f.debug_struct("Environment")
26 .field("home", &self.home)
27 .field("vars", &self.vars.keys().collect::<Vec<_>>())
28 .finish()
29 }
30}
31
32impl Environment {
33 #[must_use]
35 pub fn new(home: impl Into<PathBuf>) -> Self {
36 Self {
37 home: home.into(),
38 vars: BTreeMap::new(),
39 }
40 }
41
42 #[must_use]
44 pub fn with_var(mut self, name: impl Into<String>, value: impl Into<OsString>) -> Self {
45 self.vars.insert(name.into(), value.into());
46 self
47 }
48
49 #[must_use]
54 pub fn from_process() -> Option<Self> {
55 let home = std::env::var_os("HOME")
56 .or_else(|| std::env::var_os("USERPROFILE"))
57 .filter(|value| !value.is_empty())?;
58 let vars = std::env::vars_os()
59 .filter_map(|(name, value)| Some((name.into_string().ok()?, value)))
60 .collect();
61 Some(Self {
62 home: PathBuf::from(home),
63 vars,
64 })
65 }
66
67 #[must_use]
69 pub fn home(&self) -> &Path {
70 &self.home
71 }
72
73 #[must_use]
78 pub fn var(&self, name: &str) -> Option<&OsString> {
79 self.vars.get(name).filter(|value| !value.is_empty())
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
89 fn debug_shows_variable_names_not_values() {
90 let env = Environment::new("/home/x").with_var("API_TOKEN", "s3cr3t-value");
91 let shown = format!("{env:?}");
92 assert!(shown.contains("API_TOKEN"), "{shown}");
93 assert!(!shown.contains("s3cr3t-value"), "{shown}");
94 }
95}