From 81e99a43e8a767c6a686941dc84e2e78fcb0789a Mon Sep 17 00:00:00 2001 From: Oliver Rose Date: Sun, 15 Mar 2026 22:04:30 -0400 Subject: [PATCH] misc rust improvements --- src-tauri/Cargo.toml | 2 ++ src-tauri/src/api.rs | 23 ++++++++++---------- src-tauri/src/commands.rs | 20 +++++++++++------ src-tauri/src/eventsub/client.rs | 8 +++---- src-tauri/src/eventsub/mod.rs | 4 ++-- src-tauri/src/irc/connection/event_loop.rs | 20 ++++++++++------- src-tauri/src/irc/message/commands.rs | 14 ++++++------ src-tauri/src/irc/message/tags.rs | 5 ++--- src-tauri/src/log.rs | 5 ++++- src-tauri/src/server.rs | 25 +++++++++++----------- src-tauri/src/seventv/mod.rs | 4 ++-- 11 files changed, 72 insertions(+), 58 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7429f347..19364a4b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -14,6 +14,8 @@ crate-type = ["staticlib", "cdylib", "rlib"] [profile.release] lto = "fat" +codegen-units = 1 +strip = true [profile.dev] debug = false diff --git a/src-tauri/src/api.rs b/src-tauri/src/api.rs index f76a771c..2ee3ec52 100644 --- a/src-tauri/src/api.rs +++ b/src-tauri/src/api.rs @@ -82,22 +82,21 @@ pub async fn join( }; let login_clone = login.clone(); - let id_clone = id.clone(); async_runtime::spawn( async move { if let Some(eventsub) = eventsub { let ch_cond = json!({ - "broadcaster_user_id": id_clone + "broadcaster_user_id": id }); let ch_with_user_cond = json!({ - "broadcaster_user_id": id_clone, + "broadcaster_user_id": id, "user_id": token.user_id }); let ch_with_mod_cond = json!({ - "broadcaster_user_id": id_clone, + "broadcaster_user_id": id, "moderator_user_id": token.user_id }); @@ -136,7 +135,7 @@ pub async fn join( let channel_cond = json!({ "ctx": "channel", "platform": "TWITCH", - "id": id_clone + "id": id }); seventv @@ -219,15 +218,17 @@ pub async fn fetch_user_emotes(app_handle: AppHandle) { async_runtime::spawn( async move { let state = app_handle.state::>(); - let state = state.lock().await; - let Some(token) = state.token.as_ref() else { - return Ok::<_, Error>(()); + let (helix, token) = { + let state = state.lock().await; + let Some(token) = state.token.clone() else { + return Ok::<_, Error>(()); + }; + (state.helix.clone(), token) }; - let emotes: Vec<_> = state - .helix - .get_user_emotes(&token.user_id, token) + let emotes: Vec<_> = helix + .get_user_emotes(&token.user_id, &token) .try_collect() .await?; diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 318a9a4e..2c8c2e3e 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -84,16 +84,22 @@ pub fn get_cache_size(app_handle: AppHandle) -> i64 { } fn format_bytes(bytes: u64) -> String { - let i = ((bytes as f64).ln() / 1024_f64.ln()).floor(); + const UNITS: [&str; 5] = ["bytes", "KB", "MB", "GB", "TB"]; - if i == 0.0 { - "0 bytes".to_string() - } else { - let value = bytes as f64 / 1024_f64.powf(i); - let unit = ["bytes", "KB", "MB", "GB", "TB"][i as usize]; + if bytes == 0 { + return "0 bytes".to_string(); + } + + let i = ((bytes as f64).log2() / 10.0).floor() as usize; + let i = i.min(UNITS.len() - 1); - format!("{value:.2} {unit}") + if i == 0 { + return format!("{bytes} bytes"); } + + let value = bytes as f64 / (1u64 << (i * 10)) as f64; + + format!("{value:.2} {}", UNITS[i]) } #[tauri::command] diff --git a/src-tauri/src/eventsub/client.rs b/src-tauri/src/eventsub/client.rs index 9f07a8a9..0e13b7dc 100644 --- a/src-tauri/src/eventsub/client.rs +++ b/src-tauri/src/eventsub/client.rs @@ -180,7 +180,7 @@ impl EventSubClient { tracing::info!("Connected to EventSub"); self.set_connected(true); - let _ = self.clone().process_stream(stream).await; + let _ = Arc::clone(&self).process_stream(stream).await; self.set_connected(false); *self.session_id.lock().await = None; @@ -201,7 +201,7 @@ impl EventSubClient { stream.send(Message::Pong(data)).await?; } Message::Text(data) => { - if let Some(new_stream) = self.clone().handle_text(&data).await? { + if let Some(new_stream) = Arc::clone(&self).handle_text(&data).await? { let frame = CloseFrame { code: CloseCode::Normal, reason: "Reconnecting".into(), @@ -228,7 +228,7 @@ impl EventSubClient { Some(Err(err)) => { tracing::error!(%err, "EventSub connection error"); - match self.clone().reconnect(TWITCH_EVENTSUB_WS_URI).await { + match Arc::clone(&self).reconnect(TWITCH_EVENTSUB_WS_URI).await { Ok(new_stream) => { stream = new_stream; } @@ -252,7 +252,7 @@ impl EventSubClient { async fn handle_text(self: Arc, data: &str) -> Result, Error> { if let Ok(msg) = serde_json::from_str(data) - && let Some(url) = self.clone().handle_message(msg).await? + && let Some(url) = Arc::clone(&self).handle_message(msg).await? { tracing::info!("Reconnecting to EventSub at {url}"); return Ok(Some(self.reconnect(&url).await?)); diff --git a/src-tauri/src/eventsub/mod.rs b/src-tauri/src/eventsub/mod.rs index 3c67dc28..c7f2ab6a 100644 --- a/src-tauri/src/eventsub/mod.rs +++ b/src-tauri/src/eventsub/mod.rs @@ -31,11 +31,11 @@ pub async fn connect_eventsub( let (mut incoming, client) = EventSubClient::new(helix, Arc::new(token)); let client = Arc::new(client); - guard.eventsub = Some(client.clone()); + guard.eventsub = Some(Arc::clone(&client)); drop(guard); async_runtime::spawn(async move { - if client.clone().connect().await.is_err() { + if Arc::clone(&client).connect().await.is_err() { let state = app_handle.state::>(); let mut state = state.lock().await; diff --git a/src-tauri/src/irc/connection/event_loop.rs b/src-tauri/src/irc/connection/event_loop.rs index d557d818..3c844cdf 100644 --- a/src-tauri/src/irc/connection/event_loop.rs +++ b/src-tauri/src/irc/connection/event_loop.rs @@ -409,11 +409,8 @@ impl ConnectionLoopStateMethods for ConnectionLoopOpenState { match server_message { Ok(server_message) => { - self.connection_incoming_tx - .send(ConnectionIncomingMessage::IncomingMessage(Box::new( - server_message.clone(), - ))) - .ok(); + let should_close = + matches!(&server_message, ServerMessage::Reconnect(_)); match &server_message { ServerMessage::Ping(_) => { @@ -422,11 +419,18 @@ impl ConnectionLoopStateMethods for ConnectionLoopOpenState { ServerMessage::Pong(_) => { self.pong_received = true; } - ServerMessage::Reconnect(_) => { - return self.transition_to_closed(Error::ReconnectCmd); - } _ => {} } + + self.connection_incoming_tx + .send(ConnectionIncomingMessage::IncomingMessage(Box::new( + server_message, + ))) + .ok(); + + if should_close { + return self.transition_to_closed(Error::ReconnectCmd); + } } Err(parse_error) => { self.connection_incoming_tx diff --git a/src-tauri/src/irc/message/commands.rs b/src-tauri/src/irc/message/commands.rs index 376e23a4..28e9f8af 100644 --- a/src-tauri/src/irc/message/commands.rs +++ b/src-tauri/src/irc/message/commands.rs @@ -1094,6 +1094,7 @@ impl IrcMessageParseExt for IrcMessage { return Ok(vec![]); } + let chars: Vec = message_text.chars().collect(); let mut emotes = Vec::new(); let make_error = || MalformedTagValue(self.to_owned(), tag_key, tag_value.to_owned()); @@ -1107,13 +1108,11 @@ impl IrcMessageParseExt for IrcMessage { let start = usize::from_str(start).map_err(|_| make_error())?; let end = usize::from_str(end).map_err(|_| make_error())? + 1; - let code_length = end - start; - - let code = message_text - .chars() - .skip(start) - .take(code_length) - .collect::(); + let code: String = chars + .get(start..end) + .unwrap_or_default() + .iter() + .collect(); emotes.push(Emote { id: emote_id.to_owned(), @@ -1142,7 +1141,6 @@ impl IrcMessageParseExt for IrcMessage { } fn try_get_badges(&self, tag_key: &'static str) -> Result, ServerMessageParseError> { - // TODO same thing as above, could be optimized to not clone the tag value as well let tag_value = self.try_get_tag_value(tag_key)?; if tag_value.is_empty() { diff --git a/src-tauri/src/irc/message/tags.rs b/src-tauri/src/irc/message/tags.rs index 08652181..311804e4 100644 --- a/src-tauri/src/irc/message/tags.rs +++ b/src-tauri/src/irc/message/tags.rs @@ -71,14 +71,13 @@ impl IrcTags { panic!("invalid input") } - let mut tags = IrcTags::new(); + let tag_count = source.as_bytes().iter().filter(|&&b| b == b';').count() + 1; + let mut tags = IrcTags(HashMap::with_capacity(tag_count)); for raw_tag in source.split(';') { let mut tag_split = raw_tag.splitn(2, '='); - // always expected to be present, even splitting an empty string yields [""] let key = tag_split.next().unwrap(); - // can be missing if no = is present let value = tag_split .next() .map_or_else(|| "".to_owned(), decode_tag_value); diff --git a/src-tauri/src/log.rs b/src-tauri/src/log.rs index b254a61c..1060aaa3 100644 --- a/src-tauri/src/log.rs +++ b/src-tauri/src/log.rs @@ -82,7 +82,10 @@ impl<'de> Deserialize<'de> for LogLevel { "info" => Ok(LogLevel::Info), "warn" => Ok(LogLevel::Warn), "error" => Ok(LogLevel::Error), - _ => unreachable!(), + other => Err(serde::de::Error::unknown_variant( + other, + &["trace", "debug", "info", "warn", "error"], + )), } } } diff --git a/src-tauri/src/server.rs b/src-tauri/src/server.rs index b83d63db..eb507b46 100644 --- a/src-tauri/src/server.rs +++ b/src-tauri/src/server.rs @@ -114,18 +114,19 @@ async fn handle_connection(mut stream: TcpStream) -> Option { " ); - stream - .write_all( - format!( - "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{response}", - response.len() - ) - .as_bytes(), - ) - .await - .unwrap(); - - stream.flush().await.unwrap(); + let header = format!( + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{response}", + response.len() + ); + + if let Err(err) = stream.write_all(header.as_bytes()).await { + tracing::error!(%err, "Failed to write response"); + return None; + } + + if let Err(err) = stream.flush().await { + tracing::error!(%err, "Failed to flush response"); + } None } diff --git a/src-tauri/src/seventv/mod.rs b/src-tauri/src/seventv/mod.rs index 888c0ea9..1570e4a4 100644 --- a/src-tauri/src/seventv/mod.rs +++ b/src-tauri/src/seventv/mod.rs @@ -28,11 +28,11 @@ pub async fn connect_seventv( let (mut incoming, client) = SeventTvClient::new(); let client = Arc::new(client); - state.seventv = Some(client.clone()); + state.seventv = Some(Arc::clone(&client)); drop(state); async_runtime::spawn(async move { - if client.clone().connect().await.is_err() { + if Arc::clone(&client).connect().await.is_err() { let state = app_handle.state::>(); let mut state = state.lock().await;