Skip to main content

kernel/
fs.rs

1//! Small filesystem-path helpers shared across the workspace.
2
3use std::path::{Path, PathBuf};
4
5/// Expand a leading `~` or `~/` in `path` against `home`. A path that does not
6/// begin with a tilde segment is returned unchanged.
7pub fn expand_tilde(path: &str, home: &Path) -> PathBuf {
8    if path == "~" {
9        home.to_path_buf()
10    } else if let Some(rest) = path.strip_prefix("~/") {
11        home.join(rest)
12    } else {
13        PathBuf::from(path)
14    }
15}
16
17/// Expand a leading `~`/`~/` in `path` against `$HOME`, read from the process
18/// environment. A path without a tilde segment — or when `$HOME` is unset —
19/// passes through unchanged.
20pub fn expand_tilde_env(path: &str) -> PathBuf {
21    match std::env::var("HOME") {
22        Ok(home) => expand_tilde(path, Path::new(&home)),
23        Err(_) => PathBuf::from(path),
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn a_tilde_expands_against_home() {
33        let home = Path::new("/home/koala");
34        assert_eq!(expand_tilde("~", home), PathBuf::from("/home/koala"));
35        assert_eq!(
36            expand_tilde("~/models/a.gguf", home),
37            PathBuf::from("/home/koala/models/a.gguf")
38        );
39    }
40
41    #[test]
42    fn a_trailing_slash_on_home_is_normalized() {
43        assert_eq!(
44            expand_tilde("~/x", Path::new("/home/koala/")),
45            PathBuf::from("/home/koala/x")
46        );
47    }
48
49    #[test]
50    fn a_non_tilde_path_is_unchanged() {
51        let home = Path::new("/home/koala");
52        assert_eq!(expand_tilde("/abs/path", home), PathBuf::from("/abs/path"));
53        assert_eq!(expand_tilde("relative", home), PathBuf::from("relative"));
54        // A tilde not at the start is not a home reference.
55        assert_eq!(expand_tilde("a/~/b", home), PathBuf::from("a/~/b"));
56    }
57}