pulse_system_types/
lib.rs1pub mod llm;
8pub mod monitoring;
9pub mod plugin;
10pub mod tool;
11
12use serde::{Deserialize, Serialize};
13use std::fmt;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(tag = "status", content = "message")]
18pub enum HealthStatus {
19 Healthy,
20 Degraded(String),
21 Down(String),
22}
23
24impl fmt::Display for HealthStatus {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Self::Healthy => write!(f, "healthy"),
28 Self::Degraded(msg) => write!(f, "degraded: {msg}"),
29 Self::Down(msg) => write!(f, "down: {msg}"),
30 }
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct PluginMeta {
37 pub name: String,
38 pub version: String,
39 pub description: String,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct SetupPrompt {
48 pub key: String,
50 pub question: String,
52 pub default: Option<String>,
54 pub required: bool,
56 pub secret: bool,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct ScheduledTask {
63 pub id: String,
64 pub name: String,
65 pub cron: String,
67 pub channel: String,
69 pub prompt: String,
71 #[serde(default)]
73 pub output_routing: OutputRouting,
74 #[serde(default = "default_true")]
76 pub enabled: bool,
77 #[serde(default)]
79 pub created_by: TaskCreator,
80}
81
82fn default_true() -> bool {
83 true
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
88#[serde(rename_all = "lowercase")]
89pub enum OutputRouting {
90 #[default]
91 Silent,
92 Share,
93 Call,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
98#[serde(rename_all = "lowercase")]
99pub enum TaskCreator {
100 #[default]
101 System,
102 Entity,
103 User,
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn health_status_display() {
112 assert_eq!(HealthStatus::Healthy.to_string(), "healthy");
113 assert_eq!(
114 HealthStatus::Degraded("high latency".into()).to_string(),
115 "degraded: high latency"
116 );
117 assert_eq!(
118 HealthStatus::Down("connection refused".into()).to_string(),
119 "down: connection refused"
120 );
121 }
122
123 #[test]
124 fn health_status_serializes() {
125 let json = serde_json::to_string(&HealthStatus::Healthy).unwrap();
126 assert!(json.contains("Healthy"));
127
128 let json = serde_json::to_string(&HealthStatus::Degraded("slow".into())).unwrap();
129 assert!(json.contains("Degraded"));
130 assert!(json.contains("slow"));
131 }
132
133 #[test]
134 fn plugin_meta_roundtrip() {
135 let meta = PluginMeta {
136 name: "voice-echo".into(),
137 version: "0.7.0".into(),
138 description: "Phone calls via Twilio".into(),
139 };
140 let json = serde_json::to_string(&meta).unwrap();
141 let back: PluginMeta = serde_json::from_str(&json).unwrap();
142 assert_eq!(back.name, "voice-echo");
143 assert_eq!(back.version, "0.7.0");
144 }
145
146 #[test]
147 fn setup_prompt_with_defaults() {
148 let prompt = SetupPrompt {
149 key: "host".into(),
150 question: "Bind address:".into(),
151 default: Some("0.0.0.0".into()),
152 required: false,
153 secret: false,
154 };
155 assert_eq!(prompt.default.as_deref(), Some("0.0.0.0"));
156 assert!(!prompt.secret);
157 }
158
159 #[test]
160 fn setup_prompt_secret() {
161 let prompt = SetupPrompt {
162 key: "api_key".into(),
163 question: "API Key:".into(),
164 default: None,
165 required: true,
166 secret: true,
167 };
168 assert!(prompt.secret);
169 assert!(prompt.required);
170 assert!(prompt.default.is_none());
171 }
172
173 #[test]
174 fn scheduled_task_defaults() {
175 let json = r#"{
176 "id": "morning",
177 "name": "Morning check",
178 "cron": "0 0 8 * * *",
179 "channel": "reflection",
180 "prompt": "Good morning."
181 }"#;
182 let task: ScheduledTask = serde_json::from_str(json).unwrap();
183 assert_eq!(task.output_routing, OutputRouting::Silent);
184 assert!(task.enabled);
185 assert_eq!(task.created_by, TaskCreator::System);
186 }
187
188 #[test]
189 fn output_routing_variants() {
190 let json = r#""share""#;
191 let routing: OutputRouting = serde_json::from_str(json).unwrap();
192 assert_eq!(routing, OutputRouting::Share);
193 }
194}