1use serde::{Deserialize, Serialize};
2use serde_json::{Value, json};
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub enum ContextSource {
10 File,
12 Project,
14 UserSelection,
16 CanvasNode,
18 StudyBlock,
20 FocusThread,
22 MaterialExcerpt,
24 SessionSummary,
26 Decision,
28 MemorySearch,
30 Other(String),
32}
33
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
40pub struct ContextPacket {
41 #[serde(default)]
43 pub id: Option<String>,
44 pub source: ContextSource,
46 #[serde(default)]
48 pub title: Option<String>,
49 pub content: String,
51 #[serde(default)]
53 pub priority: i32,
54 #[serde(default = "default_context_metadata")]
56 pub metadata: Value,
57}
58
59fn default_context_metadata() -> Value {
60 json!({})
61}
62
63pub fn render_context_packets(packets: &[ContextPacket]) -> Option<String> {
68 if packets.is_empty() {
69 return None;
70 }
71
72 let mut ordered = packets.to_vec();
73 ordered.sort_by_key(|b| std::cmp::Reverse(b.priority));
74
75 let mut rendered = String::from("=== External Context Packets ===\n");
76 for packet in ordered {
77 let title = packet.title.as_deref().unwrap_or("untitled");
78 rendered.push_str(&format!(
79 "- source: {:?}; priority: {}; title: {}\n",
80 packet.source, packet.priority, title
81 ));
82 rendered.push_str(packet.content.trim());
83 rendered.push_str("\n\n");
84 }
85
86 Some(rendered)
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn renders_context_packets_by_priority() {
95 let low = ContextPacket {
96 id: None,
97 source: ContextSource::StudyBlock,
98 title: Some("low".to_string()),
99 content: "later".to_string(),
100 priority: 1,
101 metadata: json!({}),
102 };
103 let high = ContextPacket {
104 id: None,
105 source: ContextSource::FocusThread,
106 title: Some("high".to_string()),
107 content: "now".to_string(),
108 priority: 10,
109 metadata: json!({}),
110 };
111
112 let rendered = render_context_packets(&[low, high]).expect("rendered");
113 assert!(rendered.find("high").unwrap() < rendered.find("low").unwrap());
114 assert!(rendered.contains("now"));
115 assert!(rendered.contains("later"));
116 }
117}