From 7a565f377203ba8e01574952014a23dfa8d94fa3 Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 30 Jul 2026 15:09:51 +0200 Subject: fix: auto completion timing out before ready + fix preexisting warns --- src/main.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 9 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 4a89315..7874c86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ use std::collections::HashMap; +use std::sync::Arc; +use std::time::{Duration, Instant}; use poise::serenity_prelude as serenity; use songbird::SerenityInit; @@ -17,6 +19,17 @@ mod voice; use database::DbPool; +/// How long a search cache entry stays fresh (10 seconds). +const AC_SEARCH_CACHE_TTL: Duration = Duration::from_secs(10); + +/// Per-user autocomplete stash: (UserId, filename) → SearchHit. +type AcCache = tokio::sync::Mutex>; + +/// Shared autocomplete search cache: query → (hits, cached_at). +type AcSearchCache = Arc< + tokio::sync::Mutex, Instant)>>, +>; + pub struct Data { pub db_pool: DbPool, pub config: config::Config, @@ -30,7 +43,12 @@ pub struct Data { /// Mirrors Python's per-cog `_cached_urls[user_id]` map: when two users /// search the same filename simultaneously, each invocation must resolve its /// own stashed `SearchHit`, so the cache is keyed by `(UserId, filename)`. - pub ac_cache: tokio::sync::Mutex>, + pub ac_cache: AcCache, + /// Autocomplete search results cache (query → hits). + /// + /// Populated by background tasks when the first search times out, so the + /// next keystroke's autocomplete can serve real results immediately. + pub ac_search_cache: AcSearchCache, /// Active music track per guild (only one music source at a time). pub music_tracks: tokio::sync::Mutex>, } @@ -63,13 +81,36 @@ impl Data { .get(&(user_id, key.to_string())) .cloned() } + + /// Look up search cache for an autocomplete query. Returns `None` if + /// uncached or stale. + pub async fn ac_search_cache_lookup( + &self, + query: &str, + ) -> Option> { + let cache = self.ac_search_cache.lock().await; + let (hits, cached_at) = cache.get(query)?; + if cached_at.elapsed() > AC_SEARCH_CACHE_TTL { + None + } else { + Some(hits.clone()) + } + } + + /// Store search results into the shared cache. + pub async fn ac_search_cache_store(&self, query: String, hits: Vec) { + self.ac_search_cache + .lock() + .await + .insert(query, (hits, Instant::now())); + } } #[allow(dead_code)] type Error = error::Error; #[tokio::main] -async fn main() { +async fn main() -> Result<(), Box> { let _ = rustls::crypto::ring::default_provider().install_default(); tracing_subscriber::fmt() @@ -84,10 +125,7 @@ async fn main() { config::check_env_vars(); let config = config::Config::from_env(); - let db_pool = database::init_pool(&config.database_path) - .await - .expect("Failed to initialize database"); - + let db_pool = database::init_pool(&config.database_path).await?; let http_client = reqwest::Client::new(); let copyparty = copyparty::CopypartyClient::new( @@ -132,6 +170,7 @@ async fn main() { volume: std::sync::atomic::AtomicU64::new(1.0f64.to_bits()), triggers: tokio::sync::Mutex::new(triggers), ac_cache: tokio::sync::Mutex::new(HashMap::new()), + ac_search_cache: Arc::new(tokio::sync::Mutex::new(HashMap::new())), music_tracks: tokio::sync::Mutex::new(HashMap::new()), }; @@ -164,10 +203,10 @@ async fn main() { serenity::ClientBuilder::new(&config_for_client.token, serenity::GatewayIntents::all()) .framework(framework) .register_songbird() - .await - .expect("Error creating client"); + .await?; - client.start().await.expect("Error starting client"); + client.start().await?; + Ok(()) } async fn handle_event(ctx: &serenity::Context, event: &serenity::FullEvent, user_data: &Data) { -- cgit v1.2.3