chore: update to rust 1.88 (#2815)

* chore: update to rust 1.88

* clippy + fmt

* update ci version

* update flake
This commit is contained in:
Ellie Huxtable
2025-07-22 16:03:20 +02:00
committed by GitHub
parent 875e339189
commit e7819d258a
24 changed files with 83 additions and 86 deletions

View File

@ -26,7 +26,7 @@ jobs:
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.86 toolchain: 1.88
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
@ -97,7 +97,7 @@ jobs:
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.86 toolchain: 1.88
- uses: taiki-e/install-action@v2 - uses: taiki-e/install-action@v2
name: Install nextest name: Install nextest
@ -127,7 +127,7 @@ jobs:
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.86 toolchain: 1.88
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
@ -171,7 +171,7 @@ jobs:
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.86 toolchain: 1.88
- uses: taiki-e/install-action@v2 - uses: taiki-e/install-action@v2
name: Install nextest name: Install nextest
@ -200,7 +200,7 @@ jobs:
- name: Install latest rust - name: Install latest rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.86 toolchain: 1.88
components: clippy components: clippy
- uses: actions/cache@v4 - uses: actions/cache@v4
@ -223,7 +223,7 @@ jobs:
- name: Install latest rust - name: Install latest rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.86 toolchain: 1.88
components: rustfmt components: rustfmt
- name: Format - name: Format

View File

@ -7,7 +7,7 @@ exclude = ["ui/backend"]
[workspace.package] [workspace.package]
version = "18.7.1" version = "18.7.1"
authors = ["Ellie Huxtable <ellie@atuin.sh>"] authors = ["Ellie Huxtable <ellie@atuin.sh>"]
rust-version = "1.86" rust-version = "1.88"
license = "MIT" license = "MIT"
homepage = "https://atuin.sh" homepage = "https://atuin.sh"
repository = "https://github.com/atuinsh/atuin" repository = "https://github.com/atuinsh/atuin"

View File

@ -41,7 +41,7 @@ fn make_url(address: &str, path: &str) -> Result<String> {
let address = if address.ends_with("/") { let address = if address.ends_with("/") {
address address
} else { } else {
&format!("{}/", address) &format!("{address}/")
}; };
// passing a path with a leading `/` will cause `join()` to replace the entire URL path // passing a path with a leading `/` will cause `join()` to replace the entire URL path
@ -148,8 +148,8 @@ pub fn ensure_version(response: &Response) -> Result<bool> {
println!( println!(
"Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin" "Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin"
); );
println!("Client: {}", ATUIN_CARGO_VERSION); println!("Client: {ATUIN_CARGO_VERSION}");
println!("Server: {}", version); println!("Server: {version}");
return Ok(false); return Ok(false);
} }

View File

@ -300,7 +300,7 @@ mod test {
// test decryption works // test decryption works
// this should pass // this should pass
match decrypt(e1, &key1) { match decrypt(e1, &key1) {
Err(e) => panic!("failed to decrypt, got {}", e), Err(e) => panic!("failed to decrypt, got {e}"),
Ok(h) => assert_eq!(h, history), Ok(h) => assert_eq!(h, history),
}; };

View File

@ -111,11 +111,11 @@ impl<'a> From<&'a [u8]> for LineType<'a> {
if line.is_empty() { if line.is_empty() {
return LineType::Empty; return LineType::Empty;
} }
let parsed = match try_parse_line_as_timestamp(line) {
match try_parse_line_as_timestamp(line) {
Some(time) => LineType::Timestamp(time), Some(time) => LineType::Timestamp(time),
None => LineType::Command(line), None => LineType::Command(line),
}; }
parsed
} }
} }

View File

@ -198,7 +198,7 @@ fn from_string(name: &str) -> Result<Color, String> {
// For full flexibility, we need to use serde_json, given // For full flexibility, we need to use serde_json, given
// crossterm's approach. // crossterm's approach.
serde_json::from_str::<Color>(format!("\"{}\"", &name[1..]).as_str()) serde_json::from_str::<Color>(format!("\"{}\"", &name[1..]).as_str())
.map_err(|_| format!("Could not convert color name {} to Crossterm color", name)) .map_err(|_| format!("Could not convert color name {name} to Crossterm color"))
} }
_ => { _ => {
let srgb = named::from_str(name).ok_or("No such color in palette")?; let srgb = named::from_str(name).ok_or("No such color in palette")?;
@ -382,7 +382,7 @@ impl ThemeManager {
theme_file theme_file
}; };
let theme_toml = format!["{}.toml", name]; let theme_toml = format!["{name}.toml"];
theme_file.push(theme_toml); theme_file.push(theme_toml);
let mut config_builder = Config::builder(); let mut config_builder = Config::builder();
@ -797,8 +797,7 @@ mod theme_tests {
assert_eq!( assert_eq!(
from_string(inp), from_string(inp),
Err(format!( Err(format!(
"Could not convert color name {} to Crossterm color", "Could not convert color name {inp} to Crossterm color"
inp
)) ))
); );
}); });

View File

@ -31,7 +31,7 @@ impl std::fmt::Display for Shell {
Shell::Unknown => "unknown", Shell::Unknown => "unknown",
}; };
write!(f, "{}", shell) write!(f, "{shell}")
} }
} }

View File

@ -46,7 +46,7 @@ pub async fn alias_config(store: &AliasStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate aliases: {}'", e); return format!("echo 'Atuin: failed to generate aliases: {e}'");
} }
cached_aliases(aliases, store).await cached_aliases(aliases, store).await
@ -61,7 +61,7 @@ pub async fn var_config(store: &VarStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate vars: {}'", e); return format!("echo 'Atuin: failed to generate vars: {e}'");
} }
cached_vars(vars, store).await cached_vars(vars, store).await

View File

@ -47,7 +47,7 @@ pub async fn alias_config(store: &AliasStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate aliases: {}'", e); return format!("echo 'Atuin: failed to generate aliases: {e}'");
} }
cached_aliases(aliases, store).await cached_aliases(aliases, store).await
@ -62,7 +62,7 @@ pub async fn var_config(store: &VarStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate vars: {}'", e); return format!("echo 'Atuin: failed to generate vars: {e}'");
} }
cached_vars(vars, store).await cached_vars(vars, store).await

View File

@ -46,7 +46,7 @@ pub async fn alias_config(store: &AliasStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate aliases: {}'", e); return format!("echo 'Atuin: failed to generate aliases: {e}'");
} }
cached_aliases(aliases, store).await cached_aliases(aliases, store).await
@ -61,7 +61,7 @@ pub async fn var_config(store: &VarStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate vars: {}'", e); return format!("echo 'Atuin: failed to generate vars: {e}'");
} }
cached_vars(vars, store).await cached_vars(vars, store).await

View File

@ -46,7 +46,7 @@ pub async fn alias_config(store: &AliasStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate aliases: {}'", e); return format!("echo 'Atuin: failed to generate aliases: {e}'");
} }
cached_aliases(aliases, store).await cached_aliases(aliases, store).await
@ -61,7 +61,7 @@ pub async fn var_config(store: &VarStore) -> String {
} }
if let Err(e) = store.build().await { if let Err(e) = store.build().await {
return format!("echo 'Atuin: failed to generate aliases: {}'", e); return format!("echo 'Atuin: failed to generate aliases: {e}'");
} }
cached_vars(vars, store).await cached_vars(vars, store).await

View File

@ -324,7 +324,7 @@ mod tests {
.into(); .into();
let stats = compute(&settings, &[history], 10, 1).expect("failed to compute stats"); let stats = compute(&settings, &[history], 10, 1).expect("failed to compute stats");
assert_eq!(stats.top.get(0).unwrap().0, vec!["echo"]); assert_eq!(stats.top.first().unwrap().0, vec!["echo"]);
} }
#[test] #[test]

View File

@ -199,10 +199,7 @@ mod tests {
let ns_list = store.list(None).await.unwrap(); let ns_list = store.list(None).await.unwrap();
assert_eq!(ns_list, expected); assert_eq!(ns_list, expected);
store store.delete("test", &["key".to_string()]).await.unwrap();
.delete("test", &vec!["key".to_string()])
.await
.unwrap();
let value = store.get("test", "key").await.unwrap(); let value = store.get("test", "key").await.unwrap();
assert_eq!(value, None); assert_eq!(value, None);

View File

@ -13,11 +13,11 @@ use tracing::debug;
pub fn build_executable_script(script: String, shebang: String) -> String { pub fn build_executable_script(script: String, shebang: String) -> String {
if shebang.is_empty() { if shebang.is_empty() {
// Default to bash if no shebang is provided // Default to bash if no shebang is provided
format!("#!/usr/bin/env bash\n{}", script) format!("#!/usr/bin/env bash\n{script}")
} else if script.starts_with("#!") { } else if script.starts_with("#!") {
format!("{}\n{}", shebang, script) format!("{shebang}\n{script}")
} else { } else {
format!("#!{}\n{}", shebang, script) format!("#!{shebang}\n{script}")
} }
} }
@ -149,7 +149,7 @@ pub async fn execute_script_interactive(
let mut child = match child_result { let mut child = match child_result {
Ok(child) => child, Ok(child) => child,
Err(e) => { Err(e) => {
return Err(format!("Failed to execute script: {}", e).into()); return Err(format!("Failed to execute script: {e}").into());
} }
}; };
@ -176,11 +176,11 @@ pub async fn execute_script_interactive(
tokio::spawn(async move { tokio::spawn(async move {
while let Some(input) = stdin_rx.recv().await { while let Some(input) = stdin_rx.recv().await {
if let Err(e) = stdin.write_all(input.as_bytes()).await { if let Err(e) = stdin.write_all(input.as_bytes()).await {
eprintln!("Error writing to stdin: {}", e); eprintln!("Error writing to stdin: {e}");
break; break;
} }
if let Err(e) = stdin.flush().await { if let Err(e) = stdin.flush().await {
eprintln!("Error flushing stdin: {}", e); eprintln!("Error flushing stdin: {e}");
break; break;
} }
} }
@ -199,16 +199,16 @@ pub async fn execute_script_interactive(
Ok(0) => break, // End of stdout Ok(0) => break, // End of stdout
Ok(n) => { Ok(n) => {
if let Err(e) = stdout_writer.write_all(&buffer[0..n]).await { if let Err(e) = stdout_writer.write_all(&buffer[0..n]).await {
eprintln!("Error writing to stdout: {}", e); eprintln!("Error writing to stdout: {e}");
break; break;
} }
if let Err(e) = stdout_writer.flush().await { if let Err(e) = stdout_writer.flush().await {
eprintln!("Error flushing stdout: {}", e); eprintln!("Error flushing stdout: {e}");
break; break;
} }
} }
Err(e) => { Err(e) => {
eprintln!("Error reading from process stdout: {}", e); eprintln!("Error reading from process stdout: {e}");
break; break;
} }
} }
@ -227,16 +227,16 @@ pub async fn execute_script_interactive(
Ok(0) => break, // End of stderr Ok(0) => break, // End of stderr
Ok(n) => { Ok(n) => {
if let Err(e) = stderr_writer.write_all(&buffer[0..n]).await { if let Err(e) = stderr_writer.write_all(&buffer[0..n]).await {
eprintln!("Error writing to stderr: {}", e); eprintln!("Error writing to stderr: {e}");
break; break;
} }
if let Err(e) = stderr_writer.flush().await { if let Err(e) = stderr_writer.flush().await {
eprintln!("Error flushing stderr: {}", e); eprintln!("Error flushing stderr: {e}");
break; break;
} }
} }
Err(e) => { Err(e) => {
eprintln!("Error reading from process stderr: {}", e); eprintln!("Error reading from process stderr: {e}");
break; break;
} }
} }
@ -257,7 +257,7 @@ pub async fn execute_script_interactive(
status status
} }
Err(e) => { Err(e) => {
eprintln!("Error waiting for child process: {}", e); eprintln!("Error waiting for child process: {e}");
// Send a default error code // Send a default error code
let _ = exit_code_tx.send(-1).await; let _ = exit_code_tx.send(-1).await;
return; return;
@ -266,11 +266,11 @@ pub async fn execute_script_interactive(
// Wait for stdout/stderr tasks to complete // Wait for stdout/stderr tasks to complete
if let Err(e) = stdout_handle.await { if let Err(e) = stdout_handle.await {
eprintln!("Error joining stdout task: {}", e); eprintln!("Error joining stdout task: {e}");
} }
if let Err(e) = stderr_handle.await { if let Err(e) = stderr_handle.await {
eprintln!("Error joining stderr task: {}", e); eprintln!("Error joining stderr task: {e}");
} }
// Send the exit code // Send the exit code

View File

@ -55,8 +55,7 @@ impl Database for Postgres {
if pg_major_version < MIN_PG_VERSION { if pg_major_version < MIN_PG_VERSION {
return Err(DbError::Other(eyre::Report::msg(format!( return Err(DbError::Other(eyre::Report::msg(format!(
"unsupported PostgreSQL version {}, minimum required is {}", "unsupported PostgreSQL version {pg_major_version}, minimum required is {MIN_PG_VERSION}"
pg_major_version, MIN_PG_VERSION
)))); ))));
} }

View File

@ -229,8 +229,7 @@ pub async fn send_verification<DB: Database>(
.subject(settings.mail.verification.subject) .subject(settings.mail.verification.subject)
.to(user.email) .to(user.email)
.body(postmark::api::Body::text(format!( .body(postmark::api::Body::text(format!(
"Please run the following command to finalize your Atuin account verification. It is valid for 15 minutes:\n\natuin account verify --token '{}'", "Please run the following command to finalize your Atuin account verification. It is valid for 15 minutes:\n\natuin account verify --token '{verification_token}'"
verification_token
))) )))
.build(); .build();

View File

@ -7,5 +7,5 @@ fn main() {
Err(_) => String::from("NO_GIT"), Err(_) => String::from("NO_GIT"),
}; };
println!("cargo:rustc-env=GIT_HASH={}", sha); println!("cargo:rustc-env=GIT_HASH={sha}");
} }

View File

@ -342,27 +342,6 @@ fn parse_fmt(format: &str) -> ParsedFmt {
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_string_no_panic() {
// Don't panic but provide helpful output (issue #2776)
let malformed_json = r#"{"command":"{command}","key":"value"}"#;
let result = std::panic::catch_unwind(|| parse_fmt(malformed_json));
assert!(result.is_ok());
}
#[test]
fn test_valid_formats_still_work() {
assert!(std::panic::catch_unwind(|| parse_fmt("{command}")).is_ok());
assert!(std::panic::catch_unwind(|| parse_fmt("{time} - {command}")).is_ok());
}
}
impl Cmd { impl Cmd {
#[allow(clippy::too_many_lines, clippy::cast_possible_truncation)] #[allow(clippy::too_many_lines, clippy::cast_possible_truncation)]
async fn handle_start( async fn handle_start(
@ -772,3 +751,24 @@ impl Cmd {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_string_no_panic() {
// Don't panic but provide helpful output (issue #2776)
let malformed_json = r#"{"command":"{command}","key":"value"}"#;
let result = std::panic::catch_unwind(|| parse_fmt(malformed_json));
assert!(result.is_ok());
}
#[test]
fn test_valid_formats_still_work() {
assert!(std::panic::catch_unwind(|| parse_fmt("{command}")).is_ok());
assert!(std::panic::catch_unwind(|| parse_fmt("{time} - {command}")).is_ok());
}
}

View File

@ -45,6 +45,7 @@ pub enum Cmd {
const BATCH_SIZE: usize = 100; const BATCH_SIZE: usize = 100;
impl Cmd { impl Cmd {
#[allow(clippy::cognitive_complexity)]
pub async fn run<DB: Database>(&self, db: &DB) -> Result<()> { pub async fn run<DB: Database>(&self, db: &DB) -> Result<()> {
println!(" Atuin "); println!(" Atuin ");
println!("======================"); println!("======================");

View File

@ -437,6 +437,7 @@ impl Cmd {
} }
} }
#[allow(clippy::cognitive_complexity)]
async fn handle_edit( async fn handle_edit(
_settings: &Settings, _settings: &Settings,
edit: Edit, edit: Edit,

View File

@ -862,13 +862,12 @@ impl State {
} }
fn build_stats(&self, theme: &Theme) -> Paragraph { fn build_stats(&self, theme: &Theme) -> Paragraph {
let stats = Paragraph::new(Text::from(Span::raw(format!( Paragraph::new(Text::from(Span::raw(format!(
"history count: {}", "history count: {}",
self.history_count, self.history_count,
)))) ))))
.style(theme.as_style(Meaning::Annotation)) .style(theme.as_style(Meaning::Annotation))
.alignment(Alignment::Right); .alignment(Alignment::Right)
stats
} }
fn build_results_list<'a>( fn build_results_list<'a>(
@ -962,7 +961,8 @@ impl State {
}) })
.join("\n") .join("\n")
}; };
let preview = if compact {
if compact {
Paragraph::new(command).style(theme.as_style(Meaning::Annotation)) Paragraph::new(command).style(theme.as_style(Meaning::Annotation))
} else { } else {
Paragraph::new(command).block( Paragraph::new(command).block(
@ -971,8 +971,7 @@ impl State {
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.title(format!("{:─>width$}", "", width = chunk_width - 2)), .title(format!("{:─>width$}", "", width = chunk_width - 2)),
) )
}; }
preview
} }
} }
@ -1045,7 +1044,11 @@ impl Write for Stdout {
// this is a big blob of horrible! clean it up! // this is a big blob of horrible! clean it up!
// for now, it works. But it'd be great if it were more easily readable, and // for now, it works. But it'd be great if it were more easily readable, and
// modular. I'd like to add some more stats and stuff at some point // modular. I'd like to add some more stats and stuff at some point
#[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] #[allow(
clippy::cast_possible_truncation,
clippy::too_many_lines,
clippy::cognitive_complexity
)]
pub async fn history( pub async fn history(
query: &[String], query: &[String],
settings: &Settings, settings: &Settings,

View File

@ -20,7 +20,7 @@ impl Rekey {
let key = if let Some(key) = self.key.clone() { let key = if let Some(key) = self.key.clone() {
println!("Re-encrypting store with specified key"); println!("Re-encrypting store with specified key");
let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) { match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?, Ok(mnemonic) => encode_key(Key::from_slice(mnemonic.entropy()))?,
Err(err) => { Err(err) => {
match err.downcast_ref::<bip39::ErrorKind>() { match err.downcast_ref::<bip39::ErrorKind>() {
@ -44,9 +44,7 @@ impl Rekey {
} }
} }
} }
}; }
key
} else { } else {
println!("Re-encrypting store with freshly-generated key"); println!("Re-encrypting store with freshly-generated key");
let (_, encoded) = generate_encoded_key()?; let (_, encoded) = generate_encoded_key()?;

View File

@ -32,7 +32,7 @@
fenix.packages.${system}.fromToolchainFile fenix.packages.${system}.fromToolchainFile
{ {
file = ./rust-toolchain.toml; file = ./rust-toolchain.toml;
sha256 = "sha256-X/4ZBHO3iW0fOenQ3foEvscgAPJYl2abspaBThDOukI="; sha256 = "sha256-Qxt8XAuaUR2OMdKbN4u8dBJOhSHxS+uS06Wl9+flVEk=";
}; };
in in
pkgs.makeRustPlatform { pkgs.makeRustPlatform {

View File

@ -1,2 +1,2 @@
[toolchain] [toolchain]
channel = "1.86" channel = "1.88"