1use super::config::ChannelConfig;
55use super::traits::{ChannelCore, ChannelMut};
56use orcs_types::{ChannelId, Principal};
57use std::collections::HashSet;
58
59#[derive(Debug, Clone, PartialEq, Eq)]
77pub enum ChannelState {
78 Running,
80
81 Paused,
85
86 AwaitingApproval {
92 request_id: String,
94 },
95
96 Completed,
98
99 Aborted {
103 reason: String,
105 },
106}
107
108#[derive(Debug)]
157pub struct BaseChannel {
158 id: ChannelId,
159 state: ChannelState,
160 parent: Option<ChannelId>,
161 children: HashSet<ChannelId>,
162 config: ChannelConfig,
163 principal: Principal,
165 ancestor_path: Vec<ChannelId>,
168}
169
170impl BaseChannel {
171 #[must_use]
201 pub fn new(
202 id: ChannelId,
203 parent: Option<ChannelId>,
204 config: ChannelConfig,
205 principal: Principal,
206 ) -> Self {
207 Self {
208 id,
209 state: ChannelState::Running,
210 parent,
211 children: HashSet::new(),
212 config,
213 principal,
214 ancestor_path: Vec::new(),
215 }
216 }
217
218 #[must_use]
231 pub fn new_with_ancestors(
232 id: ChannelId,
233 parent: ChannelId,
234 config: ChannelConfig,
235 principal: Principal,
236 ancestor_path: Vec<ChannelId>,
237 ) -> Self {
238 Self {
239 id,
240 state: ChannelState::Running,
241 parent: Some(parent),
242 children: HashSet::new(),
243 config,
244 principal,
245 ancestor_path,
246 }
247 }
248}
249
250impl ChannelCore for BaseChannel {
253 fn id(&self) -> ChannelId {
254 self.id
255 }
256
257 fn principal(&self) -> &Principal {
258 &self.principal
259 }
260
261 fn state(&self) -> &ChannelState {
262 &self.state
263 }
264
265 fn config(&self) -> &ChannelConfig {
266 &self.config
267 }
268
269 fn parent(&self) -> Option<ChannelId> {
270 self.parent
271 }
272
273 fn children(&self) -> &HashSet<ChannelId> {
274 &self.children
275 }
276
277 fn ancestor_path(&self) -> &[ChannelId] {
278 &self.ancestor_path
279 }
280}
281
282impl ChannelMut for BaseChannel {
285 fn complete(&mut self) -> bool {
286 if matches!(self.state, ChannelState::Running) {
287 self.state = ChannelState::Completed;
288 true
289 } else {
290 false
291 }
292 }
293
294 fn abort(&mut self, reason: String) -> bool {
295 if !self.is_terminal() {
296 self.state = ChannelState::Aborted { reason };
297 true
298 } else {
299 false
300 }
301 }
302
303 fn pause(&mut self) -> bool {
304 if matches!(self.state, ChannelState::Running) {
305 self.state = ChannelState::Paused;
306 true
307 } else {
308 false
309 }
310 }
311
312 fn resume(&mut self) -> bool {
313 if matches!(self.state, ChannelState::Paused) {
314 self.state = ChannelState::Running;
315 true
316 } else {
317 false
318 }
319 }
320
321 fn await_approval(&mut self, request_id: String) -> bool {
322 if matches!(self.state, ChannelState::Running) {
323 self.state = ChannelState::AwaitingApproval { request_id };
324 true
325 } else {
326 false
327 }
328 }
329
330 fn resolve_approval(&mut self, approval_id: &str) -> Option<String> {
331 if let ChannelState::AwaitingApproval { request_id } = &self.state {
332 if request_id == approval_id {
333 let id = request_id.clone();
334 self.state = ChannelState::Running;
335 Some(id)
336 } else {
337 None
338 }
339 } else {
340 None
341 }
342 }
343
344 fn add_child(&mut self, id: ChannelId) {
345 self.children.insert(id);
346 }
347
348 fn remove_child(&mut self, id: &ChannelId) {
349 self.children.remove(id);
350 }
351}
352
353pub type Channel = BaseChannel;
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use orcs_types::PrincipalId;
363
364 fn test_principal() -> Principal {
365 Principal::User(PrincipalId::new())
366 }
367
368 #[test]
369 fn channel_creation() {
370 let id = ChannelId::new();
371 let channel = BaseChannel::new(id, None, ChannelConfig::interactive(), test_principal());
372
373 assert_eq!(channel.id(), id);
374 assert!(channel.is_running());
375 assert!(channel.parent().is_none());
376 assert!(!channel.has_children());
377 }
378
379 #[test]
380 fn channel_with_parent() {
381 let parent_id = ChannelId::new();
382 let child_id = ChannelId::new();
383 let channel = BaseChannel::new(
384 child_id,
385 Some(parent_id),
386 ChannelConfig::default(),
387 test_principal(),
388 );
389
390 assert_eq!(channel.parent(), Some(parent_id));
391 }
392
393 #[test]
394 fn channel_principal() {
395 let principal = Principal::System;
396 let channel = BaseChannel::new(
397 ChannelId::new(),
398 None,
399 ChannelConfig::default(),
400 principal.clone(),
401 );
402
403 assert_eq!(channel.principal(), &principal);
404 }
405
406 #[test]
407 fn channel_state_transition() {
408 let id = ChannelId::new();
409 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
410
411 assert!(channel.is_running());
412
413 assert!(channel.complete());
414 assert!(!channel.is_running());
415 assert_eq!(channel.state(), &ChannelState::Completed);
416
417 assert!(!channel.complete());
419 assert!(!channel.abort("test".into()));
420 }
421
422 #[test]
423 fn channel_abort_transition() {
424 let id = ChannelId::new();
425 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
426
427 assert!(channel.abort("reason".into()));
428 assert!(!channel.is_running());
429 assert!(matches!(channel.state(), ChannelState::Aborted { .. }));
430
431 assert!(!channel.complete());
433 }
434
435 #[test]
436 fn channel_children() {
437 let parent_id = ChannelId::new();
438 let child1 = ChannelId::new();
439 let child2 = ChannelId::new();
440
441 let mut parent = BaseChannel::new(
442 parent_id,
443 None,
444 ChannelConfig::interactive(),
445 test_principal(),
446 );
447 assert!(!parent.has_children());
448
449 parent.add_child(child1);
450 parent.add_child(child2);
451 assert!(parent.has_children());
452 assert_eq!(parent.children().len(), 2);
453
454 parent.remove_child(&child1);
455 assert_eq!(parent.children().len(), 1);
456 }
457
458 #[test]
459 fn channel_priority() {
460 let id = ChannelId::new();
461
462 let primary = BaseChannel::new(id, None, ChannelConfig::interactive(), test_principal());
463 assert_eq!(primary.priority(), 255);
464
465 let background = BaseChannel::new(
466 ChannelId::new(),
467 None,
468 ChannelConfig::background(),
469 test_principal(),
470 );
471 assert_eq!(background.priority(), 10);
472
473 let tool = BaseChannel::new(
474 ChannelId::new(),
475 None,
476 ChannelConfig::tool(),
477 test_principal(),
478 );
479 assert_eq!(tool.priority(), 100);
480 }
481
482 #[test]
483 fn channel_can_spawn() {
484 let primary = BaseChannel::new(
485 ChannelId::new(),
486 None,
487 ChannelConfig::interactive(),
488 test_principal(),
489 );
490 assert!(primary.can_spawn());
491
492 let background = BaseChannel::new(
493 ChannelId::new(),
494 None,
495 ChannelConfig::background(),
496 test_principal(),
497 );
498 assert!(!background.can_spawn());
499
500 let tool = BaseChannel::new(
501 ChannelId::new(),
502 None,
503 ChannelConfig::tool(),
504 test_principal(),
505 );
506 assert!(tool.can_spawn());
507 }
508
509 #[test]
510 fn channel_config_access() {
511 let config = ChannelConfig::new(200, false);
512 let channel = BaseChannel::new(ChannelId::new(), None, config, test_principal());
513
514 assert_eq!(channel.config().priority(), 200);
515 assert!(!channel.config().can_spawn());
516 }
517
518 #[test]
521 fn channel_pause_resume() {
522 let id = ChannelId::new();
523 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
524
525 assert!(channel.is_running());
526 assert!(!channel.is_paused());
527
528 assert!(channel.pause());
529 assert!(!channel.is_running());
530 assert!(channel.is_paused());
531
532 assert!(!channel.pause());
534
535 assert!(channel.resume());
536 assert!(channel.is_running());
537 assert!(!channel.is_paused());
538
539 assert!(!channel.resume());
541 }
542
543 #[test]
544 fn channel_await_approval() {
545 let id = ChannelId::new();
546 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
547
548 assert!(channel.await_approval("req-123".to_string()));
549 assert!(channel.is_awaiting_approval());
550 assert!(!channel.is_running());
551
552 assert!(!channel.await_approval("req-456".to_string()));
554
555 if let ChannelState::AwaitingApproval { request_id } = channel.state() {
556 assert_eq!(request_id, "req-123");
557 } else {
558 panic!("Expected AwaitingApproval state");
559 }
560 }
561
562 #[test]
563 fn channel_resolve_approval() {
564 let id = ChannelId::new();
565 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
566
567 assert!(channel.await_approval("req-123".to_string()));
568
569 assert!(channel.resolve_approval("wrong-id").is_none());
571 assert!(channel.is_awaiting_approval());
572
573 let resolved_id = channel.resolve_approval("req-123");
575 assert_eq!(resolved_id, Some("req-123".to_string()));
576 assert!(channel.is_running());
577
578 assert!(channel.resolve_approval("req-123").is_none());
580 }
581
582 #[test]
583 fn channel_abort_from_awaiting() {
584 let id = ChannelId::new();
585 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
586
587 assert!(channel.await_approval("req-123".to_string()));
588 assert!(channel.abort("rejected by user".to_string()));
589
590 assert!(channel.is_terminal());
591 if let ChannelState::Aborted { reason } = channel.state() {
592 assert_eq!(reason, "rejected by user");
593 } else {
594 panic!("Expected Aborted state");
595 }
596 }
597
598 #[test]
599 fn channel_terminal_state() {
600 let id1 = ChannelId::new();
601 let mut ch1 = BaseChannel::new(id1, None, ChannelConfig::default(), test_principal());
602 ch1.complete();
603 assert!(ch1.is_terminal());
604
605 let id2 = ChannelId::new();
606 let mut ch2 = BaseChannel::new(id2, None, ChannelConfig::default(), test_principal());
607 ch2.abort("test".into());
608 assert!(ch2.is_terminal());
609
610 let id3 = ChannelId::new();
611 let ch3 = BaseChannel::new(id3, None, ChannelConfig::default(), test_principal());
612 assert!(!ch3.is_terminal());
613 }
614
615 #[test]
616 fn channel_cannot_pause_from_terminal() {
617 let id = ChannelId::new();
618 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
619 channel.complete();
620
621 assert!(!channel.pause());
622 assert!(!channel.await_approval("req".to_string()));
623 }
624
625 #[test]
626 fn channel_cannot_complete_from_paused() {
627 let id = ChannelId::new();
628 let mut channel = BaseChannel::new(id, None, ChannelConfig::default(), test_principal());
629 assert!(channel.pause());
630
631 assert!(!channel.complete());
633 assert!(channel.resume());
634 assert!(channel.complete());
635 }
636
637 #[test]
640 fn channel_root_has_empty_ancestor_path() {
641 let root = BaseChannel::new(
642 ChannelId::new(),
643 None,
644 ChannelConfig::interactive(),
645 test_principal(),
646 );
647 assert!(root.ancestor_path().is_empty());
648 assert_eq!(root.depth(), 0);
649 }
650
651 #[test]
652 fn channel_with_ancestors() {
653 let root_id = ChannelId::new();
654 let parent_id = ChannelId::new();
655
656 let child = BaseChannel::new_with_ancestors(
657 ChannelId::new(),
658 parent_id,
659 ChannelConfig::default(),
660 test_principal(),
661 vec![parent_id, root_id],
662 );
663
664 assert_eq!(child.ancestor_path(), &[parent_id, root_id]);
665 assert_eq!(child.depth(), 2);
666 }
667
668 #[test]
669 fn channel_is_descendant_of() {
670 let root_id = ChannelId::new();
671 let parent_id = ChannelId::new();
672 let other_id = ChannelId::new();
673
674 let child = BaseChannel::new_with_ancestors(
675 ChannelId::new(),
676 parent_id,
677 ChannelConfig::default(),
678 test_principal(),
679 vec![parent_id, root_id],
680 );
681
682 assert!(child.is_descendant_of(parent_id));
683 assert!(child.is_descendant_of(root_id));
684 assert!(!child.is_descendant_of(other_id));
685 assert!(!child.is_descendant_of(child.id())); }
687
688 #[test]
689 fn channel_root_not_descendant_of_anything() {
690 let root = BaseChannel::new(
691 ChannelId::new(),
692 None,
693 ChannelConfig::interactive(),
694 test_principal(),
695 );
696 let other_id = ChannelId::new();
697
698 assert!(!root.is_descendant_of(other_id));
699 assert!(!root.is_descendant_of(root.id()));
700 }
701}