runmat_runtime/builtins/common/
fs.rs1use std::cmp::Ordering;
8use std::env;
9use std::path::{Path, PathBuf};
10
11pub fn expand_user_path(raw: &str, error_prefix: &str) -> Result<String, String> {
15 if raw == "~" {
16 return home_directory()
17 .map(|path| path_to_string(&path))
18 .ok_or_else(|| format!("{error_prefix}: unable to resolve home directory"));
19 }
20
21 if let Some(stripped) = raw.strip_prefix("~/").or_else(|| raw.strip_prefix("~\\")) {
22 let home = home_directory()
23 .ok_or_else(|| format!("{error_prefix}: unable to resolve home directory"))?;
24 let mut buf = home;
25 if !stripped.is_empty() {
26 buf.push(stripped);
27 }
28 return Ok(path_to_string(&buf));
29 }
30
31 Ok(raw.to_string())
32}
33
34pub fn home_directory() -> Option<PathBuf> {
36 #[cfg(windows)]
37 {
38 if let Ok(user_profile) = env::var("USERPROFILE") {
39 return Some(PathBuf::from(user_profile));
40 }
41 if let (Ok(drive), Ok(path)) = (env::var("HOMEDRIVE"), env::var("HOMEPATH")) {
42 return Some(PathBuf::from(format!("{drive}{path}")));
43 }
44 None
45 }
46 #[cfg(not(windows))]
47 {
48 env::var("HOME").map(PathBuf::from).ok()
49 }
50}
51
52pub fn path_to_string(path: &Path) -> String {
54 path.to_string_lossy().into_owned()
55}
56
57pub fn contains_wildcards(text: &str) -> bool {
59 text.contains('*') || text.contains('?')
60}
61
62pub fn sort_entries(entries: &mut [String]) {
64 entries.sort_by(|a, b| compare_names(a, b));
65}
66
67pub fn compare_names(a: &str, b: &str) -> Ordering {
70 #[cfg(windows)]
71 {
72 let lower_a = a.to_ascii_lowercase();
73 let lower_b = b.to_ascii_lowercase();
74 match lower_a.cmp(&lower_b) {
75 Ordering::Equal => a.cmp(b),
76 other => other,
77 }
78 }
79 #[cfg(not(windows))]
80 {
81 a.cmp(b)
82 }
83}