Skip to main content

tear_types/
path.rs

1//! Path-string helpers shared across tear crates. Kept tiny and
2//! dependency-free so every consumer can reuse without pulling
3//! `shellexpand` or other heavy crates.
4
5/// Pure helper: expand a leading `~/` to `<home>/<rest>`. Returns
6/// the input unchanged when the string doesn't start with `~/` or
7/// when `home` is `None`. The pure shape keeps it testable without
8/// touching the process environment.
9#[must_use]
10pub fn expand_tilde_with_home(s: &str, home: Option<&str>) -> String {
11    if let Some(rest) = s.strip_prefix("~/") {
12        if let Some(h) = home {
13            return format!("{h}/{rest}");
14        }
15    }
16    s.to_string()
17}
18
19/// Convenience: expand a leading `~/` to `$HOME/`. Returns the
20/// input unchanged when the string does not start with `~/` or
21/// when `HOME` is not set in the environment. Used by every tear
22/// surface that takes an operator-supplied path string (audit log
23/// path, recording dir, MCP socket, etc.).
24#[must_use]
25pub fn expand_tilde(s: &str) -> String {
26    let home = std::env::var("HOME").ok();
27    expand_tilde_with_home(s, home.as_deref())
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn expand_tilde_with_home_set_replaces_prefix() {
36        assert_eq!(
37            expand_tilde_with_home("~/notes/x", Some("/tmp/fake-home")),
38            "/tmp/fake-home/notes/x"
39        );
40    }
41
42    #[test]
43    fn expand_tilde_passes_through_paths_without_prefix() {
44        assert_eq!(expand_tilde_with_home("/abs", Some("/h")), "/abs");
45        assert_eq!(expand_tilde_with_home("rel", Some("/h")), "rel");
46        // A bare `~` (no slash) is intentionally NOT expanded — the
47        // spec is "expand a leading `~/`".
48        assert_eq!(expand_tilde_with_home("~", Some("/h")), "~");
49    }
50
51    #[test]
52    fn expand_tilde_returns_input_when_home_missing() {
53        assert_eq!(
54            expand_tilde_with_home("~/something", None),
55            "~/something"
56        );
57    }
58
59    #[test]
60    fn expand_tilde_env_wrapper_runs_without_panicking() {
61        // We can't assert the result (depends on the runner's
62        // $HOME) but we can prove the call path itself works.
63        let _ = expand_tilde("~/x");
64        let _ = expand_tilde("/abs");
65    }
66}