agentic_robotics_core/
service.rs1use crate::error::{Error, Result};
4use crate::message::Message;
5use parking_lot::RwLock;
6use std::sync::Arc;
7use tracing::debug;
8
9pub type ServiceHandler<Req, Res> =
11 Arc<dyn Fn(Req) -> Result<Res> + Send + Sync + 'static>;
12
13pub struct Queryable<Req: Message, Res: Message> {
15 name: String,
16 handler: ServiceHandler<Req, Res>,
17 stats: Arc<RwLock<ServiceStats>>,
18}
19
20#[derive(Debug, Default)]
21struct ServiceStats {
22 pub requests_handled: u64,
23 pub errors: u64,
24}
25
26impl<Req: Message, Res: Message> Queryable<Req, Res> {
27 pub fn new<F>(name: impl Into<String>, handler: F) -> Self
29 where
30 F: Fn(Req) -> Result<Res> + Send + Sync + 'static,
31 {
32 let name = name.into();
33 debug!("Creating queryable service: {}", name);
34
35 Self {
36 name,
37 handler: Arc::new(handler),
38 stats: Arc::new(RwLock::new(ServiceStats::default())),
39 }
40 }
41
42 pub async fn handle(&self, request: Req) -> Result<Res> {
44 let result = (self.handler)(request);
45
46 let mut stats = self.stats.write();
47 stats.requests_handled += 1;
48 if result.is_err() {
49 stats.errors += 1;
50 }
51
52 result
53 }
54
55 pub fn name(&self) -> &str {
57 &self.name
58 }
59
60 pub fn stats(&self) -> (u64, u64) {
62 let stats = self.stats.read();
63 (stats.requests_handled, stats.errors)
64 }
65}
66
67pub struct Service<Req: Message, Res: Message> {
69 name: String,
70 _phantom: std::marker::PhantomData<(Req, Res)>,
71}
72
73impl<Req: Message, Res: Message> Service<Req, Res> {
74 pub fn new(name: impl Into<String>) -> Self {
76 let name = name.into();
77 debug!("Creating service client: {}", name);
78
79 Self {
80 name,
81 _phantom: std::marker::PhantomData,
82 }
83 }
84
85 pub async fn call(&self, _request: Req) -> Result<Res> {
87 Err(Error::Other(anyhow::anyhow!("Service call not implemented")))
89 }
90
91 pub fn name(&self) -> &str {
93 &self.name
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::message::RobotState;
101
102 #[tokio::test]
103 async fn test_queryable() {
104 let queryable = Queryable::new("compute", |req: RobotState| {
105 Ok(RobotState {
106 position: req.position,
107 velocity: [1.0, 2.0, 3.0],
108 timestamp: req.timestamp + 1,
109 })
110 });
111
112 let request = RobotState::default();
113 let response = queryable.handle(request).await.unwrap();
114
115 assert_eq!(response.velocity, [1.0, 2.0, 3.0]);
116
117 let (handled, errors) = queryable.stats();
118 assert_eq!(handled, 1);
119 assert_eq!(errors, 0);
120 }
121
122 #[test]
123 fn test_service_client() {
124 let service = Service::<RobotState, RobotState>::new("compute");
125 assert_eq!(service.name(), "compute");
126 }
127}