1use anyhow::Result;
8use directories::UserDirs;
9use std::path::PathBuf;
10
11#[cfg(unix)]
18fn sudo_user_home() -> Option<PathBuf> {
19 let sudo_user = std::env::var("SUDO_USER").ok()?;
20 let sudo_user = sudo_user.trim();
21 if sudo_user.is_empty() || sudo_user == "root" {
22 return None;
23 }
24 let passwd = std::fs::read_to_string("/etc/passwd").ok()?;
26 for line in passwd.lines() {
27 let mut fields = line.splitn(7, ':');
28 let username = fields.next()?;
29 if username != sudo_user {
30 continue;
31 }
32 let home = fields.nth(4)?; if !home.is_empty() {
35 return Some(PathBuf::from(home));
36 }
37 }
38 None
39}
40
41#[cfg(not(unix))]
42fn sudo_user_home() -> Option<PathBuf> {
43 None
44}
45
46pub(crate) fn effective_home_dir() -> PathBuf {
47 if let Some(home) = sudo_user_home() {
48 return home;
49 }
50 if let Ok(home) = std::env::var("HOME") {
51 let home = home.trim().to_string();
52 if !home.is_empty() {
53 return PathBuf::from(home);
54 }
55 }
56 UserDirs::new().map_or_else(|| PathBuf::from("."), |u| u.home_dir().to_path_buf())
57}
58
59pub fn tilde_expand(s: &str) -> String {
62 let home = effective_home_dir().to_string_lossy().into_owned();
63 shellexpand::tilde_with_context(s, || Some(home)).into_owned()
64}
65
66pub fn full_expand(s: &str) -> Result<String, shellexpand::LookupError<std::env::VarError>> {
68 full_expand_with_home(s, &effective_home_dir())
69}
70
71pub fn full_expand_with_home(
74 s: &str,
75 home: &std::path::Path,
76) -> Result<String, shellexpand::LookupError<std::env::VarError>> {
77 let home = home.to_string_lossy().into_owned();
78 let home2 = home.clone();
79 shellexpand::full_with_context(
80 s,
81 move || Some(home),
82 move |var| {
83 if var == "HOME" {
84 return Ok(Some(home2.clone()));
85 }
86 match std::env::var(var) {
87 Ok(v) => Ok(Some(v)),
88 Err(std::env::VarError::NotPresent) => Ok(None),
89 Err(e) => Err(e),
90 }
91 },
92 )
93 .map(|c| c.into_owned())
94}
95
96fn default_config_dir() -> Result<PathBuf> {
97 Ok(effective_home_dir().join(".shine"))
98}
99
100pub(crate) fn default_config_and_presets_dir() -> Result<(PathBuf, PathBuf)> {
101 let config_dir = default_config_dir()?;
102 Ok((config_dir.clone(), config_dir.join("presets")))
103}