fix: resolve all 23 clippy warnings
Some checks failed
ci/woodpecker/pr/check Pipeline failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled

This commit is contained in:
Mikhail Kilin
2026-02-22 17:28:50 +03:00
parent d9eb61dda7
commit d4e1ed1376
14 changed files with 49 additions and 92 deletions

View File

@@ -14,9 +14,10 @@ pub enum InputMode {
} }
/// Состояния чата - взаимоисключающие режимы работы с чатом /// Состояния чата - взаимоисключающие режимы работы с чатом
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub enum ChatState { pub enum ChatState {
/// Обычный режим - просмотр сообщений, набор текста /// Обычный режим - просмотр сообщений, набор текста
#[default]
Normal, Normal,
/// Выбор сообщения для действия (edit/delete/reply/forward/reaction) /// Выбор сообщения для действия (edit/delete/reply/forward/reaction)
@@ -90,12 +91,6 @@ pub enum ChatState {
}, },
} }
impl Default for ChatState {
fn default() -> Self {
ChatState::Normal
}
}
impl ChatState { impl ChatState {
/// Проверка: находимся в режиме выбора сообщения /// Проверка: находимся в режиме выбора сообщения
pub fn is_message_selection(&self) -> bool { pub fn is_message_selection(&self) -> bool {

View File

@@ -110,8 +110,19 @@ pub struct Keybindings {
} }
impl Keybindings { impl Keybindings {
/// Создаёт дефолтную конфигурацию /// Ищет команду по клавише
pub fn default() -> Self { pub fn get_command(&self, event: &KeyEvent) -> Option<Command> {
for (command, bindings) in &self.bindings {
if bindings.iter().any(|binding| binding.matches(event)) {
return Some(*command);
}
}
None
}
}
impl Default for Keybindings {
fn default() -> Self {
let mut bindings = HashMap::new(); let mut bindings = HashMap::new();
// Navigation // Navigation
@@ -301,22 +312,6 @@ impl Keybindings {
Self { bindings } Self { bindings }
} }
/// Ищет команду по клавише
pub fn get_command(&self, event: &KeyEvent) -> Option<Command> {
for (command, bindings) in &self.bindings {
if bindings.iter().any(|binding| binding.matches(event)) {
return Some(*command);
}
}
None
}
}
impl Default for Keybindings {
fn default() -> Self {
Self::default()
}
} }
/// Сериализация KeyModifiers /// Сериализация KeyModifiers
@@ -428,8 +423,8 @@ mod key_code_serde {
return Ok(KeyCode::Char(c)); return Ok(KeyCode::Char(c));
} }
if s.starts_with("F") { if let Some(suffix) = s.strip_prefix("F") {
let n = s[1..].parse().map_err(serde::de::Error::custom)?; let n = suffix.parse().map_err(serde::de::Error::custom)?;
return Ok(KeyCode::F(n)); return Ok(KeyCode::F(n));
} }

View File

@@ -26,7 +26,7 @@ pub use keybindings::{Command, Keybindings};
/// println!("Timezone: {}", config.general.timezone); /// println!("Timezone: {}", config.general.timezone);
/// println!("Incoming color: {}", config.colors.incoming_message); /// println!("Incoming color: {}", config.colors.incoming_message);
/// ``` /// ```
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config { pub struct Config {
/// Общие настройки (timezone и т.д.). /// Общие настройки (timezone и т.д.).
#[serde(default)] #[serde(default)]
@@ -260,19 +260,6 @@ impl Default for NotificationsConfig {
} }
} }
impl Default for Config {
fn default() -> Self {
Self {
general: GeneralConfig::default(),
colors: ColorsConfig::default(),
keybindings: Keybindings::default(),
notifications: NotificationsConfig::default(),
images: ImagesConfig::default(),
audio: AudioConfig::default(),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@@ -126,22 +126,22 @@ pub fn format_text_with_entities(
let start = entity.offset as usize; let start = entity.offset as usize;
let end = (entity.offset + entity.length) as usize; let end = (entity.offset + entity.length) as usize;
for i in start..end.min(chars.len()) { for item in char_styles.iter_mut().take(end.min(chars.len())).skip(start) {
match &entity.r#type { match &entity.r#type {
TextEntityType::Bold => char_styles[i].bold = true, TextEntityType::Bold => item.bold = true,
TextEntityType::Italic => char_styles[i].italic = true, TextEntityType::Italic => item.italic = true,
TextEntityType::Underline => char_styles[i].underline = true, TextEntityType::Underline => item.underline = true,
TextEntityType::Strikethrough => char_styles[i].strikethrough = true, TextEntityType::Strikethrough => item.strikethrough = true,
TextEntityType::Code | TextEntityType::Pre | TextEntityType::PreCode(_) => { TextEntityType::Code | TextEntityType::Pre | TextEntityType::PreCode(_) => {
char_styles[i].code = true item.code = true
} }
TextEntityType::Spoiler => char_styles[i].spoiler = true, TextEntityType::Spoiler => item.spoiler = true,
TextEntityType::Url TextEntityType::Url
| TextEntityType::TextUrl(_) | TextEntityType::TextUrl(_)
| TextEntityType::EmailAddress | TextEntityType::EmailAddress
| TextEntityType::PhoneNumber => char_styles[i].url = true, | TextEntityType::PhoneNumber => item.url = true,
TextEntityType::Mention | TextEntityType::MentionName(_) => { TextEntityType::Mention | TextEntityType::MentionName(_) => {
char_styles[i].mention = true item.mention = true
} }
_ => {} _ => {}
} }

View File

@@ -172,7 +172,7 @@ pub async fn edit_message<T: TdClientTrait>(
.interactions .interactions
.reply_to .reply_to
.as_ref() .as_ref()
.map_or(true, |r| r.sender_name == "Unknown") .is_none_or(|r| r.sender_name == "Unknown")
{ {
edited_msg.interactions.reply_to = Some(old_reply); edited_msg.interactions.reply_to = Some(old_reply);
} }
@@ -780,7 +780,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
return handle_play_voice_from_path( return handle_play_voice_from_path(
app, app,
&found_path, &found_path,
&voice, voice,
&msg, &msg,
) )
.await; .await;
@@ -799,7 +799,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
let _ = cache.store(&file_id.to_string(), Path::new(&audio_path)); let _ = cache.store(&file_id.to_string(), Path::new(&audio_path));
} }
handle_play_voice_from_path(app, &audio_path, &voice, &msg).await; handle_play_voice_from_path(app, &audio_path, voice, &msg).await;
} }
VoiceDownloadState::Downloading => { VoiceDownloadState::Downloading => {
app.status_message = Some("Загрузка голосового...".to_string()); app.status_message = Some("Загрузка голосового...".to_string());
@@ -809,7 +809,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
let cache_key = file_id.to_string(); let cache_key = file_id.to_string();
if let Some(cached_path) = app.voice_cache.as_mut().and_then(|c| c.get(&cache_key)) { if let Some(cached_path) = app.voice_cache.as_mut().and_then(|c| c.get(&cache_key)) {
let path_str = cached_path.to_string_lossy().to_string(); let path_str = cached_path.to_string_lossy().to_string();
handle_play_voice_from_path(app, &path_str, &voice, &msg).await; handle_play_voice_from_path(app, &path_str, voice, &msg).await;
return; return;
} }
@@ -822,7 +822,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
let _ = cache.store(&cache_key, std::path::Path::new(&path)); let _ = cache.store(&cache_key, std::path::Path::new(&path));
} }
handle_play_voice_from_path(app, &path, &voice, &msg).await; handle_play_voice_from_path(app, &path, voice, &msg).await;
} }
Err(e) => { Err(e) => {
app.error_message = Some(format!("Ошибка загрузки: {}", e)); app.error_message = Some(format!("Ошибка загрузки: {}", e));

View File

@@ -17,7 +17,7 @@ pub enum MessageGroup {
sender_name: String, sender_name: String,
}, },
/// Сообщение /// Сообщение
Message(MessageInfo), Message(Box<MessageInfo>),
/// Альбом (группа фото с одинаковым media_album_id) /// Альбом (группа фото с одинаковым media_album_id)
Album(Vec<MessageInfo>), Album(Vec<MessageInfo>),
} }
@@ -78,7 +78,7 @@ pub fn group_messages(messages: &[MessageInfo]) -> Vec<MessageGroup> {
result.push(MessageGroup::Album(std::mem::take(acc))); result.push(MessageGroup::Album(std::mem::take(acc)));
} else { } else {
// Одно сообщение — не альбом // Одно сообщение — не альбом
result.push(MessageGroup::Message(acc.remove(0))); result.push(MessageGroup::Message(Box::new(acc.remove(0))));
} }
} }
@@ -137,7 +137,7 @@ pub fn group_messages(messages: &[MessageInfo]) -> Vec<MessageGroup> {
// Обычное сообщение (не альбом) — flush аккумулятор // Обычное сообщение (не альбом) — flush аккумулятор
flush_album(&mut album_acc, &mut result); flush_album(&mut album_acc, &mut result);
result.push(MessageGroup::Message(msg.clone())); result.push(MessageGroup::Message(Box::new(msg.clone())));
} }
// Flush оставшийся аккумулятор // Flush оставшийся аккумулятор

View File

@@ -490,19 +490,6 @@ impl TdClient {
// ==================== Helper методы для упрощения обработки updates ==================== // ==================== Helper методы для упрощения обработки updates ====================
/// Находит мутабельную ссылку на чат по ID.
///
/// Упрощает повторяющийся паттерн `self.chats_mut().iter_mut().find(...)`.
///
/// # Arguments
///
/// * `chat_id` - ID чата для поиска
///
/// # Returns
///
/// * `Some(&mut ChatInfo)` - если чат найден
/// * `None` - если чат не найден
/// Обрабатываем одно обновление от TDLib /// Обрабатываем одно обновление от TDLib
pub fn handle_update(&mut self, update: Update) { pub fn handle_update(&mut self, update: Update) {
match update { match update {

View File

@@ -116,7 +116,7 @@ pub fn extract_reply_info(client: &TdClient, message: &TdMessage) -> Option<Repl
let sender_name = reply let sender_name = reply
.origin .origin
.as_ref() .as_ref()
.map(|origin| get_origin_sender_name(origin)) .map(get_origin_sender_name)
.unwrap_or_else(|| { .unwrap_or_else(|| {
// Пробуем найти оригинальное сообщение в текущем списке // Пробуем найти оригинальное сообщение в текущем списке
let reply_msg_id = MessageId::new(reply.message_id); let reply_msg_id = MessageId::new(reply.message_id);

View File

@@ -206,13 +206,11 @@ impl MessageManager {
match result { match result {
Ok(tdlib_rs::enums::Messages::Messages(messages_obj)) => { Ok(tdlib_rs::enums::Messages::Messages(messages_obj)) => {
let mut messages = Vec::new(); let mut messages = Vec::new();
for msg_opt in messages_obj.messages.iter().rev() { for msg in messages_obj.messages.iter().rev().flatten() {
if let Some(msg) = msg_opt {
if let Some(info) = self.convert_message(msg).await { if let Some(info) = self.convert_message(msg).await {
messages.push(info); messages.push(info);
} }
} }
}
Ok(messages) Ok(messages)
} }
Err(e) => Err(format!("Ошибка загрузки старых сообщений: {:?}", e)), Err(e) => Err(format!("Ошибка загрузки старых сообщений: {:?}", e)),

View File

@@ -155,6 +155,7 @@ pub struct MessageInfo {
impl MessageInfo { impl MessageInfo {
/// Создать новое сообщение /// Создать новое сообщение
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
id: MessageId, id: MessageId,
sender_name: String, sender_name: String,

View File

@@ -105,7 +105,7 @@ pub fn handle_chat_action_update(client: &mut TdClient, update: UpdateChatAction
ChatAction::ChoosingSticker => Some("выбирает стикер...".to_string()), ChatAction::ChoosingSticker => Some("выбирает стикер...".to_string()),
ChatAction::RecordingVideoNote => Some("записывает видеосообщение...".to_string()), ChatAction::RecordingVideoNote => Some("записывает видеосообщение...".to_string()),
ChatAction::UploadingVideoNote(_) => Some("отправляет видеосообщение...".to_string()), ChatAction::UploadingVideoNote(_) => Some("отправляет видеосообщение...".to_string()),
ChatAction::Cancel | _ => None, // Отмена или неизвестное действие _ => None, // Отмена или неизвестное действие
}; };
match action_text { match action_text {

View File

@@ -21,7 +21,7 @@ pub fn render_emoji_picker(
) { ) {
// Размеры модалки (зависят от количества реакций) // Размеры модалки (зависят от количества реакций)
let emojis_per_row = 8; let emojis_per_row = 8;
let rows = (available_reactions.len() + emojis_per_row - 1) / emojis_per_row; let rows = available_reactions.len().div_ceil(emojis_per_row);
let modal_width = 50u16; let modal_width = 50u16;
let modal_height = (rows + 4) as u16; // +4 для заголовка, отступов и подсказки let modal_height = (rows + 4) as u16; // +4 для заголовка, отступов и подсказки

View File

@@ -401,12 +401,10 @@ pub fn render_message_bubble(
} else { } else {
format!("[{}]", reaction.emoji) format!("[{}]", reaction.emoji)
} }
} else { } else if reaction.count > 1 {
if reaction.count > 1 {
format!("{} {}", reaction.emoji, reaction.count) format!("{} {}", reaction.emoji, reaction.count)
} else { } else {
reaction.emoji.clone() reaction.emoji.clone()
}
}; };
let style = if reaction.is_chosen { let style = if reaction.is_chosen {
@@ -548,7 +546,7 @@ pub fn render_album_bubble(
let mut deferred: Vec<DeferredImageRender> = Vec::new(); let mut deferred: Vec<DeferredImageRender> = Vec::new();
let is_selected = messages.iter().any(|m| selected_msg_id == Some(m.id())); let is_selected = messages.iter().any(|m| selected_msg_id == Some(m.id()));
let is_outgoing = messages.first().map_or(false, |m| m.is_outgoing()); let is_outgoing = messages.first().is_some_and(|m| m.is_outgoing());
// Selection marker // Selection marker
let selection_marker = if is_selected { "" } else { "" }; let selection_marker = if is_selected { "" } else { "" };
@@ -567,7 +565,7 @@ pub fn render_album_bubble(
// Grid layout // Grid layout
let cols = photo_count.min(ALBUM_GRID_MAX_COLS); let cols = photo_count.min(ALBUM_GRID_MAX_COLS);
let rows = (photo_count + cols - 1) / cols; let rows = photo_count.div_ceil(cols);
// Добавляем маркер выбора на первую строку // Добавляем маркер выбора на первую строку
if is_selected { if is_selected {

View File

@@ -309,11 +309,7 @@ fn render_message_list<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut Ap
let total_lines = lines.len(); let total_lines = lines.len();
// Базовый скролл (показываем последние сообщения) // Базовый скролл (показываем последние сообщения)
let base_scroll = if total_lines > visible_height { let base_scroll = total_lines.saturating_sub(visible_height);
total_lines - visible_height
} else {
0
};
// Если выбрано сообщение, автоскроллим к нему // Если выбрано сообщение, автоскроллим к нему
let scroll_offset = if app.is_selecting_message() { let scroll_offset = if app.is_selecting_message() {
@@ -431,7 +427,7 @@ pub fn render<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut App<T>) {
1 1
}; };
// Минимум 3 строки (1 контент + 2 рамки), максимум 10 // Минимум 3 строки (1 контент + 2 рамки), максимум 10
let input_height = (input_lines + 2).min(10).max(3); let input_height = (input_lines + 2).clamp(3, 10);
// Проверяем, есть ли закреплённое сообщение // Проверяем, есть ли закреплённое сообщение
let has_pinned = app.td_client.current_pinned_message().is_some(); let has_pinned = app.td_client.current_pinned_message().is_some();