ralph/notification/
config.rs1#[derive(Debug, Clone, Default)]
18pub struct NotificationOverrides {
19 pub notify_on_complete: Option<bool>,
21 pub notify_on_fail: Option<bool>,
23 pub notify_sound: Option<bool>,
25}
26
27#[derive(Debug, Clone, Default)]
29pub struct NotificationConfig {
30 pub enabled: bool,
32 pub notify_on_complete: bool,
34 pub notify_on_fail: bool,
36 pub notify_on_loop_complete: bool,
38 pub notify_on_watch_new_tasks: bool,
40 pub suppress_when_active: bool,
42 pub sound_enabled: bool,
44 pub sound_path: Option<String>,
47 pub timeout_ms: u32,
49}
50
51impl NotificationConfig {
52 pub fn new() -> Self {
54 Self {
55 enabled: true,
56 notify_on_complete: true,
57 notify_on_fail: true,
58 notify_on_loop_complete: true,
59 notify_on_watch_new_tasks: true,
60 suppress_when_active: true,
61 sound_enabled: false,
62 sound_path: None,
63 timeout_ms: 8000,
64 }
65 }
66
67 pub fn should_suppress(&self, ui_active: bool) -> bool {
69 if !self.enabled {
70 return true;
71 }
72 ui_active && self.suppress_when_active
73 }
74}
75
76pub fn build_notification_config(
80 config: &crate::contracts::NotificationConfig,
81 overrides: &NotificationOverrides,
82) -> NotificationConfig {
83 let notify_on_complete = overrides
84 .notify_on_complete
85 .or(config.notify_on_complete)
86 .unwrap_or(true);
87 let notify_on_fail = overrides
88 .notify_on_fail
89 .or(config.notify_on_fail)
90 .unwrap_or(true);
91 let notify_on_loop_complete = config.notify_on_loop_complete.unwrap_or(true);
92 let notify_on_watch_new_tasks = config.notify_on_watch_new_tasks.unwrap_or(true);
93
94 NotificationConfig {
95 enabled: notify_on_complete
96 || notify_on_fail
97 || notify_on_loop_complete
98 || notify_on_watch_new_tasks,
99 notify_on_complete,
100 notify_on_fail,
101 notify_on_loop_complete,
102 notify_on_watch_new_tasks,
103 suppress_when_active: config.suppress_when_active.unwrap_or(true),
104 sound_enabled: overrides
105 .notify_sound
106 .or(config.sound_enabled)
107 .unwrap_or(false),
108 sound_path: config.sound_path.clone(),
109 timeout_ms: config.timeout_ms.unwrap_or(8000),
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn build_notification_config_uses_defaults() {
119 let config = crate::contracts::NotificationConfig::default();
120 let overrides = NotificationOverrides::default();
121 let result = build_notification_config(&config, &overrides);
122
123 assert!(result.enabled);
124 assert!(result.notify_on_complete);
125 assert!(result.notify_on_fail);
126 assert!(result.notify_on_loop_complete);
127 assert!(result.notify_on_watch_new_tasks);
128 assert!(result.suppress_when_active);
129 assert!(!result.sound_enabled);
130 assert!(result.sound_path.is_none());
131 assert_eq!(result.timeout_ms, 8000);
132 }
133
134 #[test]
135 fn build_notification_config_overrides_take_precedence() {
136 let config = crate::contracts::NotificationConfig {
137 notify_on_complete: Some(false),
138 notify_on_fail: Some(false),
139 sound_enabled: Some(false),
140 ..Default::default()
141 };
142 let overrides = NotificationOverrides {
143 notify_on_complete: Some(true),
144 notify_on_fail: Some(true),
145 notify_sound: Some(true),
146 };
147 let result = build_notification_config(&config, &overrides);
148
149 assert!(result.notify_on_complete);
150 assert!(result.notify_on_fail);
151 assert!(result.sound_enabled);
152 }
153
154 #[test]
155 fn build_notification_config_config_used_when_no_override() {
156 let config = crate::contracts::NotificationConfig {
157 notify_on_complete: Some(false),
158 notify_on_fail: Some(true),
159 suppress_when_active: Some(false),
160 timeout_ms: Some(5000),
161 sound_path: Some("/path/to/sound.wav".to_string()),
162 ..Default::default()
163 };
164 let overrides = NotificationOverrides::default();
165 let result = build_notification_config(&config, &overrides);
166
167 assert!(!result.notify_on_complete);
168 assert!(result.notify_on_fail);
169 assert!(!result.suppress_when_active);
170 assert_eq!(result.timeout_ms, 5000);
171 assert_eq!(result.sound_path, Some("/path/to/sound.wav".to_string()));
172 }
173
174 #[test]
175 fn build_notification_config_enabled_computed_correctly() {
176 let config = crate::contracts::NotificationConfig {
177 notify_on_complete: Some(false),
178 notify_on_fail: Some(false),
179 notify_on_loop_complete: Some(false),
180 notify_on_watch_new_tasks: Some(false),
181 ..Default::default()
182 };
183 let overrides = NotificationOverrides::default();
184 let result = build_notification_config(&config, &overrides);
185 assert!(!result.enabled);
186
187 let config = crate::contracts::NotificationConfig {
188 notify_on_complete: Some(true),
189 notify_on_fail: Some(false),
190 notify_on_loop_complete: Some(false),
191 ..Default::default()
192 };
193 let result = build_notification_config(&config, &overrides);
194 assert!(result.enabled);
195
196 let config = crate::contracts::NotificationConfig {
197 notify_on_complete: Some(false),
198 notify_on_fail: Some(false),
199 notify_on_loop_complete: Some(false),
200 notify_on_watch_new_tasks: Some(true),
201 ..Default::default()
202 };
203 let result = build_notification_config(&config, &overrides);
204 assert!(result.enabled);
205 assert!(result.notify_on_watch_new_tasks);
206 }
207}