1use std::collections::HashMap;
14
15use appcore_core::{RuntimeError, RuntimeResult};
16
17use crate::api::{ApiRequest, ApiResponse};
18use crate::command_endpoint::CommandEndpoint;
19use crate::query_endpoint::{QueryEndpoint, QueryName};
20
21#[derive(Default)]
23pub struct ApiRouter {
24 command_endpoint: Option<Box<dyn CommandEndpoint>>,
25 queries: HashMap<QueryName, Box<dyn QueryEndpoint>>,
26}
27
28impl ApiRouter {
29 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn set_command_endpoint<E: CommandEndpoint + 'static>(&mut self, endpoint: E) {
36 self.command_endpoint = Some(Box::new(endpoint));
37 }
38
39 pub fn register_query<E: QueryEndpoint + 'static>(&mut self, endpoint: E) -> RuntimeResult<()> {
41 let name = endpoint.query_name().clone();
42 if self.queries.contains_key(&name) {
43 return Err(RuntimeError::RegistryItemAlreadyRegistered {
44 kind: "query",
45 name: name.as_str().to_string(),
46 });
47 }
48 self.queries.insert(name, Box::new(endpoint));
49 Ok(())
50 }
51
52 pub fn has_query(&self, name: &QueryName) -> bool {
54 self.queries.contains_key(name)
55 }
56
57 pub fn query_names(&self) -> Vec<QueryName> {
59 let mut names = self.queries.keys().cloned().collect::<Vec<_>>();
60 names.sort_by(|left, right| left.as_str().cmp(right.as_str()));
61 names
62 }
63
64 pub fn dispatch_query(
66 &self,
67 name: &QueryName,
68 request: ApiRequest,
69 ) -> RuntimeResult<ApiResponse> {
70 let Some(endpoint) = self.queries.get(name) else {
71 return Err(RuntimeError::RegistryItemNotFound {
72 kind: "query",
73 name: name.as_str().to_string(),
74 });
75 };
76 endpoint.handle_query(request)
77 }
78
79 pub fn dispatch_command(&self, request: ApiRequest) -> RuntimeResult<ApiResponse> {
81 let Some(endpoint) = &self.command_endpoint else {
82 return Err(RuntimeError::MissingConfiguration {
83 name: "command_endpoint",
84 });
85 };
86 endpoint.handle_command(request)
87 }
88}
89
90#[cfg(test)]
91#[path = "router_tests.rs"]
92mod tests;