PicoBot/src/command/handlers/switch_topic.rs
oudecheng 1019dbe8cc refactor(code-quality): 清理 clippy 存量告警(unwrap/clone/redundant 等)
- 移除无用克隆与冗余引用,减少不必要内存分配
- 规范 unwrap/expect 使用,修复可提前失败路径
- 修复 anthropic provider llm_timeout_secs 死代码并补全超时日志
- cargo fmt 统一格式
2026-08-16 23:22:22 +08:00

132 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::command::Command;
use crate::command::context::CommandContext;
use crate::command::handler::{CommandHandler, CommandMetadata};
use crate::command::response::{CommandError, CommandResponse, MessageKind};
use crate::gateway::session::SessionManager;
use crate::storage::SessionStore;
use async_trait::async_trait;
use std::sync::Arc;
/// 切换话题命令处理器
pub struct SwitchTopicCommandHandler {
store: Arc<SessionStore>,
session_manager: Option<SessionManager>,
}
impl SwitchTopicCommandHandler {
pub fn new(store: Arc<SessionStore>) -> Self {
Self {
store,
session_manager: None,
}
}
pub fn with_session_manager(mut self, session_manager: SessionManager) -> Self {
self.session_manager = Some(session_manager);
self
}
}
#[async_trait]
impl CommandHandler for SwitchTopicCommandHandler {
fn can_handle(&self, cmd: &Command) -> bool {
matches!(cmd, Command::SwitchTopic { .. })
}
fn metadata(&self) -> Option<CommandMetadata> {
Some(CommandMetadata {
name: "use",
description: "切换到指定话题",
usage: "/use <topic_id>",
})
}
async fn handle(
&self,
cmd: Command,
ctx: CommandContext,
) -> Result<CommandResponse, CommandError> {
match cmd {
Command::SwitchTopic { topic_id } => handle_switch_topic(self, topic_id, ctx).await,
_ => unreachable!(),
}
}
}
async fn handle_switch_topic(
handler: &SwitchTopicCommandHandler,
topic_id: String,
ctx: CommandContext,
) -> Result<CommandResponse, CommandError> {
let session_id = ctx
.session_id
.as_deref()
.ok_or_else(|| CommandError::new("NO_SESSION", "No active session"))?;
let chat_id = ctx
.chat_id
.as_deref()
.ok_or_else(|| CommandError::new("NO_CHAT_ID", "No chat_id in context"))?;
// 尝试解析为序号
let target_topic_id = if let Ok(index) = topic_id.parse::<usize>() {
let topics = handler
.store
.list_topics(session_id)
.map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?;
let index = index.saturating_sub(1);
if index >= topics.len() {
return Err(CommandError::new(
"INVALID_TOPIC_INDEX",
format!(
"Topic index {} is out of range (1-{})",
index + 1,
topics.len()
),
));
}
topics[index].id.clone()
} else {
topic_id
};
// 验证目标话题存在
let topic = handler
.store
.get_topic(&target_topic_id)
.map_err(|e| CommandError::new("SWITCH_TOPIC_ERROR", e.to_string()))?
.ok_or_else(|| {
CommandError::new(
"TOPIC_NOT_FOUND",
format!("Topic not found: {}", target_topic_id),
)
})?;
// 如果有 SessionManager实际切换话题历史
if let Some(ref session_manager) = handler.session_manager
&& let Some(session) = session_manager.get(&ctx.channel_name).await
{
let mut session_guard = session.lock().await;
session_guard
.switch_topic(chat_id, &target_topic_id)
.map_err(|e| CommandError::new("SWITCH_TOPIC_ERROR", e.to_string()))?;
}
// 使用辅助方法获取消息数量
let msg_count = handler
.store
.get_topic_message_count(&target_topic_id)
.unwrap_or(0);
let message = format!(
"✓ Switched to topic: {} ({} messages)",
topic.title, msg_count
);
Ok(CommandResponse::success(ctx.request_id)
.with_message(MessageKind::Notification, &message)
.with_metadata("topic_id", &topic.id)
.with_metadata("title", &topic.title)
.with_metadata("message_count", msg_count.to_string()))
}