Skip to main content

git_su/
git.rs

1// Git: config get/set/unset, scopes local/global/system
2
3use std::io::IsTerminal;
4
5use crate::shell;
6use crate::user::User;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum Scope {
10    Local,
11    Global,
12    System,
13    Derived,
14    Default,
15}
16
17impl Scope {
18    pub fn as_flag(self) -> &'static str {
19        match self {
20            Scope::Local => "local",
21            Scope::Global => "global",
22            Scope::System => "system",
23            Scope::Derived | Scope::Default => "",
24        }
25    }
26
27    pub fn display_name(self) -> &'static str {
28        match self {
29            Scope::Local => "local",
30            Scope::Global => "global",
31            Scope::System => "system",
32            Scope::Derived => "derived",
33            Scope::Default => "local",
34        }
35    }
36}
37
38pub struct Git;
39
40impl Git {
41    pub fn get_config(scope: Scope, key: &str) -> String {
42        let suffix = format!("--get {}", key);
43        let cmd = if scope == Scope::Derived {
44            format!("git config {}", suffix)
45        } else {
46            format!("git config --{} {}", scope.as_flag(), suffix)
47        };
48        shell::capture(&cmd)
49    }
50
51    pub fn set_config(scope: Scope, key: &str, value: &str) -> bool {
52        let escaped = value.replace("'", "'\\''");
53        let suffix = format!("{} '{}'", key, escaped);
54        let cmd = if scope == Scope::Derived {
55            format!("git config {}", suffix)
56        } else {
57            format!("git config --{} {}", scope.as_flag(), suffix)
58        };
59        shell::execute(&cmd)
60    }
61
62    pub fn unset_config(scope: Scope, key: &str) -> bool {
63        let suffix = format!("--unset {}", key);
64        let cmd = if scope == Scope::Derived {
65            format!("git config {}", suffix)
66        } else {
67            format!("git config --{} {}", scope.as_flag(), suffix)
68        };
69        shell::execute(&cmd)
70    }
71
72    pub fn list_config(scope: Scope) -> Vec<String> {
73        let suffix = "--list";
74        let cmd = if scope == Scope::Derived {
75            format!("git config {}", suffix)
76        } else {
77            format!("git config --{} {}", scope.as_flag(), suffix)
78        };
79        let s = shell::capture(&cmd);
80        if s.is_empty() {
81            return vec![];
82        }
83        s.lines().map(str::to_string).collect()
84    }
85
86    pub fn remove_section(scope: Scope, section: &str) -> bool {
87        let suffix = format!("--remove-section {} 2>/dev/null", section);
88        let cmd = if scope == Scope::Derived {
89            format!("git config {}", suffix)
90        } else {
91            format!("git config --{} {}", scope.as_flag(), suffix)
92        };
93        shell::execute(&cmd)
94    }
95
96    pub fn select_user(user: &User, scope: Scope) -> bool {
97        Self::set_config(scope, "user.name", &user.name())
98            && Self::set_config(scope, "user.email", &user.email())
99    }
100
101    pub fn selected_user(scope: Scope) -> User {
102        let name = Self::get_config(scope, "user.name");
103        if name.is_empty() {
104            return User::none();
105        }
106        let email = Self::get_config(scope, "user.email");
107        User::new(name, email)
108    }
109
110    pub fn clear_user(scope: Scope) {
111        let current = Self::selected_user(scope);
112        if current.is_none() {
113            return;
114        }
115        Self::unset_config(scope, "user.name");
116        Self::unset_config(scope, "user.email");
117        let list = Self::list_config(scope);
118        if list.iter().filter(|e| e.starts_with("user.")).count() == 0 {
119            Self::remove_section(scope, "user");
120        }
121    }
122
123    pub fn edit_gitsu_config(path: &std::path::Path) -> bool {
124        let path_str = path.to_string_lossy();
125        let cmd = format!("git config --edit --file {}", path_str);
126        shell::execute(&cmd)
127    }
128
129    pub fn get_color(name: &str) -> String {
130        let suffix = format!("--get-color '' '{}'", name);
131        let cmd = format!("git config {}", suffix);
132        shell::capture(&cmd)
133    }
134
135    pub fn color_output() -> bool {
136        if !std::io::stdout().is_terminal() {
137            return false;
138        }
139        let cmd = "git config --get-colorbool color.ui true";
140        let v = shell::capture(cmd).trim().to_string();
141        v == "true"
142    }
143
144    pub fn render(user: &User) -> String {
145        if Self::color_output() {
146            let user_color = Self::get_color("blue");
147            let email_color = Self::get_color("green");
148            let reset = Self::get_color("reset");
149            format!(
150                "{}{} {}<{}>{}",
151                user_color,
152                user.name(),
153                email_color,
154                user.email(),
155                reset
156            )
157        } else {
158            user.to_string()
159        }
160    }
161}