pitboard_core/switch/uninstall.rs
1//! Taking pitboard off a machine without leaving credentials behind.
2
3use super::{Result, Settled, purge};
4use crate::context::Context;
5use crate::{home, state};
6
7/// What was removed, for the report.
8pub struct Removed {
9 /// Parked logins deleted from the keychain or the vault.
10 pub parks: usize,
11 /// Parked logins that could not be deleted, which is why the home was kept.
12 pub pending: usize,
13 /// Whether ~/.pitboard itself is gone.
14 pub home_removed: bool,
15}
16
17/// Deletes every parked login, then pitboard's own directory. Claude Code's login is left
18/// exactly as it is: whoever is signed in stays signed in.
19///
20/// The directory is removed last and only when every parked login is gone, because
21/// state.json is the only index of those keychain items. Deleting it first would leave live
22/// refresh tokens on the machine with no way left to name them.
23pub fn uninstall(settled: Settled) -> Result<Removed> {
24 let Settled {
25 _exclusive,
26 mut state,
27 ctx,
28 } = settled;
29 let parks = state.accounts.iter().filter(|a| a.parked.is_some()).count();
30 for label in state
31 .accounts
32 .iter()
33 .map(|a| a.label.clone())
34 .collect::<Vec<_>>()
35 {
36 state.remove(&label);
37 }
38 state.active = None;
39 state::save(&ctx, &state)?;
40 let pending = purge(&ctx, &mut state);
41 let home_removed = pending == 0 && remove_home(&ctx);
42 Ok(Removed {
43 parks: parks - pending,
44 pending,
45 home_removed,
46 })
47}
48
49/// The lock file this run holds lives in here too; on Unix an open file goes on existing
50/// until the last handle closes, so removing the directory now is safe.
51fn remove_home(ctx: &Context) -> bool {
52 std::fs::remove_dir_all(home::dir(ctx)).is_ok()
53}