refactor: apply zero-unwrap policy and optimize locks in store.rs and handlers

This commit is contained in:
Riz Ashraf committed 2026-09-21 11:34:21 +01:00
1 parent 9f24e66d88
commit 8afbf97b11
33 files changed
+3127 -3207

No files matched your search

+273
View File
@@ -0,0 +1,273 @@
use crate::models::*;
use crate::router::McpTool;
use crate::state::MemoryState;
use crate::tools::*;
use async_trait::async_trait;
use serde_json::Value;
use std::collections::HashSet;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
pub struct AddStickyNoteHandler;
#[async_trait]
impl McpTool for AddStickyNoteHandler {
fn name(&self) -> &'static str {
"add_sticky_note"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<AddStickyNoteTool>("add_sticky_note", "Execute add_sticky_note")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: AddStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.sticky.modify(|notes| {
notes.push(StickyNote {
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
content: req.content,
});
});
Ok("Sticky note added.".to_string())
}
}
pub struct ReadStickyNotesHandler;
#[async_trait]
impl McpTool for ReadStickyNotesHandler {
fn name(&self) -> &'static str {
"read_sticky_notes"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ReadStickyNotesTool>(
"read_sticky_notes",
"Execute read_sticky_notes",
)
}
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let data = serde_json::to_string(&state.sticky.read()).unwrap_or_else(|_| "[]".to_string());
Ok(data.to_string())
}
}
pub struct DeleteStickyNoteHandler;
#[async_trait]
impl McpTool for DeleteStickyNoteHandler {
fn name(&self) -> &'static str {
"delete_sticky_note"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<DeleteStickyNoteTool>(
"delete_sticky_note",
"Execute delete_sticky_note",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: DeleteStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
state.sticky.modify(|notes| {
if req.index > 0 && req.index <= notes.len() {
notes.remove(req.index - 1);
success = true;
}
});
if success {
Ok("Sticky note deleted.".to_string())
} else {
Err("Invalid sticky note index.".to_string())
}
}
}
pub struct ClearStickyNotesHandler;
#[async_trait]
impl McpTool for ClearStickyNotesHandler {
fn name(&self) -> &'static str {
"clear_sticky_notes"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ClearStickyNotesTool>(
"clear_sticky_notes",
"Execute clear_sticky_notes",
)
}
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
state.sticky.modify(|notes| {
notes.clear();
});
Ok("All sticky notes cleared.".to_string())
}
}
pub struct LeaveHandoffMemoHandler;
#[async_trait]
impl McpTool for LeaveHandoffMemoHandler {
fn name(&self) -> &'static str {
"leave_handoff_memo"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<LeaveHandoffMemoTool>(
"leave_handoff_memo",
"Execute leave_handoff_memo",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: LeaveHandoffMemoTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.handoff_memos.modify(|memos| {
memos.push(crate::models::HandoffMemo {
id: uuid::Uuid::new_v4().to_string(),
author: "agy".to_string(),
content: req.content,
namespace: req.namespace,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
})
});
Ok("Handoff memo left".to_string())
}
}
pub struct ReadHandoffMemosHandler;
#[async_trait]
impl McpTool for ReadHandoffMemosHandler {
fn name(&self) -> &'static str {
"read_handoff_memos"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ReadHandoffMemosTool>(
"read_handoff_memos",
"Execute read_handoff_memos",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ReadHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut items = state.handoff_memos.read();
if let Some(ns) = req.namespace {
items.retain(|i| i.namespace == ns);
}
let data = serde_json::to_string(&items).unwrap_or_default();
Ok(data.to_string())
}
}
pub struct ClearHandoffMemosHandler;
#[async_trait]
impl McpTool for ClearHandoffMemosHandler {
fn name(&self) -> &'static str {
"clear_handoff_memos"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ClearHandoffMemosTool>(
"clear_handoff_memos",
"Execute clear_handoff_memos",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ClearHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let ids: HashSet<_> = req.ids.into_iter().collect();
state
.handoff_memos
.modify(|memos| memos.retain(|m| !ids.contains(&m.id)));
Ok("Handoff memos cleared".to_string())
}
}
pub struct AddSessionSummaryHandler;
#[async_trait]
impl McpTool for AddSessionSummaryHandler {
fn name(&self) -> &'static str {
"add_session_summary"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<AddSessionSummaryTool>(
"add_session_summary",
"Execute add_session_summary",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: AddSessionSummaryTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.session_summaries.modify(|summaries| {
summaries.push(crate::models::SessionSummary {
summary: req.summary,
namespace: req.namespace,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
})
});
Ok("Session summary added".to_string())
}
}
pub struct GenerateStandupReportHandler;
#[async_trait]
impl McpTool for GenerateStandupReportHandler {
fn name(&self) -> &'static str {
"generate_standup_report"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<GenerateStandupReportTool>(
"generate_standup_report",
"Execute generate_standup_report",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: GenerateStandupReportTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let cutoff = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
.saturating_sub(req.hours_lookback * 3600);
let tasks = state
.tasks
.read()
.into_iter()
.filter(|t| t.updated_at >= cutoff)
.collect::<Vec<_>>();
let changes = state
.ledger
.read()
.into_iter()
.filter(|c| c.timestamp >= cutoff)
.collect::<Vec<_>>();
let summaries = state
.session_summaries
.read()
.into_iter()
.filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff)
.collect::<Vec<_>>();
let report = serde_json::json!({ "tasks_updated": tasks, "code_changes": changes, "session_summaries": summaries });
Ok(report.to_string())
}
}