clippy: clear workspace warnings
A newer clippy lint set surfaced ~48 warnings across the workspace that
predate this work. Auto-fixed the mechanical ones (collapsible if -> let
chains, manual range contains, etc.) via clippy --fix, and hand-fixed the
rest: sort_by_key + Reverse for the two reverse sorts, type aliases for the
two complex sync_changelog row tuples, allow(clippy::too_many_arguments) on
the civil-date recurrence helper, and allow(deprecated) on the shell opener
(migration to tauri-plugin-opener tracked separately). cargo clippy is clean.
- Co-Authored-By
- Claude Opus 4.8 <noreply@anthropic.com>
29 files changed,
+105 insertions,
-140 deletions
| 230 |
230 |
|
.collect();
|
| 231 |
231 |
|
|
| 232 |
232 |
|
// Sort by creation time, newest first
|
| 233 |
|
- |
backups.sort_by(|a, b| b.1.cmp(&a.1));
|
|
233 |
+ |
backups.sort_by_key(|b| std::cmp::Reverse(b.1));
|
| 234 |
234 |
|
|
| 235 |
235 |
|
// Remove backups beyond the limit
|
| 236 |
236 |
|
for (path, _) in backups.into_iter().skip(max_to_keep) {
|
| 172 |
172 |
|
.duration_since(SystemTime::UNIX_EPOCH)
|
| 173 |
173 |
|
.unwrap_or_default()
|
| 174 |
174 |
|
.subsec_nanos();
|
| 175 |
|
- |
(nanos % backoff) != 0
|
|
175 |
+ |
!nanos.is_multiple_of(backoff)
|
| 176 |
176 |
|
}
|
| 47 |
47 |
|
button_state: MouseButtonState::Up,
|
| 48 |
48 |
|
..
|
| 49 |
49 |
|
} = event
|
| 50 |
|
- |
{
|
| 51 |
|
- |
if let Some(window) = tray.app_handle().get_webview_window("main") {
|
|
50 |
+ |
&& let Some(window) = tray.app_handle().get_webview_window("main") {
|
| 52 |
51 |
|
let _ = window.show();
|
| 53 |
52 |
|
let _ = window.unminimize();
|
| 54 |
53 |
|
let _ = window.set_focus();
|
| 55 |
54 |
|
}
|
| 56 |
|
- |
}
|
| 57 |
55 |
|
})
|
| 58 |
56 |
|
.on_menu_event(|app, event| match event.id().as_ref() {
|
| 59 |
57 |
|
"tray_show" => {
|
| 122 |
122 |
|
|
| 123 |
123 |
|
for task in snoozed_tasks {
|
| 124 |
124 |
|
// Check if snooze has expired and we haven't notified yet
|
| 125 |
|
- |
if let Some(snoozed_until) = task.snoozed_until {
|
| 126 |
|
- |
if snoozed_until <= now && !notified.task_ids.contains(&task.id) {
|
|
125 |
+ |
if let Some(snoozed_until) = task.snoozed_until
|
|
126 |
+ |
&& snoozed_until <= now && !notified.task_ids.contains(&task.id) {
|
| 127 |
127 |
|
info!(task_id = %task.id, "Task snooze expired, sending notification");
|
| 128 |
128 |
|
|
| 129 |
129 |
|
// Send notification
|
| 141 |
141 |
|
warn!(task_id = %task.id, error = %e, "Failed to unsnooze task after notification");
|
| 142 |
142 |
|
}
|
| 143 |
143 |
|
}
|
| 144 |
|
- |
}
|
| 145 |
144 |
|
}
|
| 146 |
145 |
|
|
| 147 |
146 |
|
Ok(())
|
| 162 |
161 |
|
|
| 163 |
162 |
|
for email in snoozed_emails {
|
| 164 |
163 |
|
// Check if snooze has expired and we haven't notified yet
|
| 165 |
|
- |
if let Some(snoozed_until) = email.snoozed_until {
|
| 166 |
|
- |
if snoozed_until <= now && !notified.email_ids.contains(&email.id) {
|
|
164 |
+ |
if let Some(snoozed_until) = email.snoozed_until
|
|
165 |
+ |
&& snoozed_until <= now && !notified.email_ids.contains(&email.id) {
|
| 167 |
166 |
|
// Send notification
|
| 168 |
167 |
|
send_notification(
|
| 169 |
168 |
|
app,
|
| 179 |
178 |
|
warn!(email_id = %email.id, error = %e, "Failed to unsnooze email after notification");
|
| 180 |
179 |
|
}
|
| 181 |
180 |
|
}
|
| 182 |
|
- |
}
|
| 183 |
181 |
|
}
|
| 184 |
182 |
|
|
| 185 |
183 |
|
Ok(())
|
| 221 |
219 |
|
.map_err(|e| e.to_string())?;
|
| 222 |
220 |
|
|
| 223 |
221 |
|
for email in waiting_emails {
|
| 224 |
|
- |
if let Some(expected_date) = email.expected_response_date {
|
| 225 |
|
- |
if expected_date < now && !notified.email_ids.contains(&email.id) {
|
|
222 |
+ |
if let Some(expected_date) = email.expected_response_date
|
|
223 |
+ |
&& expected_date < now && !notified.email_ids.contains(&email.id) {
|
| 226 |
224 |
|
send_notification(
|
| 227 |
225 |
|
app,
|
| 228 |
226 |
|
"Response Overdue",
|
| 230 |
228 |
|
);
|
| 231 |
229 |
|
notified.email_ids.insert(email.id);
|
| 232 |
230 |
|
}
|
| 233 |
|
- |
}
|
| 234 |
231 |
|
}
|
| 235 |
232 |
|
|
| 236 |
233 |
|
Ok(())
|
| 215 |
215 |
|
fn load_api_key(data_dir: &std::path::Path) -> Option<String> {
|
| 216 |
216 |
|
// Migrate plaintext file to keychain (one-time)
|
| 217 |
217 |
|
let key_path = data_dir.join("sync_api_key");
|
| 218 |
|
- |
if key_path.exists() {
|
| 219 |
|
- |
if let Ok(file_key) = std::fs::read_to_string(&key_path) {
|
|
218 |
+ |
if key_path.exists()
|
|
219 |
+ |
&& let Ok(file_key) = std::fs::read_to_string(&key_path) {
|
| 220 |
220 |
|
let file_key = file_key.trim().to_string();
|
| 221 |
221 |
|
if !file_key.is_empty() {
|
| 222 |
222 |
|
if crate::oauth::CredentialStore::get_sync_api_key().is_none() {
|
| 231 |
231 |
|
}
|
| 232 |
232 |
|
}
|
| 233 |
233 |
|
}
|
| 234 |
|
- |
}
|
| 235 |
234 |
|
|
| 236 |
235 |
|
// Load from keychain
|
| 237 |
236 |
|
if let Some(key) = crate::oauth::CredentialStore::get_sync_api_key() {
|
| 143 |
143 |
|
}
|
| 144 |
144 |
|
};
|
| 145 |
145 |
|
|
| 146 |
|
- |
if !last_sync.is_empty() {
|
| 147 |
|
- |
if let Ok(last) = chrono::DateTime::parse_from_rfc3339(&last_sync) {
|
|
146 |
+ |
if !last_sync.is_empty()
|
|
147 |
+ |
&& let Ok(last) = chrono::DateTime::parse_from_rfc3339(&last_sync) {
|
| 148 |
148 |
|
let elapsed = chrono::Utc::now() - last.with_timezone(&chrono::Utc);
|
| 149 |
149 |
|
if elapsed.num_minutes() < interval_minutes as i64 {
|
| 150 |
150 |
|
continue;
|
| 151 |
151 |
|
}
|
| 152 |
152 |
|
}
|
| 153 |
|
- |
}
|
| 154 |
153 |
|
}
|
| 155 |
154 |
|
sse_triggered = false;
|
| 156 |
155 |
|
|
| 157 |
156 |
|
// Check backoff
|
| 158 |
|
- |
if let Some(until) = backoff_until {
|
| 159 |
|
- |
if chrono::Utc::now() < until {
|
|
157 |
+ |
if let Some(until) = backoff_until
|
|
158 |
+ |
&& chrono::Utc::now() < until {
|
| 160 |
159 |
|
debug!("Sync scheduler: backing off until {}", until);
|
| 161 |
160 |
|
continue;
|
| 162 |
161 |
|
}
|
| 163 |
|
- |
}
|
| 164 |
162 |
|
|
| 165 |
163 |
|
// Create initial snapshot on first sync
|
| 166 |
164 |
|
let snapshot_done = sync_service::get_sync_state(&state.pool, "initial_snapshot_done")
|
| 95 |
95 |
|
let mut reply_to_ids: Vec<String> = Vec::new();
|
| 96 |
96 |
|
|
| 97 |
97 |
|
for email in emails {
|
| 98 |
|
- |
if let Some(ref msg_id) = email.message_id {
|
| 99 |
|
- |
if existing_ids.contains(msg_id) {
|
|
98 |
+ |
if let Some(ref msg_id) = email.message_id
|
|
99 |
+ |
&& existing_ids.contains(msg_id) {
|
| 100 |
100 |
|
continue;
|
| 101 |
101 |
|
}
|
| 102 |
|
- |
}
|
| 103 |
102 |
|
|
| 104 |
103 |
|
// thread_id groups conversations: use in_reply_to if this is a reply,
|
| 105 |
104 |
|
// otherwise fall back to message_id (starts a new thread). This means
|
| 140 |
139 |
|
|
| 141 |
140 |
|
// Clear waiting status for emails that received replies
|
| 142 |
141 |
|
for reply_to_msg_id in &reply_to_ids {
|
| 143 |
|
- |
if let Ok(Some(original)) = email_repo.get_by_message_id(user_id, reply_to_msg_id).await {
|
| 144 |
|
- |
if original.waiting_for_response {
|
|
142 |
+ |
if let Ok(Some(original)) = email_repo.get_by_message_id(user_id, reply_to_msg_id).await
|
|
143 |
+ |
&& original.waiting_for_response {
|
| 145 |
144 |
|
let _ = email_repo.clear_waiting(original.id, user_id).await;
|
| 146 |
145 |
|
result.waiting_cleared += 1;
|
| 147 |
146 |
|
}
|
| 148 |
|
- |
}
|
| 149 |
147 |
|
}
|
| 150 |
148 |
|
|
| 151 |
149 |
|
Ok(result)
|
| 195 |
195 |
|
|
| 196 |
196 |
|
let (num_str, unit) = s.split_at(s.len() - 1);
|
| 197 |
197 |
|
let num: i64 = num_str.parse().ok()?;
|
| 198 |
|
- |
if num < 0 || num > MAX_RELATIVE_DATE_DAYS {
|
|
198 |
+ |
if !(0..=MAX_RELATIVE_DATE_DAYS).contains(&num) {
|
| 199 |
199 |
|
return None;
|
| 200 |
200 |
|
}
|
| 201 |
201 |
|
|
| 51 |
51 |
|
let local = dt.with_timezone(&tz);
|
| 52 |
52 |
|
let day = local.day();
|
| 53 |
53 |
|
let month_len = days_in_month(local.year(), local.month());
|
| 54 |
|
- |
if day == month_len && day >= 29 && day < 31 {
|
|
54 |
+ |
if day == month_len && (29..31).contains(&day) {
|
| 55 |
55 |
|
Some(31)
|
| 56 |
56 |
|
} else {
|
| 57 |
57 |
|
None
|
| 246 |
246 |
|
let target_day = {
|
| 247 |
247 |
|
let day = local.day();
|
| 248 |
248 |
|
let month_len = days_in_month(local.year(), local.month());
|
| 249 |
|
- |
if day == month_len && day >= 29 && day < 31 { Some(31) } else { None }
|
|
249 |
+ |
if day == month_len && (29..31).contains(&day) { Some(31) } else { None }
|
| 250 |
250 |
|
};
|
| 251 |
251 |
|
Some(add_months(base, interval as i32, target_day, tz))
|
| 252 |
252 |
|
}
|
| 260 |
260 |
|
/// `week`: 1-4 for ordinal, -1 for last.
|
| 261 |
261 |
|
/// `weekday`: 0=Mon..6=Sun.
|
| 262 |
262 |
|
/// `year`/`month`/`hour`/`minute`/`second` are civil fields in `tz`.
|
|
263 |
+ |
#[allow(clippy::too_many_arguments)] // civil date/time fields are clearer flat than boxed in a struct
|
| 263 |
264 |
|
fn nth_weekday_in_month(
|
| 264 |
265 |
|
year: i32, month: u32,
|
| 265 |
266 |
|
week: i8, weekday: u8,
|