Skip to main content

git_su/
switcher.rs

1// Switcher: request switch, print current, list, add, clear, edit
2
3use 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(&not_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; // Local, Global, System
93        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}