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