Skip to main content

max / goingson

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>
Author: Max Johnson <me@maxj.phd> · 2026-06-22 02:10 UTC
Commit: da9c526384c6190af0244a1ea69d9a359955c102
Parent: f755200
29 files changed, +105 insertions, -140 deletions
@@ -95,11 +95,10 @@ pub async fn process_fetched_emails(
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,12 +139,11 @@ pub async fn process_fetched_emails(
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,7 +195,7 @@ fn parse_relative_date(s: &str, from: NaiveDate, time: NaiveTime) -> Option<Date
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,7 +51,7 @@ pub fn calculate_next_due_in_tz(
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,7 +246,7 @@ pub fn calculate_next_due_rich_in_tz(
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,6 +260,7 @@ pub fn calculate_next_due_rich_in_tz(
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,
@@ -128,11 +128,10 @@ pub fn parse_search_query(input: &str) -> ParsedSearchQuery {
128 128
129 129 // is: filter
130 130 if let Some(value) = strip_prefix_ci(token, "is:") {
131 - if let Some(filter) = IsFilter::parse(value) {
132 - if !result.is_filters.contains(&filter) {
131 + if let Some(filter) = IsFilter::parse(value)
132 + && !result.is_filters.contains(&filter) {
133 133 result.is_filters.push(filter);
134 134 }
135 - }
136 135 continue;
137 136 }
138 137
@@ -156,11 +155,10 @@ pub fn parse_search_query(input: &str) -> ParsedSearchQuery {
156 155
157 156 // type: filter
158 157 if let Some(value) = strip_prefix_ci(token, "type:") {
159 - if let Some(result_type) = parse_result_type(value) {
160 - if !result.result_types.contains(&result_type) {
158 + if let Some(result_type) = parse_result_type(value)
159 + && !result.result_types.contains(&result_type) {
161 160 result.result_types.push(result_type);
162 161 }
163 - }
164 162 continue;
165 163 }
166 164
@@ -120,11 +120,10 @@ impl Validate for UpdateTask {
120 120 impl Validate for NewEvent {
121 121 fn validate(&self) -> Result<(), CoreError> {
122 122 validate_required_string("title", &self.title, MAX_EVENT_TITLE_LENGTH)?;
123 - if let Some(end) = self.end_time {
124 - if end <= self.start_time {
123 + if let Some(end) = self.end_time
124 + && end <= self.start_time {
125 125 return Err(CoreError::validation("end_time", "must be after start_time"));
126 126 }
127 - }
128 127 Ok(())
129 128 }
130 129 }
@@ -133,11 +132,10 @@ impl Validate for NewEvent {
133 132 impl Validate for UpdateEvent {
134 133 fn validate(&self) -> Result<(), CoreError> {
135 134 validate_required_string("title", &self.title, MAX_EVENT_TITLE_LENGTH)?;
136 - if let Some(end) = self.end_time {
137 - if end <= self.start_time {
135 + if let Some(end) = self.end_time
136 + && end <= self.start_time {
138 137 return Err(CoreError::validation("end_time", "must be after start_time"));
139 138 }
140 - }
141 139 Ok(())
142 140 }
143 141 }
@@ -230,7 +230,7 @@ fn prune_old_backups(backup_dir: &std::path::Path, max_to_keep: usize) -> Result
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) {
@@ -120,11 +120,10 @@ pub async fn get_day_planning(
120 120 events.extend(expanded);
121 121 // Check if the original also falls on this day
122 122 let effective_end = r.end_time.unwrap_or(r.start_time + Duration::hours(1));
123 - if effective_end >= day_start && r.start_time <= day_end {
124 - if !existing_ids.contains(&r.id) {
123 + if effective_end >= day_start && r.start_time <= day_end
124 + && !existing_ids.contains(&r.id) {
125 125 events.push(r);
126 126 }
127 - }
128 127 }
129 128 }
130 129 events.sort_by_key(|e| e.start_time);
@@ -474,11 +474,10 @@ pub async fn cleanup_stale_temp_files() {
474 474 Err(_) => return,
475 475 };
476 476 while let Ok(Some(entry)) = entries.next_entry().await {
477 - if let Some(name) = entry.file_name().to_str() {
478 - if name.starts_with("goingson_email_") && name.ends_with(".html") {
477 + if let Some(name) = entry.file_name().to_str()
478 + && name.starts_with("goingson_email_") && name.ends_with(".html") {
479 479 let _ = tokio::fs::remove_file(entry.path()).await;
480 480 }
481 - }
482 481 }
483 482 }
484 483
@@ -525,7 +524,7 @@ pub async fn create_email(state: State<'_, Arc<AppState>>, input: EmailInput) ->
525 524 #[tauri::command]
526 525 #[instrument(skip_all)]
527 526 pub async fn save_email_draft(state: State<'_, Arc<AppState>>, input: DraftInput) -> Result<EmailResponse, ApiError> {
528 - let id = input.id.unwrap_or_else(EmailId::new);
527 + let id = input.id.unwrap_or_default();
529 528 let account_id = input.account_id;
530 529 let from = if let Some(aid) = account_id {
531 530 let acct = state.email_accounts.get_by_id(aid, DESKTOP_USER_ID).await?;
@@ -667,7 +666,7 @@ async fn send_email_inner(state: &Arc<AppState>, input: SendEmailInput) -> Resul
667 666 let message_id = if uses_jmap(&account) {
668 667 return Err(ApiError::bad_request("JMAP email sending not yet implemented - use IMAP account"));
669 668 } else if uses_oauth_imap(&account) {
670 - let access_token = get_valid_access_token(&state, &account).await?;
669 + let access_token = get_valid_access_token(state, &account).await?;
671 670 let smtp_client = SmtpClient::with_oauth(
672 671 &account.smtp_server,
673 672 account.smtp_port as u16,
@@ -775,8 +774,7 @@ async fn create_implicit_contacts(
775 774
776 775 // Derive display name from the local part of the email address
777 776 let display_name = addr.split('@').next().unwrap_or(&addr)
778 - .replace('.', " ")
779 - .replace('_', " ")
777 + .replace(['.', '_'], " ")
780 778 .split_whitespace()
781 779 .map(|w| {
782 780 let mut c = w.chars();
@@ -866,13 +864,11 @@ pub async fn move_email_to_folder(
866 864 let current_folder = email.source_folder.as_deref().unwrap_or("INBOX");
867 865
868 866 // Attempt IMAP move if the email has an account and UID
869 - if let (Some(account_id), Some(uid)) = (email.email_account_id, email.imap_uid) {
870 - if let Some((imap_client, _)) = build_imap_client(&state, account_id, "move_to_folder").await {
871 - if let Err(e) = imap_client.move_message(uid as u32, current_folder, &folder).await {
867 + if let (Some(account_id), Some(uid)) = (email.email_account_id, email.imap_uid)
868 + && let Some((imap_client, _)) = build_imap_client(&state, account_id, "move_to_folder").await
869 + && let Err(e) = imap_client.move_message(uid as u32, current_folder, &folder).await {
872 870 warn!("IMAP move failed (local update will proceed): {}", e);
873 871 }
874 - }
875 - }
876 872
877 873 // Always update locally
878 874 Ok(state.emails.update_source_folder(id, DESKTOP_USER_ID, &folder).await?)
@@ -913,13 +909,11 @@ pub async fn archive_email(state: State<'_, Arc<AppState>>, id: EmailId) -> Resu
913 909 .await?
914 910 .or_not_found("email", id)?;
915 911
916 - if let (Some(account_id), Some(imap_uid)) = (email.email_account_id, email.imap_uid) {
917 - if let Some((imap_client, archive_folder)) = build_imap_client(&state, account_id, "archive").await {
918 - if let Err(e) = imap_client.archive_message(imap_uid as u32, &archive_folder).await {
912 + if let (Some(account_id), Some(imap_uid)) = (email.email_account_id, email.imap_uid)
913 + && let Some((imap_client, archive_folder)) = build_imap_client(&state, account_id, "archive").await
914 + && let Err(e) = imap_client.archive_message(imap_uid as u32, &archive_folder).await {
919 915 warn!("Failed to archive on IMAP server: {}", e);
920 916 }
921 - }
922 - }
923 917
924 918 Ok(state.emails.archive(id, DESKTOP_USER_ID).await?)
925 919 }
@@ -936,13 +930,11 @@ pub async fn unarchive_email(state: State<'_, Arc<AppState>>, id: EmailId) -> Re
936 930 .await?
937 931 .or_not_found("email", id)?;
938 932
939 - if let (Some(account_id), Some(imap_uid)) = (email.email_account_id, email.imap_uid) {
940 - if let Some((imap_client, archive_folder)) = build_imap_client(&state, account_id, "unarchive").await {
941 - if let Err(e) = imap_client.unarchive_message(imap_uid as u32, &archive_folder).await {
933 + if let (Some(account_id), Some(imap_uid)) = (email.email_account_id, email.imap_uid)
934 + && let Some((imap_client, archive_folder)) = build_imap_client(&state, account_id, "unarchive").await
935 + && let Err(e) = imap_client.unarchive_message(imap_uid as u32, &archive_folder).await {
942 936 warn!("Failed to unarchive on IMAP server: {}", e);
943 937 }
944 - }
945 - }
946 938
947 939 Ok(state.emails.unarchive(id, DESKTOP_USER_ID).await?)
948 940 }
@@ -257,11 +257,10 @@ pub async fn create_email_account(state: State<'_, Arc<AppState>>, input: EmailA
257 257 return Err(ApiError::validation("smtpServer", "SMTP server is required"));
258 258 }
259 259
260 - if let Some(ref folder) = input.archive_folder_name {
261 - if folder.contains('\r') || folder.contains('\n') || folder.chars().any(|c| c.is_control()) {
260 + if let Some(ref folder) = input.archive_folder_name
261 + && (folder.contains('\r') || folder.contains('\n') || folder.chars().any(|c| c.is_control())) {
262 262 return Err(ApiError::validation("archiveFolderName", "Folder name contains invalid characters"));
263 263 }
264 - }
265 264
266 265 // Never write the password to the DB column; store it in the OS keychain
267 266 // and leave the column empty (mirrors the OAuth token path).
@@ -301,11 +300,10 @@ pub async fn update_email_account(state: State<'_, Arc<AppState>>, id: EmailAcco
301 300 return Err(ApiError::validation("emailAddress", "Invalid email address"));
302 301 }
303 302
304 - if let Some(ref folder) = input.archive_folder_name {
305 - if folder.contains('\r') || folder.contains('\n') || folder.chars().any(|c| c.is_control()) {
303 + if let Some(ref folder) = input.archive_folder_name
304 + && (folder.contains('\r') || folder.contains('\n') || folder.chars().any(|c| c.is_control())) {
306 305 return Err(ApiError::validation("archiveFolderName", "Folder name contains invalid characters"));
307 306 }
308 - }
309 307
310 308 // Route a changed password through the keychain. Pass `Some("")` to the
311 309 // repo so any stale plaintext in the column is cleared; `None` leaves the
@@ -171,11 +171,10 @@ async fn sync_imap_account_inner(
171 171
172 172 // --- Update sync states ---
173 173 if let Some(validity) = inbox_result.uid_validity {
174 - if let Some(ref prev) = inbox_sync_state {
175 - if prev.uid_validity != validity {
174 + if let Some(ref prev) = inbox_sync_state
175 + && prev.uid_validity != validity {
176 176 state.email_accounts.delete_folder_sync_state(id, "INBOX").await.ok();
177 177 }
178 - }
179 178 let new_max = inbox_result.max_uid_fetched
180 179 .unwrap_or_else(|| inbox_sync_state.as_ref().map_or(0, |s| s.last_seen_uid));
181 180 if new_max > 0 {
@@ -184,11 +183,10 @@ async fn sync_imap_account_inner(
184 183 }
185 184
186 185 if let Some(validity) = archive_uid_validity {
187 - if let Some(ref prev) = archive_sync_state {
188 - if prev.uid_validity != validity {
186 + if let Some(ref prev) = archive_sync_state
187 + && prev.uid_validity != validity {
189 188 state.email_accounts.delete_folder_sync_state(id, &archive_folder).await.ok();
190 189 }
191 - }
192 190 let new_max = archive_max_uid
193 191 .unwrap_or_else(|| archive_sync_state.as_ref().map_or(0, |s| s.last_seen_uid));
194 192 if new_max > 0 {
@@ -470,10 +468,9 @@ fn write_pending_blobs(blobs_dir: &Path, pending: Vec<(String, Vec<u8>)>) {
470 468 }
471 469 for (hash, data) in pending {
472 470 let blob_path = blobs_dir.join(&hash);
473 - if !blob_path.exists() {
474 - if let Err(e) = std::fs::write(&blob_path, &data) {
471 + if !blob_path.exists()
472 + && let Err(e) = std::fs::write(&blob_path, &data) {
475 473 warn!(blob_hash = %hash, "Failed to write attachment blob: {}", e);
476 474 }
477 - }
478 475 }
479 476 }
@@ -331,11 +331,10 @@ pub async fn create_event(state: State<'_, Arc<AppState>>, input: EventInput) ->
331 331 }
332 332
333 333 // Validate end_time > start_time if end_time is provided
334 - if let Some(end_time) = input.end_time {
335 - if end_time <= input.start_time {
334 + if let Some(end_time) = input.end_time
335 + && end_time <= input.start_time {
336 336 return Err(ApiError::validation("endTime", "End time must be after start time"));
337 337 }
338 - }
339 338
340 339 let recurrence = input.recurrence.as_deref().map(Recurrence::from_str_or_default).unwrap_or(Recurrence::None);
341 340 let block_type = input.block_type.as_deref().and_then(BlockType::from_str_opt);
@@ -318,7 +318,7 @@ pub async fn list_backups(app: tauri::AppHandle) -> Result<Vec<BackupInfoRespons
318 318 }
319 319
320 320 // Sort by creation time, newest first
321 - backups.sort_by(|a, b| b.created_at.cmp(&a.created_at));
321 + backups.sort_by_key(|b| std::cmp::Reverse(b.created_at));
322 322
323 323 Ok(backups)
324 324 }
@@ -435,7 +435,7 @@ pub async fn delete_backup(
435 435 }
436 436
437 437 // Verify it's actually a backup file
438 - if !canonical_path.extension().is_some_and(|ext| ext == "gz")
438 + if canonical_path.extension().is_none_or(|ext| ext != "gz")
439 439 || !canonical_path.to_string_lossy().ends_with(".json.gz")
440 440 {
441 441 return Err(ApiError::bad_request(
@@ -303,8 +303,8 @@ pub async fn import_ics(
303 303 }
304 304
305 305 // Dedup by UID (real or synthetic)
306 - if let Some(ref uid) = parsed.external_id {
307 - if let Ok(Some(_)) = state
306 + if let Some(ref uid) = parsed.external_id
307 + && let Ok(Some(_)) = state
308 308 .events
309 309 .find_by_external_id("ics", uid, DESKTOP_USER_ID)
310 310 .await
@@ -312,7 +312,6 @@ pub async fn import_ics(
312 312 skipped += 1;
313 313 continue;
314 314 }
315 - }
316 315
317 316 let new_event = NewEvent {
318 317 user_id: Some(DESKTOP_USER_ID),
@@ -172,8 +172,8 @@ pub fn get_snooze_options() -> SnoozeOptionsResponse {
172 172 Some(now + Duration::hours(3))
173 173 };
174 174
175 - if let Some(lt) = later_today {
176 - if lt > now {
175 + if let Some(lt) = later_today
176 + && lt > now {
177 177 let utc_time = lt.with_timezone(&Utc);
178 178 options.push(SnoozeOption {
179 179 key: "laterToday".to_string(),
@@ -182,7 +182,6 @@ pub fn get_snooze_options() -> SnoozeOptionsResponse {
182 182 formatted: format_snooze_time(&lt),
183 183 });
184 184 }
185 - }
186 185
187 186 // Tomorrow 9am
188 187 let tomorrow = today + Duration::days(1);
@@ -626,15 +626,14 @@ pub async fn complete_task(state: State<'_, Arc<AppState>>, id: TaskId) -> Resul
626 626 // check naturally prevents auto-complete when a task recurs.
627 627 if let Some(milestone_id) = task.milestone_id {
628 628 let remaining = state.tasks.count_incomplete_by_milestone(milestone_id, DESKTOP_USER_ID).await?;
629 - if remaining == 0 {
630 - if let Some(ms) = state.milestones.get_by_id(milestone_id, DESKTOP_USER_ID).await? {
629 + if remaining == 0
630 + && let Some(ms) = state.milestones.get_by_id(milestone_id, DESKTOP_USER_ID).await? {
631 631 state.milestones.update(
632 632 milestone_id, DESKTOP_USER_ID,
633 633 &ms.name, &ms.description, ms.target_date,
634 634 &MilestoneStatus::Completed,
635 635 ).await?;
636 636 }
637 - }
638 637 }
639 638
640 639 Ok(CompleteTaskResponse {
@@ -147,6 +147,8 @@ pub async fn open_external_url(
147 147 #[cfg(not(any(target_os = "ios", target_os = "android")))]
148 148 {
149 149 use tauri_plugin_shell::ShellExt;
150 + // TODO: migrate to tauri-plugin-opener; shell().open is deprecated.
151 + #[allow(deprecated)]
150 152 app.shell()
151 153 .open(trimmed.to_string(), None)
152 154 .map_api_err("Failed to open URL", ApiError::internal)?;
@@ -208,7 +208,7 @@ impl ImapClient {
208 208 // Build a set of UIDs that are safe to fetch
209 209 while let Some(result) = size_stream.next().await {
210 210 if let Ok(msg) = result {
211 - let over_limit = msg.size.map_or(false, |s| s > MAX_EMAIL_SIZE);
211 + let over_limit = msg.size.is_some_and(|s| s > MAX_EMAIL_SIZE);
212 212 if over_limit {
213 213 skipped_large += 1;
214 214 tracing::warn!(uid = ?msg.uid, size = ?msg.size, folder = %folder, "Skipping oversized email");
@@ -383,18 +383,16 @@ impl ImapClient {
383 383 let mut safe_uids: Vec<u32> = Vec::new();
384 384 let mut skipped_large = 0usize;
385 385 while let Some(result) = size_stream.next().await {
386 - if let Ok(msg) = result {
387 - if let Some(uid) = msg.uid {
388 - if let Some(size) = msg.size {
389 - if size > MAX_EMAIL_SIZE {
386 + if let Ok(msg) = result
387 + && let Some(uid) = msg.uid {
388 + if let Some(size) = msg.size
389 + && size > MAX_EMAIL_SIZE {
390 390 skipped_large += 1;
391 391 tracing::warn!(uid, size, folder = %folder, "Skipping oversized email ({} bytes)", size);
392 392 continue;
393 393 }
394 - }
395 394 safe_uids.push(uid);
396 395 }
397 - }
398 396 }
399 397 drop(size_stream);
400 398
@@ -172,5 +172,5 @@ fn rand_skip(backoff: u32) -> bool {
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 }
@@ -121,8 +121,8 @@ fn parse_datetime_property(properties: &[Property], name: &str) -> Option<DateTi
121 121 });
122 122
123 123 // Try parsing with timezone
124 - if let Some(tz_name) = tzid {
125 - if let Some(ndt) = parse_ical_datetime(value) {
124 + if let Some(tz_name) = tzid
125 + && let Some(ndt) = parse_ical_datetime(value) {
126 126 // Resolve IANA timezone and convert local time to UTC
127 127 if let Ok(tz) = tz_name.parse::<Tz>() {
128 128 // .earliest() returns None for times in a DST spring-forward gap.
@@ -140,7 +140,6 @@ fn parse_datetime_property(properties: &[Property], name: &str) -> Option<DateTi
140 140 // intent than a blind UTC stamp).
141 141 return naive_local_to_utc(&ndt);
142 142 }
143 - }
144 143
145 144 // UTC (ends with Z)
146 145 if let Some(clean) = value.strip_suffix('Z') {
@@ -179,7 +178,7 @@ fn parse_ical_datetime(s: &str) -> Option<NaiveDateTime> {
179 178 let hour: u32 = time_part.get(0..2)?.parse().ok()?;
180 179 let min: u32 = time_part.get(2..4)?.parse().ok()?;
181 180 let sec: u32 = time_part.get(4..6)?.parse().ok()?;
182 - Some(date.and_hms_opt(hour, min, sec)?)
181 + date.and_hms_opt(hour, min, sec)
183 182 }
184 183
185 184 /// Parse a DATE-only value to midnight UTC.
@@ -264,11 +263,10 @@ fn parse_rrule(rule: &str) -> Recurrence {
264 263 }
265 264
266 265 // Complex rules with BYDAY having multiple days → skip
267 - if let Some(byday) = parts.get("BYDAY") {
268 - if byday.contains(',') {
266 + if let Some(byday) = parts.get("BYDAY")
267 + && byday.contains(',') {
269 268 return Recurrence::None;
270 269 }
271 - }
272 270
273 271 // UNTIL or COUNT → still map the frequency (they just limit recurrence)
274 272 match freq.as_str() {
@@ -73,11 +73,10 @@ pub fn parse_vcf(content: &str) -> Result<Vec<ParsedVCard>, String> {
73 73 in_card = true;
74 74 lines.clear();
75 75 } else if trimmed.eq_ignore_ascii_case("END:VCARD") {
76 - if in_card {
77 - if let Some(card) = parse_single_vcard(&lines) {
76 + if in_card
77 + && let Some(card) = parse_single_vcard(&lines) {
78 78 contacts.push(card);
79 79 }
80 - }
81 80 in_card = false;
82 81 } else if in_card {
83 82 lines.push(line);
@@ -378,8 +377,8 @@ fn decode_quoted_printable(input: &str) -> String {
378 377 continue;
379 378 }
380 379 // Hex-encoded byte: =XX
381 - if i + 2 < bytes.len() {
382 - if let (Some(h), Some(l)) = (
380 + if i + 2 < bytes.len()
381 + && let (Some(h), Some(l)) = (
383 382 hex_val(bytes[i + 1]),
384 383 hex_val(bytes[i + 2]),
385 384 ) {
@@ -387,7 +386,6 @@ fn decode_quoted_printable(input: &str) -> String {
387 386 i += 3;
388 387 continue;
389 388 }
390 - }
391 389 }
392 390 decoded_bytes.push(bytes[i]);
393 391 i += 1;