1use crate::config_repository::ConfigRepository;
4use crate::git::{Git, Scope};
5use crate::user::User;
6use crate::user_list::UserList;
7use std::path::Path;
8
9pub struct Switcher<'a> {
10 user_list: &'a UserList,
11}
12
13impl<'a> Switcher<'a> {
14 pub fn new(user_list: &'a UserList) -> Self {
15 Switcher { user_list }
16 }
17
18 pub fn request(&self, scope: Scope, user_strings: &[String], output: &mut impl std::io::Write) {
19 let (parsed, not_parsed): (Vec<User>, Vec<String>) = user_strings
20 .iter()
21 .map(|s| {
22 User::parse(s)
23 .map(|u| (Some(u), None))
24 .unwrap_or_else(|_| (None, Some(s.clone())))
25 })
26 .fold(
27 (vec![], vec![]),
28 |(mut parsed, mut not_parsed), (p, n)| {
29 if let Some(u) = p {
30 parsed.push(u);
31 }
32 if let Some(s) = n {
33 not_parsed.push(s);
34 }
35 (parsed, not_parsed)
36 },
37 );
38
39 let found = match self.user_list.find(¬_parsed) {
40 Ok(users) => users,
41 Err(e) => {
42 let _ = writeln!(output, "{}", crate::color::error(&e.to_string()));
43 return;
44 }
45 };
46
47 let group_email = ConfigRepository::group_email_address();
48 let all_users: Vec<User> = parsed
49 .iter()
50 .chain(found.iter())
51 .cloned()
52 .collect();
53 let combined = all_users
54 .iter()
55 .fold(User::none(), |acc, u| acc.combine(u, &group_email));
56
57 let actual_scope = if scope == Scope::Default || scope == Scope::Derived {
58 ConfigRepository::default_select_scope()
59 } else {
60 scope
61 };
62
63 if Git::select_user(&combined, actual_scope) {
64 let _ = writeln!(
65 output,
66 "{}",
67 crate::color::success(&format!(
68 "Switched {} user to {}",
69 actual_scope.display_name(),
70 Git::render(&combined)
71 ))
72 );
73 } else {
74 let _ = writeln!(
75 output,
76 "{}",
77 crate::color::error(&format!(
78 "Failed to set user in {} scope (e.g. system scope may need: sudo git su --system <user>)",
79 actual_scope.display_name()
80 ))
81 );
82 }
83
84 for u in &parsed {
85 if !u.is_none() && !self.user_list.list().iter().any(|l| l == u) {
86 self.user_list.add(u);
87 }
88 }
89 }
90
91 pub fn print_current(&self, scopes: &[Scope], output: &mut impl std::io::Write) {
92 const LABEL_WIDTH: usize = 6; let show_all = scopes.is_empty();
94 if show_all {
95 let _ = writeln!(
96 output,
97 "{} {}",
98 crate::color::label("Current user:"),
99 Git::render(&Git::selected_user(Scope::Derived))
100 );
101 let _ = writeln!(output);
102 for (scope_label, scope) in [
103 ("Local", Scope::Local),
104 ("Global", Scope::Global),
105 ("System", Scope::System),
106 ] {
107 let padded = format!("{:>1$}", scope_label, LABEL_WIDTH);
108 let _ = writeln!(
109 output,
110 "{}: {}",
111 crate::color::label(&padded),
112 Git::render(&Git::selected_user(scope))
113 );
114 }
115 } else {
116 let scope_labels: [(&str, Scope); 3] = [
117 ("Local", Scope::Local),
118 ("Global", Scope::Global),
119 ("System", Scope::System),
120 ];
121 for (scope_label, scope) in scope_labels {
122 if !scopes.contains(&scope) {
123 continue;
124 }
125 let w = scope_label.len().max(LABEL_WIDTH);
126 let padded = format!("{:<1$}", scope_label, w);
127 let u = Git::selected_user(scope);
128 let _ = writeln!(
129 output,
130 "{}: {}",
131 crate::color::label(&padded),
132 Git::render(&u)
133 );
134 }
135 }
136 }
137
138 pub fn clear(&self, scopes: &[Scope], output: &mut impl std::io::Write) {
139 let scopes: Vec<Scope> = if scopes.is_empty() {
140 vec![Scope::Local, Scope::Global, Scope::System]
141 } else {
142 scopes.to_vec()
143 };
144 let scope_names: Vec<&str> = scopes.iter().map(|s| s.as_flag()).collect();
145 let _ = writeln!(
146 output,
147 "{}",
148 crate::color::dim(&format!(
149 "Clearing Git user in {} scope(s)",
150 scope_names.join(", ")
151 ))
152 );
153 for scope in scopes {
154 Git::clear_user(scope);
155 }
156 }
157
158 pub fn list(&self, output: &mut impl std::io::Write) {
159 for u in self.user_list.list() {
160 let _ = writeln!(output, "{}", Git::render(&u));
161 }
162 }
163
164 pub fn add(&self, user_string: &str, output: &mut impl std::io::Write) {
165 let user = match User::parse(user_string) {
166 Ok(u) => u,
167 Err(e) => {
168 let _ = writeln!(output, "{}", crate::color::error(&e.to_string()));
169 return;
170 }
171 };
172 if self.user_list.list().iter().any(|u| u == &user) {
173 let _ = writeln!(
174 output,
175 "{}",
176 crate::color::dim(&format!(
177 "User '{}' already in user list (try switching to them with 'git su {}')",
178 user,
179 user.initials()
180 ))
181 );
182 } else {
183 self.user_list.add(&user);
184 let _ = writeln!(
185 output,
186 "{}",
187 crate::color::success(&format!("User '{}' added to users", user))
188 );
189 }
190 }
191
192 pub fn edit_config(&self, path: &Path) -> bool {
193 Git::edit_gitsu_config(path)
194 }
195}