1use crate::hooks::HookContext;
84use crate::Value;
85use std::collections::HashMap;
86use std::sync::{Arc, RwLock};
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
96pub enum Event {
97 BeforeInsert,
99 AfterInsert,
101 BeforeUpdate,
103 AfterUpdate,
105 BeforeDelete,
107 AfterDelete,
109 AfterFind,
111 BeforeRestore,
113 AfterRestore,
115}
116
117impl Event {
118 pub fn is_before(&self) -> bool {
120 matches!(
121 self,
122 Event::BeforeInsert | Event::BeforeUpdate | Event::BeforeDelete | Event::BeforeRestore
123 )
124 }
125
126 pub fn is_after(&self) -> bool {
128 matches!(
129 self,
130 Event::AfterInsert
131 | Event::AfterUpdate
132 | Event::AfterDelete
133 | Event::AfterFind
134 | Event::AfterRestore
135 )
136 }
137
138 pub fn is_write_event(&self) -> bool {
140 matches!(
141 self,
142 Event::BeforeInsert
143 | Event::AfterInsert
144 | Event::BeforeUpdate
145 | Event::AfterUpdate
146 | Event::BeforeDelete
147 | Event::AfterDelete
148 )
149 }
150
151 pub fn name(&self) -> &'static str {
153 match self {
154 Event::BeforeInsert => "before_insert",
155 Event::AfterInsert => "after_insert",
156 Event::BeforeUpdate => "before_update",
157 Event::AfterUpdate => "after_update",
158 Event::BeforeDelete => "before_delete",
159 Event::AfterDelete => "after_delete",
160 Event::AfterFind => "after_find",
161 Event::BeforeRestore => "before_restore",
162 Event::AfterRestore => "after_restore",
163 }
164 }
165}
166
167#[derive(Debug)]
173pub enum SubscriberError {
174 Failed {
176 subscriber: String,
178 reason: String,
180 },
181 Vetoed {
185 subscriber: String,
187 reason: String,
189 },
190}
191
192impl std::fmt::Display for SubscriberError {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 match self {
195 SubscriberError::Failed { subscriber, reason } => {
196 write!(f, "Subscriber `{}` failed: {}", subscriber, reason)
197 }
198 SubscriberError::Vetoed { subscriber, reason } => {
199 write!(f, "Subscriber `{}` vetoed: {}", subscriber, reason)
200 }
201 }
202 }
203}
204
205impl std::error::Error for SubscriberError {}
206
207pub type SubscriberResult<T> = Result<T, SubscriberError>;
209
210pub trait Observer: Send + Sync {
224 fn name(&self) -> &str {
226 "anonymous_observer"
227 }
228
229 fn before_insert(
231 &self,
232 _ctx: &HookContext,
233 _attrs: &mut HashMap<String, Value>,
234 ) -> SubscriberResult<()> {
235 Ok(())
236 }
237
238 fn after_insert(
240 &self,
241 _ctx: &HookContext,
242 _attrs: &HashMap<String, Value>,
243 ) -> SubscriberResult<()> {
244 Ok(())
245 }
246
247 fn before_update(
249 &self,
250 _ctx: &HookContext,
251 _attrs: &mut HashMap<String, Value>,
252 ) -> SubscriberResult<()> {
253 Ok(())
254 }
255
256 fn after_update(
258 &self,
259 _ctx: &HookContext,
260 _attrs: &HashMap<String, Value>,
261 ) -> SubscriberResult<()> {
262 Ok(())
263 }
264
265 fn before_delete(
267 &self,
268 _ctx: &HookContext,
269 _attrs: &HashMap<String, Value>,
270 ) -> SubscriberResult<()> {
271 Ok(())
272 }
273
274 fn after_delete(
276 &self,
277 _ctx: &HookContext,
278 _attrs: &HashMap<String, Value>,
279 ) -> SubscriberResult<()> {
280 Ok(())
281 }
282
283 fn after_find(
285 &self,
286 _ctx: &HookContext,
287 _attrs: &mut HashMap<String, Value>,
288 ) -> SubscriberResult<()> {
289 Ok(())
290 }
291}
292
293pub trait EventSubscriber: Send + Sync {
302 fn name(&self) -> &str {
304 "anonymous_subscriber"
305 }
306
307 fn subscribed_events(&self) -> Vec<Event>;
311
312 fn on_event(
324 &self,
325 event: Event,
326 ctx: &HookContext,
327 attrs: &HashMap<String, Value>,
328 ) -> SubscriberResult<()>;
329}
330
331pub struct EventDispatcher {
359 observers: RwLock<Vec<Arc<dyn Observer>>>,
360 subscribers: RwLock<Vec<Arc<dyn EventSubscriber>>>,
361 errors: RwLock<Vec<SubscriberError>>,
363 max_errors: usize,
368}
369
370const DEFAULT_MAX_ERRORS: usize = 1024;
372
373impl EventDispatcher {
374 pub fn new() -> Self {
376 Self {
377 observers: RwLock::new(Vec::new()),
378 subscribers: RwLock::new(Vec::new()),
379 errors: RwLock::new(Vec::new()),
380 max_errors: DEFAULT_MAX_ERRORS,
381 }
382 }
383
384 pub fn with_max_errors(mut self, max_errors: usize) -> Self {
389 self.max_errors = max_errors;
390 self
391 }
392
393 pub fn add_observer(&self, observer: Box<dyn Observer>) {
398 let arc: Arc<dyn Observer> = Arc::from(observer);
399 self.observers.write().unwrap().push(arc);
400 }
401
402 pub fn subscribe(&self, subscriber: Box<dyn EventSubscriber>) {
406 let arc: Arc<dyn EventSubscriber> = Arc::from(subscriber);
407 self.subscribers.write().unwrap().push(arc);
408 }
409
410 pub fn clear(&self) {
412 self.observers.write().unwrap().clear();
413 self.subscribers.write().unwrap().clear();
414 self.errors.write().unwrap().clear();
415 }
416
417 pub fn observer_count(&self) -> usize {
419 self.observers.read().unwrap().len()
420 }
421
422 pub fn subscriber_count(&self) -> usize {
424 self.subscribers.read().unwrap().len()
425 }
426
427 pub fn drain_errors(&self) -> Vec<SubscriberError> {
429 std::mem::take(&mut *self.errors.write().unwrap())
430 }
431
432 pub fn error_count(&self) -> usize {
434 self.errors.read().unwrap().len()
435 }
436
437 fn push_errors(&self, new_errors: Vec<SubscriberError>) {
442 if new_errors.is_empty() {
443 return;
444 }
445 let mut errors = self.errors.write().unwrap();
446 if self.max_errors == 0 {
447 errors.extend(new_errors);
448 return;
449 }
450 for e in new_errors {
451 if errors.len() >= self.max_errors {
452 errors.remove(0);
454 }
455 errors.push(e);
456 }
457 }
458
459 pub fn dispatch(&self, event: Event, ctx: &HookContext, attrs: &HashMap<String, Value>) {
469 let mut local_errors: Vec<SubscriberError> = Vec::new();
470
471 let observers_snapshot: Vec<Arc<dyn Observer>> = {
473 let observers = self.observers.read().unwrap();
474 observers.clone()
475 };
476 for observer in observers_snapshot.iter() {
478 let result = match event {
479 Event::AfterInsert => observer.after_insert(ctx, attrs),
480 Event::AfterUpdate => observer.after_update(ctx, attrs),
481 Event::AfterDelete => observer.after_delete(ctx, attrs),
482 _ => Ok(()),
483 };
484 if let Err(e) = result {
485 local_errors.push(e);
486 }
487 }
488
489 let subscribers_snapshot: Vec<Arc<dyn EventSubscriber>> = {
491 let subscribers = self.subscribers.read().unwrap();
492 subscribers.clone()
493 };
494 for subscriber in subscribers_snapshot.iter() {
495 if !subscriber.subscribed_events().contains(&event) {
496 continue;
497 }
498 if let Err(e) = subscriber.on_event(event, ctx, attrs) {
499 local_errors.push(e);
500 }
501 }
502
503 if !local_errors.is_empty() {
504 self.push_errors(local_errors);
505 }
506 }
507
508 pub fn dispatch_before_mut(
518 &self,
519 event: Event,
520 ctx: &HookContext,
521 attrs: &mut HashMap<String, Value>,
522 ) -> SubscriberResult<()> {
523 let mut local_errors: Vec<SubscriberError> = Vec::new();
524 let mut vetoed: Option<SubscriberError> = None;
525
526 let observers_snapshot: Vec<Arc<dyn Observer>> = {
528 let observers = self.observers.read().unwrap();
529 observers.clone()
530 };
531 for observer in observers_snapshot.iter() {
532 let result = match event {
533 Event::BeforeInsert => observer.before_insert(ctx, attrs),
534 Event::BeforeUpdate => observer.before_update(ctx, attrs),
535 _ => Ok(()),
536 };
537 match result {
538 Ok(()) => {}
539 Err(e @ SubscriberError::Vetoed { .. }) => {
540 vetoed = Some(e);
541 break;
542 }
543 Err(e) => local_errors.push(e),
544 }
545 }
546
547 if vetoed.is_none() {
549 let subscribers_snapshot: Vec<Arc<dyn EventSubscriber>> = {
550 let subscribers = self.subscribers.read().unwrap();
551 subscribers.clone()
552 };
553 for subscriber in subscribers_snapshot.iter() {
554 if !subscriber.subscribed_events().contains(&event) {
555 continue;
556 }
557 match subscriber.on_event(event, ctx, attrs) {
558 Ok(()) => {}
559 Err(e @ SubscriberError::Vetoed { .. }) => {
560 vetoed = Some(e);
561 break;
562 }
563 Err(e) => local_errors.push(e),
564 }
565 }
566 }
567
568 if !local_errors.is_empty() {
569 self.push_errors(local_errors);
570 }
571
572 if let Some(e) = vetoed {
573 return Err(e);
574 }
575 Ok(())
576 }
577
578 pub fn dispatch_after_find(
585 &self,
586 ctx: &HookContext,
587 attrs: &mut HashMap<String, Value>,
588 ) -> SubscriberResult<()> {
589 let mut local_errors: Vec<SubscriberError> = Vec::new();
590
591 let observers_snapshot: Vec<Arc<dyn Observer>> = {
592 let observers = self.observers.read().unwrap();
593 observers.clone()
594 };
595 for observer in observers_snapshot.iter() {
596 if let Err(e) = observer.after_find(ctx, attrs) {
597 local_errors.push(e);
598 }
599 }
600
601 let subscribers_snapshot: Vec<Arc<dyn EventSubscriber>> = {
602 let subscribers = self.subscribers.read().unwrap();
603 subscribers.clone()
604 };
605 for subscriber in subscribers_snapshot.iter() {
606 if !subscriber.subscribed_events().contains(&Event::AfterFind) {
607 continue;
608 }
609 if let Err(e) = subscriber.on_event(Event::AfterFind, ctx, attrs) {
610 local_errors.push(e);
611 }
612 }
613
614 if !local_errors.is_empty() {
615 self.push_errors(local_errors);
616 }
617
618 Ok(())
619 }
620}
621
622impl Default for EventDispatcher {
623 fn default() -> Self {
624 Self::new()
625 }
626}
627
628#[derive(Clone)]
656pub struct AuditLogSubscriber {
657 logs: std::sync::Arc<std::sync::Mutex<Vec<String>>>,
658}
659
660impl AuditLogSubscriber {
661 pub fn new() -> Self {
663 Self {
664 logs: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
665 }
666 }
667
668 pub fn logs(&self) -> &std::sync::Arc<std::sync::Mutex<Vec<String>>> {
670 &self.logs
671 }
672}
673
674impl Default for AuditLogSubscriber {
675 fn default() -> Self {
676 Self::new()
677 }
678}
679
680impl EventSubscriber for AuditLogSubscriber {
681 fn name(&self) -> &str {
682 "audit_log"
683 }
684
685 fn subscribed_events(&self) -> Vec<Event> {
686 vec![Event::AfterInsert, Event::AfterUpdate, Event::AfterDelete]
687 }
688
689 fn on_event(
690 &self,
691 event: Event,
692 ctx: &HookContext,
693 attrs: &HashMap<String, Value>,
694 ) -> SubscriberResult<()> {
695 let mut logs = self.logs.lock().unwrap();
696 logs.push(format!(
697 "event={} operator={:?} field_count={}",
698 event.name(),
699 ctx.operator_id,
700 attrs.len()
701 ));
702 Ok(())
703 }
704}
705
706#[cfg(test)]
711mod tests {
712 use super::*;
713 use std::sync::{Arc, Mutex};
714
715 #[test]
718 fn test_event_is_before_after() {
719 assert!(Event::BeforeInsert.is_before());
720 assert!(!Event::BeforeInsert.is_after());
721 assert!(Event::AfterInsert.is_after());
722 assert!(!Event::AfterInsert.is_before());
723 }
724
725 #[test]
726 fn test_event_is_write_event() {
727 assert!(Event::BeforeInsert.is_write_event());
728 assert!(Event::AfterUpdate.is_write_event());
729 assert!(Event::BeforeDelete.is_write_event());
730 assert!(!Event::AfterFind.is_write_event());
731 }
732
733 #[test]
734 fn test_event_name() {
735 assert_eq!(Event::BeforeInsert.name(), "before_insert");
736 assert_eq!(Event::AfterDelete.name(), "after_delete");
737 assert_eq!(Event::AfterFind.name(), "after_find");
738 }
739
740 #[test]
743 fn test_new_dispatcher_is_empty() {
744 let d = EventDispatcher::new();
745 assert_eq!(d.observer_count(), 0);
746 assert_eq!(d.subscriber_count(), 0);
747 }
748
749 #[test]
750 fn test_add_observer() {
751 struct DummyObserver;
752 impl Observer for DummyObserver {}
753
754 let d = EventDispatcher::new();
755 d.add_observer(Box::new(DummyObserver));
756 assert_eq!(d.observer_count(), 1);
757 }
758
759 #[test]
760 fn test_subscribe() {
761 struct DummySubscriber;
762 impl EventSubscriber for DummySubscriber {
763 fn subscribed_events(&self) -> Vec<Event> {
764 vec![Event::AfterInsert]
765 }
766 fn on_event(
767 &self,
768 _event: Event,
769 _ctx: &HookContext,
770 _attrs: &HashMap<String, Value>,
771 ) -> SubscriberResult<()> {
772 Ok(())
773 }
774 }
775
776 let d = EventDispatcher::new();
777 d.subscribe(Box::new(DummySubscriber));
778 assert_eq!(d.subscriber_count(), 1);
779 }
780
781 #[test]
782 fn test_clear() {
783 struct DummyObserver;
784 impl Observer for DummyObserver {}
785
786 let d = EventDispatcher::new();
787 d.add_observer(Box::new(DummyObserver));
788 d.clear();
789 assert_eq!(d.observer_count(), 0);
790 }
791
792 struct CountingObserver {
796 insert_count: Arc<Mutex<u32>>,
797 update_count: Arc<Mutex<u32>>,
798 delete_count: Arc<Mutex<u32>>,
799 }
800
801 impl Observer for CountingObserver {
802 fn name(&self) -> &str {
803 "counting"
804 }
805
806 fn after_insert(
807 &self,
808 _ctx: &HookContext,
809 _attrs: &HashMap<String, Value>,
810 ) -> SubscriberResult<()> {
811 *self.insert_count.lock().unwrap() += 1;
812 Ok(())
813 }
814
815 fn after_update(
816 &self,
817 _ctx: &HookContext,
818 _attrs: &HashMap<String, Value>,
819 ) -> SubscriberResult<()> {
820 *self.update_count.lock().unwrap() += 1;
821 Ok(())
822 }
823
824 fn after_delete(
825 &self,
826 _ctx: &HookContext,
827 _attrs: &HashMap<String, Value>,
828 ) -> SubscriberResult<()> {
829 *self.delete_count.lock().unwrap() += 1;
830 Ok(())
831 }
832 }
833
834 #[test]
835 fn test_observer_triggered_on_dispatch() {
836 let insert = Arc::new(Mutex::new(0u32));
837 let update = Arc::new(Mutex::new(0u32));
838 let delete = Arc::new(Mutex::new(0u32));
839
840 let observer = CountingObserver {
841 insert_count: insert.clone(),
842 update_count: update.clone(),
843 delete_count: delete.clone(),
844 };
845
846 let d = EventDispatcher::new();
847 d.add_observer(Box::new(observer));
848
849 let ctx = HookContext::default();
850 let attrs = HashMap::new();
851
852 d.dispatch(Event::AfterInsert, &ctx, &attrs);
853 d.dispatch(Event::AfterInsert, &ctx, &attrs);
854 d.dispatch(Event::AfterUpdate, &ctx, &attrs);
855 d.dispatch(Event::AfterDelete, &ctx, &attrs);
856
857 assert_eq!(*insert.lock().unwrap(), 2);
858 assert_eq!(*update.lock().unwrap(), 1);
859 assert_eq!(*delete.lock().unwrap(), 1);
860 }
861
862 #[test]
863 fn test_observer_before_event_can_modify_attrs() {
864 struct TimestampInjector;
865 impl Observer for TimestampInjector {
866 fn before_insert(
867 &self,
868 _ctx: &HookContext,
869 attrs: &mut HashMap<String, Value>,
870 ) -> SubscriberResult<()> {
871 attrs.insert(
872 "created_at".to_string(),
873 Value::String("2026-07-19".to_string()),
874 );
875 Ok(())
876 }
877 }
878
879 let d = EventDispatcher::new();
880 d.add_observer(Box::new(TimestampInjector));
881
882 let ctx = HookContext::default();
883 let mut attrs = HashMap::new();
884 d.dispatch_before_mut(Event::BeforeInsert, &ctx, &mut attrs)
885 .unwrap();
886
887 assert_eq!(
888 attrs.get("created_at"),
889 Some(&Value::String("2026-07-19".to_string()))
890 );
891 }
892
893 struct InsertOnlySubscriber {
897 called: Arc<Mutex<u32>>,
898 }
899
900 impl EventSubscriber for InsertOnlySubscriber {
901 fn name(&self) -> &str {
902 "insert_only"
903 }
904
905 fn subscribed_events(&self) -> Vec<Event> {
906 vec![Event::AfterInsert]
907 }
908
909 fn on_event(
910 &self,
911 _event: Event,
912 _ctx: &HookContext,
913 _attrs: &HashMap<String, Value>,
914 ) -> SubscriberResult<()> {
915 *self.called.lock().unwrap() += 1;
916 Ok(())
917 }
918 }
919
920 #[test]
921 fn test_subscriber_only_called_for_subscribed_events() {
922 let called = Arc::new(Mutex::new(0u32));
923 let subscriber = InsertOnlySubscriber {
924 called: called.clone(),
925 };
926
927 let d = EventDispatcher::new();
928 d.subscribe(Box::new(subscriber));
929
930 let ctx = HookContext::default();
931 let attrs = HashMap::new();
932
933 d.dispatch(Event::AfterInsert, &ctx, &attrs);
935 d.dispatch(Event::AfterUpdate, &ctx, &attrs);
937 d.dispatch(Event::AfterDelete, &ctx, &attrs);
939 d.dispatch(Event::AfterInsert, &ctx, &attrs);
941
942 assert_eq!(*called.lock().unwrap(), 2);
943 }
944
945 #[test]
946 fn test_subscriber_veto_aborts_before_event() {
947 struct VetoSubscriber;
948 impl EventSubscriber for VetoSubscriber {
949 fn name(&self) -> &str {
950 "veto"
951 }
952 fn subscribed_events(&self) -> Vec<Event> {
953 vec![Event::BeforeInsert]
954 }
955 fn on_event(
956 &self,
957 _event: Event,
958 _ctx: &HookContext,
959 _attrs: &HashMap<String, Value>,
960 ) -> SubscriberResult<()> {
961 Err(SubscriberError::Vetoed {
962 subscriber: "veto".to_string(),
963 reason: "Business rule violation".to_string(),
964 })
965 }
966 }
967
968 let d = EventDispatcher::new();
969 d.subscribe(Box::new(VetoSubscriber));
970
971 let ctx = HookContext::default();
972 let mut attrs = HashMap::new();
973 let result = d.dispatch_before_mut(Event::BeforeInsert, &ctx, &mut attrs);
974
975 assert!(matches!(result, Err(SubscriberError::Vetoed { .. })));
976 }
977
978 #[test]
979 fn test_subscriber_failed_does_not_abort_after_event() {
980 struct FailingSubscriber;
981 impl EventSubscriber for FailingSubscriber {
982 fn name(&self) -> &str {
983 "failing"
984 }
985 fn subscribed_events(&self) -> Vec<Event> {
986 vec![Event::AfterInsert]
987 }
988 fn on_event(
989 &self,
990 _event: Event,
991 _ctx: &HookContext,
992 _attrs: &HashMap<String, Value>,
993 ) -> SubscriberResult<()> {
994 Err(SubscriberError::Failed {
995 subscriber: "failing".to_string(),
996 reason: "Connection lost".to_string(),
997 })
998 }
999 }
1000
1001 struct CountingSubscriber {
1002 called: Arc<Mutex<u32>>,
1003 }
1004 impl EventSubscriber for CountingSubscriber {
1005 fn name(&self) -> &str {
1006 "counting"
1007 }
1008 fn subscribed_events(&self) -> Vec<Event> {
1009 vec![Event::AfterInsert]
1010 }
1011 fn on_event(
1012 &self,
1013 _event: Event,
1014 _ctx: &HookContext,
1015 _attrs: &HashMap<String, Value>,
1016 ) -> SubscriberResult<()> {
1017 *self.called.lock().unwrap() += 1;
1018 Ok(())
1019 }
1020 }
1021
1022 let called = Arc::new(Mutex::new(0u32));
1023 let d = EventDispatcher::new();
1024 d.subscribe(Box::new(FailingSubscriber));
1025 d.subscribe(Box::new(CountingSubscriber {
1026 called: called.clone(),
1027 }));
1028
1029 let ctx = HookContext::default();
1030 let attrs = HashMap::new();
1031 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1032
1033 assert_eq!(*called.lock().unwrap(), 1);
1035 }
1036
1037 #[test]
1040 fn test_audit_log_subscriber() {
1041 let audit = AuditLogSubscriber::new();
1042 let audit_clone = audit.clone();
1043
1044 let d = EventDispatcher::new();
1045 d.subscribe(Box::new(audit_clone));
1046
1047 let ctx = HookContext {
1048 operator_id: Some(42),
1049 ..Default::default()
1050 };
1051 let mut attrs = HashMap::new();
1052 attrs.insert("name".to_string(), Value::String("alice".to_string()));
1053
1054 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1055 d.dispatch(Event::AfterUpdate, &ctx, &attrs);
1056 d.dispatch(Event::AfterDelete, &ctx, &attrs);
1057 d.dispatch_after_find(&ctx, &mut attrs).unwrap();
1059
1060 let logs = audit.logs().lock().unwrap();
1061 assert_eq!(logs.len(), 3);
1062 assert!(logs[0].contains("event=after_insert"));
1063 assert!(logs[0].contains("operator=Some(42)"));
1064 assert!(logs[0].contains("field_count=1"));
1065 }
1066
1067 #[test]
1070 fn test_multiple_subscribers_and_observers() {
1071 let sub1_called = Arc::new(Mutex::new(0u32));
1072 let sub2_called = Arc::new(Mutex::new(0u32));
1073 let obs_called = Arc::new(Mutex::new(0u32));
1074
1075 struct Sub1(Arc<Mutex<u32>>);
1076 impl EventSubscriber for Sub1 {
1077 fn name(&self) -> &str {
1078 "sub1"
1079 }
1080 fn subscribed_events(&self) -> Vec<Event> {
1081 vec![Event::AfterInsert]
1082 }
1083 fn on_event(
1084 &self,
1085 _e: Event,
1086 _c: &HookContext,
1087 _a: &HashMap<String, Value>,
1088 ) -> SubscriberResult<()> {
1089 *self.0.lock().unwrap() += 1;
1090 Ok(())
1091 }
1092 }
1093
1094 struct Sub2(Arc<Mutex<u32>>);
1095 impl EventSubscriber for Sub2 {
1096 fn name(&self) -> &str {
1097 "sub2"
1098 }
1099 fn subscribed_events(&self) -> Vec<Event> {
1100 vec![Event::AfterInsert, Event::AfterUpdate]
1101 }
1102 fn on_event(
1103 &self,
1104 _e: Event,
1105 _c: &HookContext,
1106 _a: &HashMap<String, Value>,
1107 ) -> SubscriberResult<()> {
1108 *self.0.lock().unwrap() += 1;
1109 Ok(())
1110 }
1111 }
1112
1113 struct Obs(Arc<Mutex<u32>>);
1114 impl Observer for Obs {
1115 fn name(&self) -> &str {
1116 "obs"
1117 }
1118 fn after_insert(
1119 &self,
1120 _c: &HookContext,
1121 _a: &HashMap<String, Value>,
1122 ) -> SubscriberResult<()> {
1123 *self.0.lock().unwrap() += 1;
1124 Ok(())
1125 }
1126 }
1127
1128 let d = EventDispatcher::new();
1129 d.subscribe(Box::new(Sub1(sub1_called.clone())));
1130 d.subscribe(Box::new(Sub2(sub2_called.clone())));
1131 d.add_observer(Box::new(Obs(obs_called.clone())));
1132
1133 let ctx = HookContext::default();
1134 let attrs = HashMap::new();
1135
1136 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1137
1138 assert_eq!(*sub1_called.lock().unwrap(), 1);
1139 assert_eq!(*sub2_called.lock().unwrap(), 1);
1140 assert_eq!(*obs_called.lock().unwrap(), 1);
1141 }
1142
1143 #[test]
1146 fn test_drain_errors() {
1147 struct ErrSub;
1148 impl EventSubscriber for ErrSub {
1149 fn name(&self) -> &str {
1150 "err"
1151 }
1152 fn subscribed_events(&self) -> Vec<Event> {
1153 vec![Event::AfterInsert]
1154 }
1155 fn on_event(
1156 &self,
1157 _e: Event,
1158 _c: &HookContext,
1159 _a: &HashMap<String, Value>,
1160 ) -> SubscriberResult<()> {
1161 Err(SubscriberError::Failed {
1162 subscriber: "err".to_string(),
1163 reason: "test".to_string(),
1164 })
1165 }
1166 }
1167
1168 let d = EventDispatcher::new();
1169 d.subscribe(Box::new(ErrSub));
1170
1171 let ctx = HookContext::default();
1172 let attrs = HashMap::new();
1173 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1174 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1175
1176 let errors = d.drain_errors();
1177 assert_eq!(errors.len(), 2);
1178 assert!(matches!(errors[0], SubscriberError::Failed { .. }));
1179
1180 let errors = d.drain_errors();
1182 assert!(errors.is_empty());
1183 }
1184
1185 #[test]
1188 fn test_max_errors_limits_buffer_size() {
1189 struct ErrSub;
1190 impl EventSubscriber for ErrSub {
1191 fn name(&self) -> &str {
1192 "err"
1193 }
1194 fn subscribed_events(&self) -> Vec<Event> {
1195 vec![Event::AfterInsert]
1196 }
1197 fn on_event(
1198 &self,
1199 _e: Event,
1200 _c: &HookContext,
1201 _a: &HashMap<String, Value>,
1202 ) -> SubscriberResult<()> {
1203 Err(SubscriberError::Failed {
1204 subscriber: "err".to_string(),
1205 reason: "test".to_string(),
1206 })
1207 }
1208 }
1209
1210 let d = EventDispatcher::new().with_max_errors(3);
1212 d.subscribe(Box::new(ErrSub));
1213
1214 let ctx = HookContext::default();
1215 let attrs = HashMap::new();
1216 for _ in 0..5 {
1217 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1218 }
1219
1220 assert_eq!(d.error_count(), 3);
1221 let errors = d.drain_errors();
1222 assert_eq!(errors.len(), 3);
1223 }
1224
1225 #[test]
1226 fn test_max_errors_zero_means_unlimited() {
1227 struct ErrSub;
1228 impl EventSubscriber for ErrSub {
1229 fn name(&self) -> &str {
1230 "err"
1231 }
1232 fn subscribed_events(&self) -> Vec<Event> {
1233 vec![Event::AfterInsert]
1234 }
1235 fn on_event(
1236 &self,
1237 _e: Event,
1238 _c: &HookContext,
1239 _a: &HashMap<String, Value>,
1240 ) -> SubscriberResult<()> {
1241 Err(SubscriberError::Failed {
1242 subscriber: "err".to_string(),
1243 reason: "test".to_string(),
1244 })
1245 }
1246 }
1247
1248 let d = EventDispatcher::new().with_max_errors(0);
1249 d.subscribe(Box::new(ErrSub));
1250
1251 let ctx = HookContext::default();
1252 let attrs = HashMap::new();
1253 for _ in 0..10 {
1254 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1255 }
1256
1257 assert_eq!(d.error_count(), 10);
1258 }
1259
1260 #[test]
1261 fn test_max_errors_fifo_eviction_order() {
1262 struct CounterSub(Arc<Mutex<u32>>);
1264 impl EventSubscriber for CounterSub {
1265 fn name(&self) -> &str {
1266 "counter"
1267 }
1268 fn subscribed_events(&self) -> Vec<Event> {
1269 vec![Event::AfterInsert]
1270 }
1271 fn on_event(
1272 &self,
1273 _e: Event,
1274 _c: &HookContext,
1275 _a: &HashMap<String, Value>,
1276 ) -> SubscriberResult<()> {
1277 let mut n = self.0.lock().unwrap();
1278 *n += 1;
1279 Err(SubscriberError::Failed {
1280 subscriber: "counter".to_string(),
1281 reason: format!("call-{}", *n),
1282 })
1283 }
1284 }
1285
1286 let counter = Arc::new(Mutex::new(0u32));
1287 let d = EventDispatcher::new().with_max_errors(2);
1288 d.subscribe(Box::new(CounterSub(counter.clone())));
1289
1290 let ctx = HookContext::default();
1291 let attrs = HashMap::new();
1292 for _ in 0..4 {
1293 d.dispatch(Event::AfterInsert, &ctx, &attrs);
1294 }
1295
1296 let errors = d.drain_errors();
1297 assert_eq!(errors.len(), 2);
1298 match &errors[0] {
1300 SubscriberError::Failed { reason, .. } => assert_eq!(reason, "call-3"),
1301 other => panic!("expected Failed, got {:?}", other),
1302 }
1303 match &errors[1] {
1304 SubscriberError::Failed { reason, .. } => assert_eq!(reason, "call-4"),
1305 other => panic!("expected Failed, got {:?}", other),
1306 }
1307 }
1308
1309 #[test]
1312 fn test_veto_aborts_subsequent_observers() {
1313 let second_called = Arc::new(Mutex::new(0u32));
1314
1315 struct VetoObs;
1316 impl Observer for VetoObs {
1317 fn name(&self) -> &str {
1318 "veto"
1319 }
1320 fn before_insert(
1321 &self,
1322 _c: &HookContext,
1323 _a: &mut HashMap<String, Value>,
1324 ) -> SubscriberResult<()> {
1325 Err(SubscriberError::Vetoed {
1326 subscriber: "veto".to_string(),
1327 reason: "no".to_string(),
1328 })
1329 }
1330 }
1331
1332 struct CountingObs(Arc<Mutex<u32>>);
1333 impl Observer for CountingObs {
1334 fn name(&self) -> &str {
1335 "counting"
1336 }
1337 fn before_insert(
1338 &self,
1339 _c: &HookContext,
1340 _a: &mut HashMap<String, Value>,
1341 ) -> SubscriberResult<()> {
1342 *self.0.lock().unwrap() += 1;
1343 Ok(())
1344 }
1345 }
1346
1347 let d = EventDispatcher::new();
1348 d.add_observer(Box::new(VetoObs));
1349 d.add_observer(Box::new(CountingObs(second_called.clone())));
1350
1351 let ctx = HookContext::default();
1352 let mut attrs = HashMap::new();
1353 let result = d.dispatch_before_mut(Event::BeforeInsert, &ctx, &mut attrs);
1354
1355 assert!(result.is_err());
1356 assert_eq!(*second_called.lock().unwrap(), 0);
1358 }
1359
1360 #[test]
1363 fn test_error_display() {
1364 let e = SubscriberError::Failed {
1365 subscriber: "test".to_string(),
1366 reason: "boom".to_string(),
1367 };
1368 assert!(e.to_string().contains("test"));
1369 assert!(e.to_string().contains("boom"));
1370
1371 let e = SubscriberError::Vetoed {
1372 subscriber: "vetoer".to_string(),
1373 reason: "rejected".to_string(),
1374 };
1375 assert!(e.to_string().contains("vetoer"));
1376 assert!(e.to_string().contains("rejected"));
1377 }
1378}