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 {
/// Обычный режим - просмотр сообщений, набор текста
#[default]
Normal,
/// Выбор сообщения для действия (edit/delete/reply/forward/reaction)
@@ -90,12 +91,6 @@ pub enum ChatState {
},
}
impl Default for ChatState {
fn default() -> Self {
ChatState::Normal
}
}
impl ChatState {
/// Проверка: находимся в режиме выбора сообщения
pub fn is_message_selection(&self) -> bool {

View File

@@ -110,8 +110,19 @@ pub struct 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();
// Navigation
@@ -301,22 +312,6 @@ impl Keybindings {
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
@@ -428,8 +423,8 @@ mod key_code_serde {
return Ok(KeyCode::Char(c));
}
if s.starts_with("F") {
let n = s[1..].parse().map_err(serde::de::Error::custom)?;
if let Some(suffix) = s.strip_prefix("F") {
let n = suffix.parse().map_err(serde::de::Error::custom)?;
return Ok(KeyCode::F(n));
}

View File

@@ -26,7 +26,7 @@ pub use keybindings::{Command, Keybindings};
/// println!("Timezone: {}", config.general.timezone);
/// println!("Incoming color: {}", config.colors.incoming_message);
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config {
/// Общие настройки (timezone и т.д.).
#[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)]
mod tests {
use super::*;

View File

@@ -126,22 +126,22 @@ pub fn format_text_with_entities(
let start = entity.offset 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 {
TextEntityType::Bold => char_styles[i].bold = true,
TextEntityType::Italic => char_styles[i].italic = true,
TextEntityType::Underline => char_styles[i].underline = true,
TextEntityType::Strikethrough => char_styles[i].strikethrough = true,
TextEntityType::Bold => item.bold = true,
TextEntityType::Italic => item.italic = true,
TextEntityType::Underline => item.underline = true,
TextEntityType::Strikethrough => item.strikethrough = true,
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::TextUrl(_)
| TextEntityType::EmailAddress
| TextEntityType::PhoneNumber => char_styles[i].url = true,
| TextEntityType::PhoneNumber => item.url = true,
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
.reply_to
.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);
}
@@ -780,7 +780,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
return handle_play_voice_from_path(
app,
&found_path,
&voice,
voice,
&msg,
)
.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));
}
handle_play_voice_from_path(app, &audio_path, &voice, &msg).await;
handle_play_voice_from_path(app, &audio_path, voice, &msg).await;
}
VoiceDownloadState::Downloading => {
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();
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();
handle_play_voice_from_path(app, &path_str, &voice, &msg).await;
handle_play_voice_from_path(app, &path_str, voice, &msg).await;
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));
}
handle_play_voice_from_path(app, &path, &voice, &msg).await;
handle_play_voice_from_path(app, &path, voice, &msg).await;
}
Err(e) => {
app.error_message = Some(format!("Ошибка загрузки: {}", e));

View File

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

View File

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

View File

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

View File

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

View File

@@ -155,6 +155,7 @@ pub struct MessageInfo {
impl MessageInfo {
/// Создать новое сообщение
#[allow(clippy::too_many_arguments)]
pub fn new(
id: MessageId,
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::RecordingVideoNote => Some("записывает видеосообщение...".to_string()),
ChatAction::UploadingVideoNote(_) => Some("отправляет видеосообщение...".to_string()),
ChatAction::Cancel | _ => None, // Отмена или неизвестное действие
_ => None, // Отмена или неизвестное действие
};
match action_text {

View File

@@ -21,7 +21,7 @@ pub fn render_emoji_picker(
) {
// Размеры модалки (зависят от количества реакций)
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_height = (rows + 4) as u16; // +4 для заголовка, отступов и подсказки

View File

@@ -401,12 +401,10 @@ pub fn render_message_bubble(
} else {
format!("[{}]", reaction.emoji)
}
} else {
if reaction.count > 1 {
} else if reaction.count > 1 {
format!("{} {}", reaction.emoji, reaction.count)
} else {
reaction.emoji.clone()
}
};
let style = if reaction.is_chosen {
@@ -548,7 +546,7 @@ pub fn render_album_bubble(
let mut deferred: Vec<DeferredImageRender> = Vec::new();
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
let selection_marker = if is_selected { "" } else { "" };
@@ -567,7 +565,7 @@ pub fn render_album_bubble(
// Grid layout
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 {

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 base_scroll = if total_lines > visible_height {
total_lines - visible_height
} else {
0
};
let base_scroll = total_lines.saturating_sub(visible_height);
// Если выбрано сообщение, автоскроллим к нему
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
};
// Минимум 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();