ralph_workflow/diagnostics/system/
io.rs1use std::process::Command;
7
8#[derive(Debug, Clone)]
10pub struct SystemInfo {
11 pub os: String,
12 pub arch: String,
13 pub working_directory: Option<String>,
14 pub shell: Option<String>,
15 pub git_version: Option<String>,
16 pub git_repo: bool,
17 pub git_branch: Option<String>,
18 pub uncommitted_changes: Option<usize>,
19}
20
21impl SystemInfo {
22 #[must_use]
23 pub fn gather() -> Self {
24 Self::gather_with_runner(run_command)
25 }
26
27 pub fn gather_with_runner(runner: impl Fn(&str, &[&str]) -> Option<String>) -> Self {
28 let os = std::env::consts::OS.to_string();
29 let arch = std::env::consts::ARCH.to_string();
30 let working_directory = gather_working_directory();
31 let shell = gather_shell();
32 let git_version = runner("git", &["--version"]);
33 let git_repo = check_git_repo(&runner);
34 let (git_branch, uncommitted_changes) = gather_git_details(&runner, git_repo);
35
36 Self {
37 os,
38 arch,
39 working_directory,
40 shell,
41 git_version,
42 git_repo,
43 git_branch,
44 uncommitted_changes,
45 }
46 }
47}
48
49fn gather_working_directory() -> Option<String> {
50 std::env::current_dir()
51 .ok()
52 .map(|p| p.display().to_string())
53}
54
55fn gather_shell() -> Option<String> {
56 std::env::var("SHELL")
57 .ok()
58 .or_else(|| std::env::var("ComSpec").ok())
59}
60
61fn gather_git_details(
62 runner: &impl Fn(&str, &[&str]) -> Option<String>,
63 git_repo: bool,
64) -> (Option<String>, Option<usize>) {
65 let git_branch = git_repo
66 .then(|| runner("git", &["branch", "--show-current"]))
67 .flatten();
68 let uncommitted_changes = git_repo
69 .then(|| runner("git", &["status", "--porcelain"]).map(|o| o.lines().count()))
70 .flatten();
71 (git_branch, uncommitted_changes)
72}
73
74fn check_git_repo(runner: &impl Fn(&str, &[&str]) -> Option<String>) -> bool {
75 runner("git", &["rev-parse", "--is-inside-work-tree"])
76 .is_some_and(|out| out.trim() == "true")
77}
78
79fn run_command(command: &str, args: &[&str]) -> Option<String> {
80 let output = Command::new(command).args(args).output().ok()?;
81 output
82 .status
83 .success()
84 .then(|| String::from_utf8_lossy(&output.stdout).trim().to_string())
85}