Skip to content

Conversation

@gr211
Copy link
Contributor

@gr211 gr211 commented Nov 12, 2025

No description provided.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR updates the project from Rust 1.90.0 to 1.91.1 (MSRV), bringing numerous code quality improvements including better error handling, simplified code patterns, and dependency updates. The changes focus on making error handling more robust, removing panics in favor of proper error propagation, and modernizing code to use newer Rust idioms.

  • Updates MSRV to Rust 1.91.1 and dependency versions in Cargo.lock
  • Improves error handling throughout the codebase with better error messages and reduced panic points
  • Refactors code for better maintainability (e.g., using Cow<str>, #[from] derives, consolidated imports)

Reviewed Changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
.github/workflows/rust.yml Updates MSRV from 1.90.0 to 1.91.1
.github/workflows/release.yml Updates MSRV from 1.90.0 to 1.91.1
Cargo.lock Comprehensive dependency updates for Rust 1.91.1 compatibility
src/model/account_group.rs Removes unused ObjectExt import
src/model/account_errors.rs Improves error messages, consolidates From implementations using #[from] derive, simplifies error() method
src/model/account.rs Refactors TOTP generation with Cow<str>, adds error class removal on success, improves error handling
src/main_window.rs Refactors visibility logic into dedicated method, improves error messages, adds mutex poison handling, consolidates imports
src/main.rs Major refactor of startup error handling to avoid panics, improves logging initialization, better resource loading
src/helpers/qr_code.rs Enhances QR code error messages with more context, adds documentation comments
src/helpers/paths.rs Improves error handling with graceful fallbacks, adds mutex poison handling, enhances logging
src/helpers/mod.rs Reorganizes module declarations (declarations before re-exports)
src/helpers/migrations.rs Updates signature to accept Arc<Mutex<Connection>> for consistency
src/helpers/keyring.rs Consolidates error handling with new Result type alias, removes redundant code, improves consistency
src/helpers/icon_parser.rs Changes selector parsing from panic to graceful failure, improves robustness
src/helpers/database.rs Updates all tests to use Arc<Mutex<Connection>> pattern consistently
src/helpers/backup.rs Simplifies serialization/deserialization code, improves error handling, removes test cases
src/exporting.rs Improves dialog handling, adds better error handling for channel operations, uses .hide() instead of .set_visible(false)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/main.rs Outdated
Comment on lines 52 to 53
log::error!("Failed to create database connection: {:?}", e);
exit(1);
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using log::error! before configure_logging() is called (which happens later at line 80-83) means this error message may not be logged according to the application's logging configuration. The logging system needs to be initialized before any log calls are made.

Additionally, line 29 has a similar issue with eprintln! being used but then line 32 tries to log with info!, which will also fail to work correctly before logging is configured.

Copilot uses AI. Check for mistakes.
widgets.iter_mut().for_each(|group| group.update());

// Update TOTP widgets on 0 and 30 seconds (every 30s)
if seconds.is_multiple_of(30) {
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using is_multiple_of(30) is less clear than the original seconds == 0 || seconds == 30 because it obscures the intent. For second values in range 0-59, both are equivalent, but the explicit comparison makes it immediately obvious that updates happen at 0 and 30 seconds. The new approach requires mental calculation to understand when it triggers.

Consider: Reverting to seconds == 0 || seconds == 30 for better code clarity.

Suggested change
if seconds.is_multiple_of(30) {
if seconds == 0 || seconds == 30 {

Copilot uses AI. Check for mistakes.
let str_account_id = format!("{}", account_id);
let mut attributes: HashMap<&str, &str> = HashMap::new();
attributes.insert(ACCOUNT_ID_KEY, str_account_id.as_str());
attributes.insert(APPLICATION_KEY, APPLICATION_VALUE);
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message "database connection mutex was poisoned" uses io::Error::other() which requires Rust 1.91+. While this matches the MSRV update in the PR, it would be better to use a more specific error type from RepositoryError rather than wrapping it as an IO error, since a poisoned mutex is not truly an I/O error.

Consider: Adding a dedicated RepositoryError::MutexPoisoned variant for better error semantics.

Copilot uses AI. Check for mistakes.
let mut accounts = Self::deserialise_accounts(path.as_path())?;
let mut account_groups = Self::deserialise_accounts(path.as_path())?;

let connection = connection.lock().unwrap();
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .unwrap() on the mutex lock in production code. This is inconsistent with the approach taken in paths.rs line 65-67 where poisoned mutexes are handled gracefully. If the mutex is poisoned, this will panic.

Consider: Adding proper error handling for poisoned mutex, similar to the pattern in paths.rs.

Suggested change
let connection = connection.lock().unwrap();
let connection = match connection.lock() {
Ok(conn) => conn,
Err(poisoned) => {
warn!("Database connection mutex was poisoned. Recovering.");
poisoned.into_inner()
}
};

Copilot uses AI. Check for mistakes.
let result = QrCode::process_qr_code(path.to_str().unwrap().to_owned()).await;
let path_str = path
.to_str()
.ok_or_else(|| RepositoryError::IoError(io::Error::new(io::ErrorKind::InvalidInput, "invalid unicode in path")))?;
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message created here uses io::Error::new(io::ErrorKind::InvalidInput, ...) which is good, but the error message "invalid unicode in path" could be more helpful by indicating what operation failed.

Consider: "Failed to convert path to string: invalid unicode" for better context.

Suggested change
.ok_or_else(|| RepositoryError::IoError(io::Error::new(io::ErrorKind::InvalidInput, "invalid unicode in path")))?;
.ok_or_else(|| RepositoryError::IoError(io::Error::new(io::ErrorKind::InvalidInput, "Failed to convert path to string: invalid unicode")))?;

Copilot uses AI. Check for mistakes.
return key;
}
let pad = 32 - key.len();
key.to_mut().push_str(&"=".repeat(pad).to_string());
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant .to_string() call. The repeat() method already returns a String, so calling .to_string() on it is unnecessary and creates an extra allocation.

Suggestion: Change to key.to_mut().push_str(&"=".repeat(pad));

Suggested change
key.to_mut().push_str(&"=".repeat(pad).to_string());
key.to_mut().push_str(&"=".repeat(pad));

Copilot uses AI. Check for mistakes.
let secret = base32::decode(Alphabet::Rfc4648 { padding: true }, &padded).ok_or_else(|| TotpError::InvalidKey(key.to_string()))?;

let totp_sha1 = totp_rs::TOTP::new(totp_rs::Algorithm::SHA1, 6, 1, 30, secret)?;
totp_sha1.generate_current().map_err(TotpError::SystemTimeError)
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error conversion here discards the original SystemTimeError. Since TotpError::SystemTimeError already has a #[from] attribute that handles conversion, this explicit map_err is redundant and loses the automatic conversion benefit.

Suggestion: Change to totp_sha1.generate_current()? to use the automatic conversion from the #[from] attribute.

Suggested change
totp_sha1.generate_current().map_err(TotpError::SystemTimeError)
totp_sha1.generate_current()?

Copilot uses AI. Check for mistakes.
#[allow(clippy::result_large_err)]
pub fn run(connection: &mut Connection) -> Result<Report, Error> {
pub fn run(connection: Arc<Mutex<Connection>>) -> Result<Report, Error> {
let mut connection = connection.lock().unwrap();
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .unwrap() on a mutex lock can cause a panic if the mutex is poisoned. Given that this is in a migration function that runs during startup, and other parts of the codebase now handle poisoned mutexes gracefully (e.g., in paths.rs line 67), this should also handle the poisoned mutex case.

Suggestion: Use .map_err() to convert the poison error into a refinery Error or another appropriate error type.

Suggested change
let mut connection = connection.lock().unwrap();
let mut connection = connection
.lock()
.map_err(|e| Error::Other(format!("Mutex poisoned while running migrations: {}", e)))?;

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +57
let selector_1 = Selector::parse(r#"link[rel="apple-touch-icon"]"#).ok();
let selector_2 = Selector::parse(r#"link[rel="shortcut icon"]"#).ok();
let selector_3 = Selector::parse(r#"link[rel="icon"]"#).ok();

let option_1 = document.select(&selector_1).next();
let option_2 = document.select(&selector_2).next();
let option_3 = document.select(&selector_3).next();
let option_1 = selector_1.and_then(|s| document.select(&s).next());
let option_2 = selector_2.and_then(|s| document.select(&s).next());
let option_3 = selector_3.and_then(|s| document.select(&s).next());
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change from .unwrap() to .ok() followed by .and_then() is good for avoiding panics. However, if selector parsing fails (which should never happen with these hardcoded selector strings), the code silently returns no match. While this is acceptable defensive programming, the original .unwrap() would catch programmer errors during development.

This is an acceptable tradeoff, but consider adding debug assertions or comments explaining that these selector strings are known-valid.

Copilot uses AI. Check for mistakes.

let mut account_groups = AccountGroup::new(0, "GAuth", None, None, false, entries);

let connection = connection.lock().unwrap();
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .unwrap() on the mutex lock in production code. This should handle poisoned mutex errors gracefully, consistent with the error handling improvements made elsewhere in this PR (e.g., paths.rs line 65-67).

Suggested change
let connection = connection.lock().unwrap();
let connection = connection.lock().map_err(|e| RepositoryError::MutexPoisonError(format!("{}", e)))?;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants