Skip to content

Commit 960d657

Browse files
committed
feat: add clear command to reset DB without losing session
1 parent 509464d commit 960d657

3 files changed

Lines changed: 208 additions & 0 deletions

File tree

src/cmd/clear.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
use crate::out;
2+
use crate::store::Store;
3+
use crate::Cli;
4+
use anyhow::Result;
5+
use clap::Args;
6+
use std::io::{self, Write};
7+
8+
#[derive(Args, Debug, Clone)]
9+
pub struct ClearArgs {
10+
/// Only clear chats and messages
11+
#[arg(long)]
12+
pub chats: bool,
13+
14+
/// Only clear contacts
15+
#[arg(long)]
16+
pub contacts: bool,
17+
18+
/// Skip confirmation prompt
19+
#[arg(long, short = 'y')]
20+
pub confirm: bool,
21+
}
22+
23+
pub async fn run(cli: &Cli, args: &ClearArgs) -> Result<()> {
24+
let store_dir = cli.store_dir();
25+
let store = Store::open(&store_dir).await?;
26+
27+
// Determine what to clear
28+
let clear_all = !args.chats && !args.contacts;
29+
let clear_chats = clear_all || args.chats;
30+
let clear_contacts = clear_all || args.contacts;
31+
32+
// Get counts before clearing
33+
let counts = get_counts(&store, clear_chats, clear_contacts).await?;
34+
35+
if counts.total() == 0 {
36+
if cli.json {
37+
out::write_json(&serde_json::json!({
38+
"cleared": false,
39+
"reason": "nothing to clear"
40+
}))?;
41+
} else {
42+
println!("Nothing to clear.");
43+
}
44+
return Ok(());
45+
}
46+
47+
// Show what will be deleted
48+
if !cli.json && !args.confirm {
49+
println!("This will delete:");
50+
if clear_chats {
51+
println!(" - {} messages", counts.messages);
52+
println!(" - {} chats", counts.chats);
53+
println!(" - {} topics", counts.topics);
54+
}
55+
if clear_contacts {
56+
println!(" - {} contacts", counts.contacts);
57+
}
58+
println!();
59+
print!("Are you sure? [y/N] ");
60+
io::stdout().flush()?;
61+
62+
let mut input = String::new();
63+
io::stdin().read_line(&mut input)?;
64+
let input = input.trim().to_lowercase();
65+
66+
if input != "y" && input != "yes" {
67+
println!("Aborted.");
68+
return Ok(());
69+
}
70+
}
71+
72+
// Perform the deletion
73+
let deleted = clear_tables(&store, clear_chats, clear_contacts).await?;
74+
75+
if cli.json {
76+
out::write_json(&serde_json::json!({
77+
"cleared": true,
78+
"deleted": {
79+
"messages": deleted.messages,
80+
"chats": deleted.chats,
81+
"topics": deleted.topics,
82+
"contacts": deleted.contacts
83+
}
84+
}))?;
85+
} else {
86+
println!("Cleared:");
87+
if clear_chats {
88+
println!(" - {} messages", deleted.messages);
89+
println!(" - {} chats", deleted.chats);
90+
println!(" - {} topics", deleted.topics);
91+
}
92+
if clear_contacts {
93+
println!(" - {} contacts", deleted.contacts);
94+
}
95+
}
96+
97+
Ok(())
98+
}
99+
100+
#[derive(Default)]
101+
struct Counts {
102+
messages: u64,
103+
chats: u64,
104+
topics: u64,
105+
contacts: u64,
106+
}
107+
108+
impl Counts {
109+
fn total(&self) -> u64 {
110+
self.messages + self.chats + self.topics + self.contacts
111+
}
112+
}
113+
114+
async fn get_counts(store: &Store, chats: bool, contacts: bool) -> Result<Counts> {
115+
let mut counts = Counts::default();
116+
117+
if chats {
118+
counts.messages = store.count_messages().await?;
119+
counts.chats = store.count_chats().await?;
120+
counts.topics = store.count_topics().await?;
121+
}
122+
if contacts {
123+
counts.contacts = store.count_contacts().await?;
124+
}
125+
126+
Ok(counts)
127+
}
128+
129+
async fn clear_tables(store: &Store, chats: bool, contacts: bool) -> Result<Counts> {
130+
let mut deleted = Counts::default();
131+
132+
if chats {
133+
deleted.messages = store.clear_messages().await?;
134+
deleted.topics = store.clear_topics().await?;
135+
deleted.chats = store.clear_chats().await?;
136+
}
137+
if contacts {
138+
deleted.contacts = store.clear_contacts().await?;
139+
}
140+
141+
Ok(deleted)
142+
}

src/cmd/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod auth;
22
pub mod chats;
3+
pub mod clear;
34
pub mod contacts;
45
pub mod folders;
56
pub mod messages;
@@ -22,6 +23,8 @@ pub enum Command {
2223
Auth(auth::AuthArgs),
2324
/// Sync messages from Telegram
2425
Sync(sync::SyncArgs),
26+
/// Clear local database (keeps session)
27+
Clear(clear::ClearArgs),
2528
/// List and show chats
2629
Chats {
2730
#[command(subcommand)]
@@ -76,6 +79,7 @@ pub async fn run(cli: Cli) -> anyhow::Result<()> {
7679
match &cli.command {
7780
Command::Auth(args) => auth::run(&cli, args).await,
7881
Command::Sync(args) => sync::run(&cli, args).await,
82+
Command::Clear(args) => clear::run(&cli, args).await,
7983
Command::Chats { cmd } => chats::run(&cli, cmd).await,
8084
Command::Messages { cmd } => messages::run(&cli, cmd).await,
8185
Command::Send(args) => send::run(&cli, args).await,

src/store/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,68 @@ impl Store {
923923
}
924924
}
925925

926+
// --- Count methods (for clear command) ---
927+
928+
pub async fn count_messages(&self) -> Result<u64> {
929+
let mut rows = self.conn.query("SELECT COUNT(*) FROM messages", ()).await?;
930+
if let Some(row) = rows.next().await? {
931+
Ok(row.get::<i64>(0)? as u64)
932+
} else {
933+
Ok(0)
934+
}
935+
}
936+
937+
pub async fn count_chats(&self) -> Result<u64> {
938+
let mut rows = self.conn.query("SELECT COUNT(*) FROM chats", ()).await?;
939+
if let Some(row) = rows.next().await? {
940+
Ok(row.get::<i64>(0)? as u64)
941+
} else {
942+
Ok(0)
943+
}
944+
}
945+
946+
pub async fn count_topics(&self) -> Result<u64> {
947+
let mut rows = self.conn.query("SELECT COUNT(*) FROM topics", ()).await?;
948+
if let Some(row) = rows.next().await? {
949+
Ok(row.get::<i64>(0)? as u64)
950+
} else {
951+
Ok(0)
952+
}
953+
}
954+
955+
pub async fn count_contacts(&self) -> Result<u64> {
956+
let mut rows = self.conn.query("SELECT COUNT(*) FROM contacts", ()).await?;
957+
if let Some(row) = rows.next().await? {
958+
Ok(row.get::<i64>(0)? as u64)
959+
} else {
960+
Ok(0)
961+
}
962+
}
963+
964+
// --- Clear methods (for clear command) ---
965+
966+
pub async fn clear_messages(&self) -> Result<u64> {
967+
// Also clear FTS table if it exists
968+
let _ = self.conn.execute("DELETE FROM messages_fts", ()).await;
969+
let affected = self.conn.execute("DELETE FROM messages", ()).await?;
970+
Ok(affected)
971+
}
972+
973+
pub async fn clear_chats(&self) -> Result<u64> {
974+
let affected = self.conn.execute("DELETE FROM chats", ()).await?;
975+
Ok(affected)
976+
}
977+
978+
pub async fn clear_topics(&self) -> Result<u64> {
979+
let affected = self.conn.execute("DELETE FROM topics", ()).await?;
980+
Ok(affected)
981+
}
982+
983+
pub async fn clear_contacts(&self) -> Result<u64> {
984+
let affected = self.conn.execute("DELETE FROM contacts", ()).await?;
985+
Ok(affected)
986+
}
987+
926988
/// Get the oldest message ID for a chat (lowest message ID).
927989
/// Returns None if no messages exist for the chat.
928990
pub async fn get_oldest_message_id(

0 commit comments

Comments
 (0)