agentic_robotics_core/
service.rs

1//! Service and RPC implementation
2
3use crate::error::{Error, Result};
4use crate::message::Message;
5use parking_lot::RwLock;
6use std::sync::Arc;
7use tracing::debug;
8
9/// Service request handler
10pub type ServiceHandler<Req, Res> =
11    Arc<dyn Fn(Req) -> Result<Res> + Send + Sync + 'static>;
12
13/// Queryable service (RPC)
14pub 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    /// Create a new queryable service
28    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    /// Handle a request
43    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    /// Get service name
56    pub fn name(&self) -> &str {
57        &self.name
58    }
59
60    /// Get statistics
61    pub fn stats(&self) -> (u64, u64) {
62        let stats = self.stats.read();
63        (stats.requests_handled, stats.errors)
64    }
65}
66
67/// Service client
68pub 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    /// Create a new service client
75    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    /// Call the service
86    pub async fn call(&self, _request: Req) -> Result<Res> {
87        // In real implementation, this would call via Zenoh
88        Err(Error::Other(anyhow::anyhow!("Service call not implemented")))
89    }
90
91    /// Get service name
92    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}