refactor: clippy warnings

This commit is contained in:
Kristofers Solo 2025-10-28 17:04:25 +02:00
parent 5dfc82508b
commit fee8178ad2
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
5 changed files with 47 additions and 45 deletions

View File

@ -13,6 +13,11 @@ pub enum Command {
Curse, Curse,
} }
/// Handle a command from the user.
///
/// # Errors
///
/// Returns a Teloxide error if the message fails to send.
pub async fn answer(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> { pub async fn answer(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {
match cmd { match cmd {
Command::Help => { Command::Help => {

View File

@ -58,11 +58,6 @@ async fn run_command_in_tempdir(cmd: &str, args: &[&str]) -> Result<DownloadResu
if !output.status.success() { if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
if stderr.is_empty() {
return Err(Error::Other(format!("{cmd} failed: {stderr}")));
}
let err = match cmd { let err = match cmd {
"yt-dlp" => Error::ytdlp_failed(stderr), "yt-dlp" => Error::ytdlp_failed(stderr),
_ => Error::Other(format!("{cmd} failed: {stderr}")), _ => Error::Other(format!("{cmd} failed: {stderr}")),
@ -241,19 +236,16 @@ pub async fn process_download_result(
} }
// deterministic ordering // deterministic ordering
media_items.sort_by(|(p1, _), (p2, _)| p1.cmp(p2)); media_items.sort_by_key(|(_, k)| match k {
MediaKind::Video => 0,
MediaKind::Image => 1,
MediaKind::Unknown => 2,
});
info!(media_items = media_items.len(), "Sending media to chat"); info!(media_items = media_items.len(), "Sending media to chat");
// prefer video over image if let Some((path, kind)) = media_items.first() {
if let Some((path, MediaKind::Video)) = media_items.iter().find(|(_, k)| *k == MediaKind::Video) return send_media_from_path(bot, chat_id, path.clone(), *kind).await;
{
return send_media_from_path(bot, chat_id, path.clone(), MediaKind::Video).await;
}
if let Some((path, MediaKind::Image)) = media_items.iter().find(|(_, k)| *k == MediaKind::Image)
{
return send_media_from_path(bot, chat_id, path.clone(), MediaKind::Image).await;
} }
Err(Error::NoMediaFound) Err(Error::NoMediaFound)

View File

@ -7,26 +7,28 @@ use std::{pin::Pin, sync::Arc};
use teloxide::{Bot, types::ChatId}; use teloxide::{Bot, types::ChatId};
use tracing::info; use tracing::info;
type DownloadFn = fn(&str) -> Pin<Box<dyn Future<Output = Result<DownloadResult>> + Send>>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Handler { pub struct Handler {
name: &'static str, name: &'static str,
regex: Regex, regex: Regex,
handler_fn: fn(&str) -> Pin<Box<dyn Future<Output = Result<DownloadResult>> + Send>>, func: DownloadFn,
} }
impl Handler { impl Handler {
#[must_use] /// Create a new handler with a regex pattern and download function.
///
/// # Errors
///
/// Returns `RegexError` if the regex pattern is invalid.
pub fn new( pub fn new(
name: &'static str, name: &'static str,
regex_pattern: &'static str, regex_pattern: &'static str,
handler_fn: fn(&str) -> Pin<Box<dyn Future<Output = Result<DownloadResult>> + Send>>, func: DownloadFn,
) -> std::result::Result<Self, RegexError> { ) -> std::result::Result<Self, RegexError> {
let regex = Regex::new(regex_pattern)?; let regex = Regex::new(regex_pattern)?;
Ok(Self { Ok(Self { name, regex, func })
name,
regex,
handler_fn,
})
} }
#[inline] #[inline]
@ -35,16 +37,22 @@ impl Handler {
self.name self.name
} }
/// Extract a URL matching this handler's regex pattern.
#[must_use] #[must_use]
pub fn try_extract(&self, text: &str) -> Option<String> { pub fn try_extract<'a>(&self, text: &'a str) -> Option<&'a str> {
self.regex self.regex
.captures(text) .captures(text)
.and_then(|c| c.get(0).map(|m| m.as_str().to_owned())) .and_then(|c| c.get(0).map(|m| m.as_str()))
} }
pub async fn handle(&self, bot: &Bot, chat_id: ChatId, url: String) -> Result<()> { /// Handle a URL by downloading and sending the media.
///
/// # Errors
///
/// Returns `Error` if download or media processing fails.
pub async fn handle(&self, bot: &Bot, chat_id: ChatId, url: &str) -> Result<()> {
info!(handler = %self.name(), url = %url, "handling url"); info!(handler = %self.name(), url = %url, "handling url");
let dr = (self.handler_fn)(&url).await?; let dr = (self.func)(url).await?;
process_download_result(bot, chat_id, dr).await process_download_result(bot, chat_id, dr).await
} }
} }

View File

@ -30,10 +30,9 @@ async fn main() -> color_eyre::Result<()> {
// Command::repl(bot.clone(), answer).await; // Command::repl(bot.clone(), answer).await;
teloxide::repl(bot.clone(), move |bot: Bot, msg: Message| { teloxide::repl(bot.clone(), move |bot: Bot, msg: Message| {
// clone the handlers vector into the closure
let handlers = handlers.clone(); let handlers = handlers.clone();
async move { async move {
process_message(&bot, &msg, &handlers); process_message(&bot, &msg, &handlers).await;
respond(()) respond(())
} }
}) })
@ -42,26 +41,20 @@ async fn main() -> color_eyre::Result<()> {
Ok(()) Ok(())
} }
fn process_message(bot: &Bot, msg: &Message, handlers: &[Handler]) { async fn process_message(bot: &Bot, msg: &Message, handlers: &[Handler]) {
let Some(text) = msg.text() else { let Some(text) = msg.text() else {
return; return;
}; };
for handler in handlers { for handler in handlers {
if let Some(url) = handler.try_extract(text) { if let Some(url) = handler.try_extract(text) {
handle_extracted_content(bot.clone(), msg.chat.id, handler.clone(), url); if let Err(err) = handler.handle(bot, msg.chat.id, url).await {
break;
}
}
}
fn handle_extracted_content(bot: Bot, chat: ChatId, handler: Handler, url: String) {
tokio::spawn(async move {
if let Err(err) = handler.handle(&bot, chat, url).await {
error!(%err, "handler failed"); error!(%err, "handler failed");
let _ = bot let _ = bot
.send_message(chat, "Failed to fetch media, you foking donkey.") .send_message(msg.chat.id, "Failed to fetch media, you foking donkey.")
.await; .await;
} }
}); return;
}
}
} }

View File

@ -121,8 +121,12 @@ pub async fn send_media_from_path(
if let Some(cap) = caption_opt { if let Some(cap) = caption_opt {
request = request.caption(cap); request = request.caption(cap);
} }
if let Ok(message) = request.await { match request.await {
info!(message_id = message.id.to_string(), "{} sent", kind); Ok(message) => info!(message_id = message.id.to_string(), "{} sent", kind),
Err(e) => {
error!("Failed to send {}: {e}", kind.to_str());
return Err(Error::Teloxide(e));
}
} }
}}; }};
} }