Skip to main content

research_agent/domain/
research_topic.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
4pub struct ResearchTopic {
5    pub id: String,
6    pub name: String,
7    pub description: String,
8    pub parent_topic_id: Option<String>,
9    pub depth: u32,
10    pub priority: f32,
11    pub created_at: String,
12}
13
14impl ResearchTopic {
15    pub fn new(name: String) -> Self {
16        let now = chrono::Utc::now().to_rfc3339();
17        Self {
18            id: uuid::Uuid::new_v4().to_string(),
19            name,
20            description: String::new(),
21            parent_topic_id: None,
22            depth: 0,
23            priority: 0.5,
24            created_at: now,
25        }
26    }
27
28    /// Create a sub-topic under `parent`, setting `parent_topic_id` and computing
29    /// `depth` from the parent. This is the single source of truth for the depth
30    /// rule, so the CLI and tests cannot diverge from it.
31    pub fn new_subtopic(name: String, parent: &ResearchTopic) -> Self {
32        let mut topic = Self::new(name);
33        topic.parent_topic_id = Some(parent.id.clone());
34        topic.depth = parent.depth + 1;
35        topic
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn topic_new_generates_fields() {
45        let t = ResearchTopic::new("Transformer Architectures".into());
46        assert_eq!(t.name, "Transformer Architectures");
47        assert!(!t.id.is_empty());
48        assert!(t.parent_topic_id.is_none());
49        assert_eq!(t.depth, 0);
50    }
51
52    #[test]
53    fn new_subtopic_inherits_parent_and_depth() {
54        let parent = ResearchTopic::new("ML".into());
55        let child = ResearchTopic::new_subtopic("Deep Learning".into(), &parent);
56        assert_eq!(child.parent_topic_id.as_deref(), Some(parent.id.as_str()));
57        assert_eq!(child.depth, parent.depth + 1);
58    }
59}