dynamic_dispatch/
dynamic_dispatch.rs

1use actor12::{Actor, Call, Handler, Init, MpscChannel, Multi, spawn};
2use futures::future;
3use std::collections::HashMap;
4use std::future::Future;
5
6// A router actor that can handle different types of requests dynamically
7pub struct RouterActor {
8    routes: HashMap<String, String>,
9    request_count: u32,
10}
11
12// Different request types
13#[derive(Debug)]
14pub struct GetRouteRequest {
15    pub path: String,
16}
17
18#[derive(Debug)]  
19pub struct AddRouteRequest {
20    pub path: String,
21    pub handler: String,
22}
23
24#[derive(Debug)]
25pub struct ListRoutesRequest;
26
27#[derive(Debug)]
28pub struct StatsRequest;
29
30#[derive(Debug)]
31pub struct ResetRequest;
32
33// Response types
34#[derive(Debug)]
35pub struct RouteResponse {
36    pub found: bool,
37    pub handler: Option<String>,
38}
39
40impl Actor for RouterActor {
41    type Spec = ();
42    type Message = Multi<Self>;
43    type Channel = MpscChannel<Self::Message>;  
44    type Cancel = ();
45    type State = ();
46
47    fn state(_spec: &Self::Spec) -> Self::State {}
48
49    fn init(_ctx: Init<'_, Self>) -> impl Future<Output = Result<Self, Self::Cancel>> + Send + 'static {
50        println!("Router actor initialized");
51        future::ready(Ok(RouterActor {
52            routes: HashMap::new(),
53            request_count: 0,
54        }))
55    }
56}
57
58// Handle route lookup
59impl Handler<GetRouteRequest> for RouterActor {
60    type Reply = Result<RouteResponse, anyhow::Error>;
61
62    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: GetRouteRequest) -> Self::Reply {
63        self.request_count += 1;
64        
65        let handler = self.routes.get(&msg.path).cloned();
66        let found = handler.is_some();
67        
68        println!("GET {}: {} (handler: {:?})", msg.path, if found { "FOUND" } else { "NOT_FOUND" }, handler);
69        
70        Ok(RouteResponse { found, handler })
71    }
72}
73
74// Handle route registration
75impl Handler<AddRouteRequest> for RouterActor {
76    type Reply = Result<bool, anyhow::Error>;
77
78    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: AddRouteRequest) -> Self::Reply {
79        self.request_count += 1;
80        
81        let was_existing = self.routes.contains_key(&msg.path);
82        self.routes.insert(msg.path.clone(), msg.handler.clone());
83        
84        println!("ROUTE ADDED: {} -> {} ({})", 
85                 msg.path, msg.handler, 
86                 if was_existing { "UPDATED" } else { "NEW" });
87        
88        Ok(!was_existing) // true if it was a new route
89    }
90}
91
92// Handle listing all routes
93impl Handler<ListRoutesRequest> for RouterActor {
94    type Reply = Result<Vec<(String, String)>, anyhow::Error>;
95
96    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, _msg: ListRoutesRequest) -> Self::Reply {
97        self.request_count += 1;
98        
99        let routes: Vec<(String, String)> = self.routes.iter()
100            .map(|(k, v)| (k.clone(), v.clone()))
101            .collect();
102            
103        println!("LISTING {} routes:", routes.len());
104        for (path, handler) in &routes {
105            println!("  {} -> {}", path, handler);
106        }
107        
108        Ok(routes)
109    }
110}
111
112// Handle stats request  
113impl Handler<StatsRequest> for RouterActor {
114    type Reply = Result<(u32, usize), anyhow::Error>;
115
116    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, _msg: StatsRequest) -> Self::Reply {
117        let stats = (self.request_count, self.routes.len());
118        println!("STATS: {} total requests, {} registered routes", stats.0, stats.1);
119        Ok(stats)
120    }
121}
122
123// Handle reset request
124impl Handler<ResetRequest> for RouterActor {
125    type Reply = Result<(), anyhow::Error>;
126
127    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, _msg: ResetRequest) -> Self::Reply {
128        let old_count = self.request_count;
129        let old_routes = self.routes.len();
130        
131        self.routes.clear();
132        self.request_count = 0;
133        
134        println!("RESET: Cleared {} routes, reset request count from {}", old_routes, old_count);
135        Ok(())
136    }
137}
138
139// Handle raw string commands (dynamic dispatch example)
140impl Handler<String> for RouterActor {
141    type Reply = Result<String, anyhow::Error>;
142
143    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: String) -> Self::Reply {
144        self.request_count += 1;
145        
146        let parts: Vec<&str> = msg.split_whitespace().collect();
147        match parts.as_slice() {
148            ["help"] => {
149                let help = "Available commands:\n\
150                           - help: Show this help\n\
151                           - status: Show current status\n\
152                           - echo <text>: Echo back the text";
153                println!("HELP requested");
154                Ok(help.to_string())
155            }
156            ["status"] => {
157                let status = format!("Router status: {} requests, {} routes", 
158                                   self.request_count, self.routes.len());
159                println!("STATUS requested");
160                Ok(status)
161            }
162            ["echo", rest @ ..] => {
163                let text = rest.join(" ");
164                let response = format!("Echo: {}", text);
165                println!("ECHO: {}", text);
166                Ok(response)
167            }
168            _ => {
169                let response = format!("Unknown command: '{}'. Try 'help'", msg);
170                println!("UNKNOWN COMMAND: {}", msg);
171                Ok(response)
172            }
173        }
174    }
175}
176
177#[tokio::main]
178async fn main() -> anyhow::Result<()> {
179    println!("=== Dynamic Dispatch Example ===\n");
180
181    let router = spawn::<RouterActor>(());
182
183    // Test typed message handlers
184    println!("1. Setting up routes:");
185    
186    let _: Result<bool, anyhow::Error> = router.ask_dyn(AddRouteRequest {
187        path: "/api/users".to_string(),
188        handler: "UserHandler".to_string(),
189    }).await;
190    
191    let _: Result<bool, anyhow::Error> = router.ask_dyn(AddRouteRequest {
192        path: "/api/posts".to_string(), 
193        handler: "PostHandler".to_string(),
194    }).await;
195
196    let _: Result<bool, anyhow::Error> = router.ask_dyn(AddRouteRequest {
197        path: "/health".to_string(),
198        handler: "HealthHandler".to_string(), 
199    }).await;
200
201    println!("\n2. Testing route lookups:");
202    
203    let response: Result<RouteResponse, anyhow::Error> = router.ask_dyn(GetRouteRequest {
204        path: "/api/users".to_string(),
205    }).await;
206    println!("Lookup result: {:?}", response);
207
208    let response: Result<RouteResponse, anyhow::Error> = router.ask_dyn(GetRouteRequest {
209        path: "/nonexistent".to_string(),
210    }).await;
211    println!("Lookup result: {:?}", response);
212
213    println!("\n3. Listing all routes:");
214    let routes: Result<Vec<(String, String)>, anyhow::Error> = router.ask_dyn(ListRoutesRequest).await;
215    println!("Routes: {:?}", routes);
216
217    println!("\n4. Testing dynamic string commands:");
218    
219    let help: Result<String, anyhow::Error> = router.ask_dyn("help".to_string()).await;
220    println!("Help response: {:?}", help);
221
222    let status: Result<String, anyhow::Error> = router.ask_dyn("status".to_string()).await;
223    println!("Status response: {:?}", status);
224
225    let echo: Result<String, anyhow::Error> = router.ask_dyn("echo Hello dynamic world!".to_string()).await;
226    println!("Echo response: {:?}", echo);
227
228    let unknown: Result<String, anyhow::Error> = router.ask_dyn("unknown command".to_string()).await;
229    println!("Unknown command response: {:?}", unknown);
230
231    println!("\n5. Final stats:");
232    let stats: Result<(u32, usize), anyhow::Error> = router.ask_dyn(StatsRequest).await;
233    println!("Final stats: {:?}", stats);
234
235    Ok(())
236}