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}