Skip to content

Commit 8aeec2a

Browse files
committed
Refactor command parsing
1 parent f706606 commit 8aeec2a

2 files changed

Lines changed: 118 additions & 69 deletions

File tree

src/client.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ impl Client {
262262
},
263263
);
264264
}
265+
266+
pub fn get_db(&self) -> sqlx::SqlitePool {
267+
self.db_pool.clone()
268+
}
265269
}
266270

267271
#[derive(Clone, Debug)]
@@ -535,7 +539,7 @@ impl Context {
535539
}
536540

537541
pub fn get_db(&self) -> sqlx::SqlitePool {
538-
self.client.db_pool.clone()
542+
self.client.get_db()
539543
}
540544
}
541545

src/plugins/remind.rs

Lines changed: 113 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ use std::time::Duration;
22

33
use crate::prelude::*;
44

5-
pub struct RemindPlugin {
6-
db_pool: sqlx::SqlitePool,
7-
}
5+
pub struct RemindPlugin {}
86

97
#[derive(sqlx::FromRow)]
108
struct Reminder {
@@ -34,7 +32,12 @@ fn parse_duration(s: &str) -> Result<Duration> {
3432
"h" => num * 60 * 60,
3533
"d" => num * 60 * 60 * 24,
3634
"w" => num * 60 * 60 * 24 * 7,
37-
_ => return Err(format_err!("Unknown duration unit '{}'. Use s/m/h/d/w", unit)),
35+
_ => {
36+
return Err(format_err!(
37+
"Unknown duration unit '{}'. Use s/m/h/d/w",
38+
unit
39+
))
40+
}
3841
};
3942

4043
Ok(Duration::from_secs(seconds))
@@ -57,11 +60,17 @@ fn format_duration(secs: i64) -> String {
5760
}
5861

5962
impl RemindPlugin {
63+
fn new() -> Self {
64+
RemindPlugin {}
65+
}
66+
6067
async fn handle_list(&self, ctx: &Arc<Context>) -> Result<()> {
6168
let sender = ctx
6269
.sender()
6370
.ok_or_else(|| format_err!("Could not determine sender"))?;
6471

72+
let db = ctx.get_db();
73+
6574
let reminders: Vec<Reminder> = sqlx::query_as!(
6675
Reminder,
6776
r#"SELECT id as "id!", channel_id, target_user, message, remind_at, created_at, created_by
@@ -71,7 +80,7 @@ impl RemindPlugin {
7180
LIMIT 10"#,
7281
sender
7382
)
74-
.fetch_all(&self.db_pool)
83+
.fetch_all(&db)
7584
.await?;
7685

7786
if reminders.is_empty() {
@@ -92,11 +101,15 @@ impl RemindPlugin {
92101
} else {
93102
r.target_user.clone()
94103
};
95-
format!("[{}] in {} for {}: \"{}\"", r.id, time_left, target, r.message)
104+
format!(
105+
"[{}] in {} for {}: \"{}\"",
106+
r.id, time_left, target, r.message
107+
)
96108
})
97109
.collect();
98110

99111
ctx.mention_reply(&lines.join(" | ")).await?;
112+
100113
Ok(())
101114
}
102115

@@ -108,74 +121,40 @@ impl RemindPlugin {
108121
let id: i64 = match id_str.trim().parse() {
109122
Ok(id) => id,
110123
Err(_) => {
111-
ctx.mention_reply("Invalid reminder ID. Use 'remind list' to see your reminders.").await?;
124+
ctx.mention_reply("Invalid reminder ID. Use 'remind list' to see your reminders.")
125+
.await?;
112126
return Ok(());
113127
}
114128
};
115129

130+
let db = ctx.get_db();
131+
116132
let result = sqlx::query!(
117133
"DELETE FROM reminders WHERE id = $1 AND (created_by = $2 OR target_user = $2)",
118134
id,
119135
sender
120136
)
121-
.execute(&self.db_pool)
137+
.execute(&db)
122138
.await?;
123139

124140
if result.rows_affected() == 0 {
125-
ctx.mention_reply("Reminder not found or you don't have permission to cancel it.").await?;
141+
ctx.mention_reply("Reminder not found or you don't have permission to cancel it.")
142+
.await?;
126143
} else {
127144
ctx.mention_reply("Reminder cancelled.").await?;
128145
}
129146

130147
Ok(())
131148
}
132149

133-
async fn handle_remind(&self, ctx: &Arc<Context>, arg: Option<&str>) -> Result<()> {
134-
let arg = match arg {
135-
Some(a) => a,
136-
None => {
137-
ctx.mention_reply("Usage: remind <user|me> <time> <message> | remind list | remind cancel <id>").await?;
138-
return Ok(());
139-
}
140-
};
141-
142-
let parts: Vec<&str> = arg.splitn(3, ' ').collect();
143-
144-
// Handle subcommands
145-
if parts[0].eq_ignore_ascii_case("list") {
146-
return self.handle_list(ctx).await;
147-
}
148-
if parts[0].eq_ignore_ascii_case("cancel") {
149-
if parts.len() < 2 {
150-
ctx.mention_reply("Usage: remind cancel <id>").await?;
151-
return Ok(());
152-
}
153-
return self.handle_cancel(ctx, parts[1]).await;
154-
}
155-
if parts.len() < 3 {
156-
ctx.mention_reply("Usage: remind <user|me> <time> <message>").await?;
157-
return Ok(());
158-
}
159-
160-
let sender = ctx
161-
.sender()
162-
.ok_or_else(|| format_err!("Could not determine sender"))?;
163-
164-
let target_user = if parts[0].eq_ignore_ascii_case("me") {
165-
sender.to_string()
166-
} else {
167-
parts[0].to_string()
168-
};
169-
let duration_str = parts[1];
170-
let message = parts[2];
171-
172-
let duration = match parse_duration(duration_str) {
173-
Ok(d) => d,
174-
Err(e) => {
175-
ctx.mention_reply(&format!("{}", e)).await?;
176-
return Ok(());
177-
}
178-
};
150+
async fn handle_add(
151+
&self,
152+
ctx: &Arc<Context>,
153+
target: &str,
154+
duration: std::time::Duration,
155+
message: &str,
156+
) -> Result<()> {
157+
let sender = ctx.sender().unwrap_or("unknown sender");
179158

180159
let channel_id = ctx
181160
.target_channel_id()
@@ -186,22 +165,24 @@ impl RemindPlugin {
186165
.as_secs() as i64;
187166
let remind_at = now + duration.as_secs() as i64;
188167

168+
let db = ctx.get_db();
169+
189170
sqlx::query!(
190171
"INSERT INTO reminders (channel_id, target_user, message, remind_at, created_at, created_by) VALUES ($1, $2, $3, $4, $5, $6)",
191172
channel_id,
192-
target_user,
173+
target,
193174
message,
194175
remind_at,
195176
now,
196177
sender
197178
)
198-
.execute(&self.db_pool)
179+
.execute(&db)
199180
.await?;
200181

201182
let duration_text = format_duration(duration.as_secs() as i64);
202183
ctx.mention_reply(&format!(
203184
"I'll remind {} in {}: \"{}\"",
204-
if target_user == sender { "you".to_string() } else { target_user },
185+
if target == sender { "you" } else { target },
205186
duration_text,
206187
message
207188
))
@@ -210,23 +191,92 @@ impl RemindPlugin {
210191
Ok(())
211192
}
212193

213-
async fn check_due_reminders(&self, bot: &Arc<Client>) -> Result<()> {
194+
async fn handle_remind(&self, ctx: &Arc<Context>, arg: Option<&str>) -> Result<()> {
195+
let arg = match arg {
196+
Some(a) => a,
197+
None => {
198+
ctx.mention_reply(
199+
"Usage: remind <user|me> <time> <message> | remind list | remind cancel <id>",
200+
)
201+
.await?;
202+
return Ok(());
203+
}
204+
};
205+
206+
let mut parts = arg.splitn(2, ' ');
207+
let first_arg = parts.next().unwrap_or("");
208+
let rest = parts.next();
209+
210+
match first_arg {
211+
"list" => {
212+
self.handle_list(ctx).await?;
213+
return Ok(());
214+
}
215+
"cancel" => {
216+
let id_str = match rest {
217+
None => {
218+
ctx.mention_reply("Usage: remind cancel <id>").await?;
219+
return Ok(());
220+
}
221+
Some(id_str) => id_str,
222+
};
223+
224+
self.handle_cancel(ctx, id_str).await?;
225+
return Ok(());
226+
}
227+
arg => {
228+
let (duration, message) = match rest.unwrap_or("").split_once(' ') {
229+
None => {
230+
ctx.mention_reply("Usage: remind <user|me> <time> <message>")
231+
.await?;
232+
return Ok(());
233+
}
234+
Some((time, message)) => (time, message),
235+
};
236+
237+
let target = if arg.eq_ignore_ascii_case("me") {
238+
ctx.sender().unwrap_or("unknown sender")
239+
} else {
240+
arg
241+
};
242+
243+
let duration = match parse_duration(duration) {
244+
Err(e) => {
245+
ctx.mention_reply(&format!("{}", e)).await?;
246+
return Ok(());
247+
}
248+
Ok(d) => d,
249+
};
250+
251+
self.handle_add(ctx, target, duration, message).await?;
252+
253+
return Ok(());
254+
}
255+
}
256+
}
257+
258+
async fn check_due_reminders(&self, bot: &Client) -> Result<()> {
214259
let now = std::time::SystemTime::now()
215260
.duration_since(std::time::UNIX_EPOCH)?
216261
.as_secs() as i64;
217262

263+
let db = bot.get_db();
264+
218265
let reminders: Vec<Reminder> = sqlx::query_as!(
219266
Reminder,
220267
r#"SELECT id as "id!", channel_id, target_user, message, remind_at, created_at, created_by FROM reminders WHERE remind_at <= $1"#,
221268
now
222269
)
223-
.fetch_all(&self.db_pool)
270+
.fetch_all(&db)
224271
.await?;
225272

226273
for reminder in reminders {
227274
let age = format_duration(now - reminder.created_at);
228275
let msg = if reminder.created_by == reminder.target_user {
229-
format!("{}: Reminder ({} ago): {}", reminder.target_user, age, reminder.message)
276+
format!(
277+
"{}: Reminder ({} ago): {}",
278+
reminder.target_user, age, reminder.message
279+
)
230280
} else {
231281
format!(
232282
"{}: Reminder from {} ({} ago): {}",
@@ -240,7 +290,7 @@ impl RemindPlugin {
240290
}
241291

242292
sqlx::query!("DELETE FROM reminders WHERE id = $1", reminder.id)
243-
.execute(&self.db_pool)
293+
.execute(&db)
244294
.await?;
245295
}
246296

@@ -251,12 +301,7 @@ impl RemindPlugin {
251301
#[async_trait]
252302
impl Plugin for RemindPlugin {
253303
fn new_from_env() -> Result<Self> {
254-
let db_url = dotenvy::var("DATABASE_URL")
255-
.map_err(|_| format_err!("Missing $DATABASE_URL"))?;
256-
let db_pool = sqlx::sqlite::SqlitePoolOptions::new()
257-
.max_connections(2)
258-
.connect_lazy(&db_url)?;
259-
Ok(RemindPlugin { db_pool })
304+
Ok(RemindPlugin::new())
260305
}
261306

262307
fn command_metadata(&self) -> Vec<CommandMetadata> {

0 commit comments

Comments
 (0)