forge_runtime/webhook/
registry.rs1use std::collections::HashMap;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7
8use forge_core::Result;
9use forge_core::webhook::{ForgeWebhook, WebhookContext, WebhookInfo, WebhookResult};
10use serde_json::Value;
11
12pub type BoxedWebhookHandler = Arc<
14 dyn Fn(
15 &WebhookContext,
16 Value,
17 ) -> Pin<Box<dyn Future<Output = Result<WebhookResult>> + Send + '_>>
18 + Send
19 + Sync,
20>;
21
22pub struct WebhookEntry {
24 pub info: WebhookInfo,
26 pub handler: BoxedWebhookHandler,
28}
29
30#[derive(Clone, Default)]
32pub struct WebhookRegistry {
33 webhooks: HashMap<String, Arc<WebhookEntry>>,
34 by_path: HashMap<String, String>,
36}
37
38impl WebhookRegistry {
39 pub fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn register<W: ForgeWebhook>(&mut self) {
46 let info = W::info();
47 let name = info.name.to_string();
48 let path = info.path.to_string();
49
50 let handler: BoxedWebhookHandler = Arc::new(move |ctx, payload| W::execute(ctx, payload));
51
52 self.by_path.insert(path, name.clone());
53 self.webhooks
54 .insert(name, Arc::new(WebhookEntry { info, handler }));
55 }
56
57 pub fn get(&self, name: &str) -> Option<Arc<WebhookEntry>> {
59 self.webhooks.get(name).cloned()
60 }
61
62 pub fn get_by_path(&self, path: &str) -> Option<Arc<WebhookEntry>> {
64 self.by_path.get(path).and_then(|name| self.get(name))
65 }
66
67 pub fn info(&self, name: &str) -> Option<&WebhookInfo> {
69 self.webhooks.get(name).map(|e| &e.info)
70 }
71
72 pub fn exists(&self, name: &str) -> bool {
74 self.webhooks.contains_key(name)
75 }
76
77 pub fn path_exists(&self, path: &str) -> bool {
79 self.by_path.contains_key(path)
80 }
81
82 pub fn webhook_names(&self) -> impl Iterator<Item = &str> {
84 self.webhooks.keys().map(|s| s.as_str())
85 }
86
87 pub fn paths(&self) -> impl Iterator<Item = &str> {
89 self.by_path.keys().map(|s| s.as_str())
90 }
91
92 pub fn webhooks(&self) -> impl Iterator<Item = (&str, &Arc<WebhookEntry>)> {
94 self.webhooks.iter().map(|(k, v)| (k.as_str(), v))
95 }
96
97 pub fn len(&self) -> usize {
99 self.webhooks.len()
100 }
101
102 pub fn is_empty(&self) -> bool {
104 self.webhooks.is_empty()
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_registry_new() {
114 let registry = WebhookRegistry::new();
115 assert!(registry.is_empty());
116 }
117}