//! 任务创建参数校验(共享层) //! //! GUI AI 工具(ai/tools/task.rs)与 MCP 工具(crates/df-mcp/src/tools.rs)曾各自内联 //! 一套 create_task 校验(queue 白名单 / parent_id 嵌套铁律 / content_json 合法性), //! 演进易漂移。抽到本模块后两套工具同源复用,一处修改全局生效。 use crate::crud::task_repo::TaskRepo; /// queue 合法值白名单(queue 是独立管理维度,与 status 执行维度正交) pub const TASK_QUEUE_VALUES: &[&str] = &["backlog", "todo", "decision", "active", "done"]; /// 默认优先级(medium)— 新任务默认中优先级,与 IPC create_task 一致 pub const DEFAULT_TASK_PRIORITY: i32 = 2; /// 校验并归一化 create_task 的 queue:空串/缺省 → "todo"; /// 白名单校验 + create 一致性(新建仅 backlog/todo/decision,active/done 须经 move_task_queue 流转)。 pub fn normalize_queue_for_create(queue: Option<&str>) -> Result { match queue { None | Some("") => Ok("todo".to_string()), Some(q) => { let q = q.trim(); if !TASK_QUEUE_VALUES.contains(&q) { return Err(format!("非法 queue 值 {:?},合法值: {:?}", q, TASK_QUEUE_VALUES)); } match q { "backlog" | "todo" | "decision" => Ok(q.to_string()), other => Err(format!( "新建任务 queue 不可直接落 {:?}(须经 move_task_queue 流转),当前仅允许 backlog/todo/decision", other )), } } } } /// 校验 content_json(非空时须是合法 JSON),返回规范化值(None=未传)。 pub fn validate_content_json(content: Option<&str>) -> Result, String> { match content { Some(c) if !c.trim().is_empty() => { serde_json::from_str::(c) .map_err(|e| format!("content_json 不是合法 JSON: {e}"))?; Ok(Some(c.to_string())) } _ => Ok(None), } } /// 校验 parent_id 1 级嵌套铁律:父任务存在 + 父任务自身不是子任务(防孙任务)。 /// 返回归一化 parent_id(空串/缺省 → None)。 pub async fn validate_parent_id( repo: &TaskRepo, parent_id: Option<&str>, ) -> Result, String> { let pid = parent_id .map(|s| s.trim().to_string()) .filter(|s| !s.is_empty()); if let Some(pid) = &pid { let parent = repo .get_by_id(pid) .await .map_err(|e| e.to_string())? .ok_or_else(|| format!("父任务 {pid} 不存在,无法创建子任务"))?; if parent.parent_id.is_some() { return Err(format!( "违反 1 级嵌套铁律:父任务 {pid} 自身是子任务(parent_id={:?}),不允许在其下创建孙任务", parent.parent_id )); } } Ok(pid) } /// 校验并归一化 create_task 的 priority:缺省 → DEFAULT_TASK_PRIORITY;越界值拒绝(值域 0..=3)。 pub fn normalize_priority(priority: Option) -> Result { match priority { None => Ok(DEFAULT_TASK_PRIORITY), Some(p) if (0..=3).contains(&p) => Ok(p as i32), Some(p) => Err(format!( "非法 priority 值 {p},合法值: 整数 0..=3(0=critical, 1=high, 2=medium, 3=low)" )), } }