Skip to main content

nros_core/
service.rs

1//! ROS 2 Service types
2//!
3//! Services provide synchronous request/response communication.
4//! A service client sends a request and waits for a response from a service server.
5
6use crate::types::RosService;
7
8/// Service server handle
9///
10/// Receives requests and sends responses for a ROS 2 service.
11pub struct ServiceServer<S: RosService> {
12    /// Service name (e.g., "/add_two_ints")
13    pub name: &'static str,
14    /// Marker for service type
15    _marker: core::marker::PhantomData<S>,
16}
17
18impl<S: RosService> ServiceServer<S> {
19    /// Create a new service server handle
20    pub fn new(name: &'static str) -> Self {
21        Self {
22            name,
23            _marker: core::marker::PhantomData,
24        }
25    }
26
27    /// Get the service name
28    pub fn name(&self) -> &str {
29        self.name
30    }
31
32    /// Get the service type name
33    pub fn service_type(&self) -> &'static str {
34        S::SERVICE_NAME
35    }
36
37    /// Get the service type hash
38    pub fn service_hash(&self) -> &'static str {
39        S::SERVICE_HASH
40    }
41}
42
43/// Service client handle
44///
45/// Sends requests and receives responses for a ROS 2 service.
46pub struct ServiceClient<S: RosService> {
47    /// Service name (e.g., "/add_two_ints")
48    pub name: &'static str,
49    /// Marker for service type
50    _marker: core::marker::PhantomData<S>,
51}
52
53impl<S: RosService> ServiceClient<S> {
54    /// Create a new service client handle
55    pub fn new(name: &'static str) -> Self {
56        Self {
57            name,
58            _marker: core::marker::PhantomData,
59        }
60    }
61
62    /// Get the service name
63    pub fn name(&self) -> &str {
64        self.name
65    }
66
67    /// Get the service type name
68    pub fn service_type(&self) -> &'static str {
69        S::SERVICE_NAME
70    }
71
72    /// Get the service type hash
73    pub fn service_hash(&self) -> &'static str {
74        S::SERVICE_HASH
75    }
76}
77
78/// Service request context
79///
80/// Passed to service handlers with request data and means to send a reply.
81pub struct ServiceRequest<'a, S: RosService> {
82    /// The deserialized request message
83    pub request: S::Request,
84    /// Raw request data (CDR encoded)
85    pub raw_data: &'a [u8],
86}
87
88// issue 0783 — `ServiceResult<T> = Result<T, NanoRosError>` used to live here.
89// It was the only consumer of `NanoRosError` in the tree and had no consumers of
90// its own; both went with the dead error module (see `lib.rs`). A service
91// handler's error type is `nros::NodeError`, which is where the alias would have
92// to point if one were wanted again.
93
94/// Synchronous service handler function pointer.
95///
96/// Takes a reference to the deserialized request and returns the reply.
97/// Used by [`ServiceServer`] for simple request-reply patterns.
98pub type ServiceCallback<S> = fn(&<S as RosService>::Request) -> <S as RosService>::Reply;
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    // Mock service for testing
105    struct MockService;
106
107    // Mock types for testing - fields exist for structural completeness but are not
108    // read since the mock serialize/deserialize implementations are no-ops
109    #[derive(Debug, Clone)]
110    struct MockRequest {
111        #[allow(dead_code)]
112        pub a: i32,
113        #[allow(dead_code)]
114        pub b: i32,
115    }
116
117    #[derive(Debug, Clone)]
118    struct MockReply {
119        #[allow(dead_code)]
120        pub sum: i32,
121    }
122
123    // Implement minimal traits for testing
124    impl nros_serdes::Serialize for MockRequest {
125        fn serialize(
126            &self,
127            _writer: &mut nros_serdes::CdrWriter,
128        ) -> Result<(), nros_serdes::SerError> {
129            Ok(())
130        }
131    }
132
133    impl nros_serdes::Deserialize for MockRequest {
134        fn deserialize(
135            _reader: &mut nros_serdes::CdrReader,
136        ) -> Result<Self, nros_serdes::DeserError> {
137            Ok(MockRequest { a: 0, b: 0 })
138        }
139    }
140
141    impl crate::RosMessage for MockRequest {
142        const TYPE_NAME: &'static str = "test_msgs::srv::dds_::AddTwoInts_Request_";
143        const TYPE_HASH: &'static str =
144            "0000000000000000000000000000000000000000000000000000000000000000";
145    }
146
147    impl nros_serdes::Serialize for MockReply {
148        fn serialize(
149            &self,
150            _writer: &mut nros_serdes::CdrWriter,
151        ) -> Result<(), nros_serdes::SerError> {
152            Ok(())
153        }
154    }
155
156    impl nros_serdes::Deserialize for MockReply {
157        fn deserialize(
158            _reader: &mut nros_serdes::CdrReader,
159        ) -> Result<Self, nros_serdes::DeserError> {
160            Ok(MockReply { sum: 0 })
161        }
162    }
163
164    impl crate::RosMessage for MockReply {
165        const TYPE_NAME: &'static str = "test_msgs::srv::dds_::AddTwoInts_Reply_";
166        const TYPE_HASH: &'static str =
167            "0000000000000000000000000000000000000000000000000000000000000000";
168    }
169
170    impl RosService for MockService {
171        type Request = MockRequest;
172        type Reply = MockReply;
173        const SERVICE_NAME: &'static str = "test_msgs::srv::dds_::AddTwoInts_";
174        const SERVICE_HASH: &'static str =
175            "0000000000000000000000000000000000000000000000000000000000000000";
176    }
177
178    #[test]
179    fn test_service_server_creation() {
180        let server = ServiceServer::<MockService>::new("/add_two_ints");
181        assert_eq!(server.name(), "/add_two_ints");
182        assert_eq!(server.service_type(), "test_msgs::srv::dds_::AddTwoInts_");
183    }
184
185    #[test]
186    fn test_service_client_creation() {
187        let client = ServiceClient::<MockService>::new("/add_two_ints");
188        assert_eq!(client.name(), "/add_two_ints");
189        assert_eq!(client.service_type(), "test_msgs::srv::dds_::AddTwoInts_");
190    }
191}