Skip to main content

double_o/
session.rs

1pub fn session_id() -> String {
2    let ppid = unsafe { libc::getppid() };
3    ppid.to_string()
4}
5
6pub fn project_id() -> String {
7    #[cfg(feature = "vipune-store")]
8    {
9        vipune::detect_project(None)
10    }
11    #[cfg(not(feature = "vipune-store"))]
12    {
13        detect_project_fallback()
14    }
15}
16
17#[cfg(not(feature = "vipune-store"))]
18fn detect_project_fallback() -> String {
19    // Try git remote origin
20    if let Ok(output) = std::process::Command::new("git")
21        .args(["remote", "get-url", "origin"])
22        .output()
23    {
24        if output.status.success() {
25            let url = String::from_utf8_lossy(&output.stdout).trim().to_string();
26            if let Some(name) = url.rsplit('/').next() {
27                let name = name.strip_suffix(".git").unwrap_or(name);
28                if !name.is_empty() {
29                    return name.to_string();
30                }
31            }
32        }
33    }
34
35    // Try git root directory name
36    if let Ok(output) = std::process::Command::new("git")
37        .args(["rev-parse", "--show-toplevel"])
38        .output()
39    {
40        if output.status.success() {
41            let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
42            if let Some(name) = path.rsplit('/').next() {
43                if !name.is_empty() {
44                    return name.to_string();
45                }
46            }
47        }
48    }
49
50    // Current directory name
51    if let Ok(cwd) = std::env::current_dir() {
52        if let Some(name) = cwd.file_name() {
53            return name.to_string_lossy().to_string();
54        }
55    }
56
57    "unknown".to_string()
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_session_id_is_nonzero() {
66        let id = session_id();
67        let pid: u32 = id.parse().expect("session_id should be numeric");
68        assert!(pid > 0);
69    }
70
71    #[test]
72    fn test_session_id_stable() {
73        assert_eq!(session_id(), session_id());
74    }
75
76    #[test]
77    fn test_project_id_nonempty() {
78        let id = project_id();
79        assert!(!id.is_empty());
80    }
81
82    // --- detect_project_fallback branch tests ---
83    // We test the function indirectly through project_id(), which always
84    // delegates to detect_project_fallback() in the non-vipune build.
85
86    #[test]
87    fn test_project_id_is_string() {
88        // project_id must return a valid (non-empty) UTF-8 string
89        let id = project_id();
90        assert!(!id.is_empty(), "project_id must not be empty");
91        assert!(
92            id.is_ascii() || !id.is_empty(),
93            "project_id must be a string"
94        );
95    }
96
97    #[test]
98    fn test_project_id_no_newlines() {
99        // The project identifier must not contain newlines (raw git output is trimmed)
100        let id = project_id();
101        assert!(
102            !id.contains('\n'),
103            "project_id must not contain newlines, got: {id:?}"
104        );
105    }
106
107    #[test]
108    #[cfg(not(feature = "vipune-store"))]
109    fn test_detect_project_fallback_cwd_fallback() {
110        // When run inside a temp dir with no git, detect_project_fallback must
111        // still return a non-empty string (the directory name or "unknown").
112        let tmp = tempfile::tempdir().expect("tempdir");
113        let original = std::env::current_dir().expect("cwd");
114        std::env::set_current_dir(tmp.path()).expect("set_current_dir");
115
116        let id = detect_project_fallback();
117
118        std::env::set_current_dir(&original).expect("restore cwd");
119        // Either the dir name (a UUID-ish string) or "unknown" — both are acceptable
120        assert!(!id.is_empty(), "fallback must not be empty");
121    }
122}