1use std::collections::{hash_map::Entry, HashMap};
25use std::sync::Arc;
26
27use async_trait::async_trait;
28
29#[cfg(feature = "selected-wire-transport-adapter")]
30use crate::payload::codec::{DecodePayload, EncodePayload, PayloadCodec};
31#[cfg(feature = "selected-wire-transport-adapter")]
32use crate::wire::UWireDecodeOwned;
33#[cfg(feature = "selected-wire-transport-adapter")]
34use crate::wire::UWireEncode;
35#[cfg(feature = "selected-wire-transport-adapter")]
36use crate::wire_transport::UHasWire;
37use crate::{
38 communication::{
39 CallOptions, NotificationError, PubSubError, RegistrationError, ServiceInvocationError,
40 SubscriptionChangeHandler, SubscriptionStatus, UPayload,
41 },
42 LocalUriProvider, UCode, UListener, UMessage, UMessageBuilder, UOwnedFrame, UOwnedListener,
43 UOwnedTransport, UStatus, UUri, UUID,
44};
45
46pub use crate::communication::RequestHandler;
47
48pub struct Endpoint<T, P>
55where
56 T: UOwnedTransport + ?Sized,
57 P: LocalUriProvider + ?Sized,
58{
59 transport: Arc<T>,
60 uri_provider: Arc<P>,
61}
62
63impl<T, P> core::fmt::Debug for Endpoint<T, P>
64where
65 T: UOwnedTransport + ?Sized,
66 P: LocalUriProvider + ?Sized,
67{
68 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69 f.debug_struct("Endpoint").finish_non_exhaustive()
70 }
71}
72
73impl<T, P> Endpoint<T, P>
74where
75 T: UOwnedTransport + ?Sized,
76 P: LocalUriProvider + ?Sized,
77{
78 #[must_use]
80 pub fn new(transport: Arc<T>, uri_provider: Arc<P>) -> Self {
81 Self {
82 transport,
83 uri_provider,
84 }
85 }
86
87 #[must_use]
89 pub fn publisher(&self) -> Publisher<T, P> {
90 Publisher::new(self.transport.clone(), self.uri_provider.clone())
91 }
92
93 #[must_use]
98 pub fn subscriber(
99 &self,
100 usubscription: Arc<dyn crate::core::usubscription::USubscription>,
101 ) -> Subscriber<T> {
102 Subscriber::new(self.transport.clone(), usubscription)
103 }
104
105 #[must_use]
107 pub fn notifier(&self) -> Notifier<T, P> {
108 Notifier::new(self.transport.clone(), self.uri_provider.clone())
109 }
110
111 #[must_use]
113 pub fn rpc_client(&self) -> RpcClient<T, P> {
114 RpcClient::new(self.transport.clone(), self.uri_provider.clone())
115 }
116}
117
118impl<T, P> Endpoint<T, P>
119where
120 T: UOwnedTransport + ?Sized + 'static,
121 P: LocalUriProvider + ?Sized,
122{
123 #[must_use]
125 pub fn rpc_server(&self) -> RpcServer<T, P> {
126 RpcServer::new(self.transport.clone(), self.uri_provider.clone())
127 }
128}
129
130fn build_message_with_payload<S: crate::umessage::BuilderState>(
131 builder: &mut UMessageBuilder<S>,
132 payload: Option<UPayload>,
133) -> Result<UMessage, crate::UMessageError> {
134 match payload {
135 Some(payload) => {
136 builder.build_with_payload(payload.payload().clone(), payload.payload_format())
137 }
138 None => builder.build(),
139 }
140}
141
142fn frame_from_message(message: &UMessage) -> Result<UOwnedFrame, crate::UFrameMetadataError> {
143 let metadata = crate::frame::metadata::try_project_umessage_to_frame_metadata(message)?;
144 UOwnedFrame::new(metadata, message.payload())
145}
146
147fn message_from_frame(frame: UOwnedFrame) -> Result<UMessage, crate::UFrameMetadataError> {
148 crate::frame::metadata::try_project_frame_to_umessage(
149 frame.metadata().clone(),
150 frame.payload().cloned(),
151 )
152}
153
154fn listener_pointer(listener: &Arc<dyn UListener>) -> usize {
155 let ptr = Arc::as_ptr(listener);
156 let thin_ptr = ptr as *const ();
157 thin_ptr as usize
158}
159
160#[derive(Clone, Debug, Eq, Hash, PartialEq)]
161struct MessageListenerKey {
162 source_filter: UUri,
163 sink_filter: Option<UUri>,
164 listener: usize,
165}
166
167impl MessageListenerKey {
168 fn new(
169 source_filter: &UUri,
170 sink_filter: Option<&UUri>,
171 listener: &Arc<dyn UListener>,
172 ) -> Self {
173 Self {
174 source_filter: source_filter.clone(),
175 sink_filter: sink_filter.cloned(),
176 listener: listener_pointer(listener),
177 }
178 }
179}
180
181struct MessageListener {
182 inner: Arc<dyn UListener>,
183}
184
185#[async_trait]
186impl UOwnedListener for MessageListener {
187 async fn on_receive_owned(&self, frame: UOwnedFrame) {
188 if let Ok(message) = message_from_frame(frame) {
189 self.inner.on_receive(message).await;
190 }
191 }
192}
193
194type MessageListenerMap = tokio::sync::Mutex<HashMap<MessageListenerKey, Arc<dyn UOwnedListener>>>;
195
196async fn register_message_listener<T>(
197 transport: &T,
198 listeners: &MessageListenerMap,
199 source_filter: &UUri,
200 sink_filter: Option<&UUri>,
201 listener: Arc<dyn UListener>,
202) -> Result<(), RegistrationError>
203where
204 T: UOwnedTransport + ?Sized,
205{
206 let key = MessageListenerKey::new(source_filter, sink_filter, &listener);
207 if listeners.lock().await.contains_key(&key) {
208 return Err(RegistrationError::AlreadyExists);
209 }
210 let owned_listener: Arc<dyn UOwnedListener> = Arc::new(MessageListener { inner: listener });
211 transport
212 .register_owned_listener(source_filter, sink_filter, owned_listener.clone())
213 .await
214 .map_err(RegistrationError::from)?;
215
216 let mut listeners = listeners.lock().await;
217 match listeners.entry(key) {
218 Entry::Vacant(entry) => {
219 entry.insert(owned_listener);
220 Ok(())
221 }
222 Entry::Occupied(_) => {
223 drop(listeners);
224 let _ = transport
225 .unregister_owned_listener(source_filter, sink_filter, owned_listener)
226 .await;
227 Err(RegistrationError::AlreadyExists)
228 }
229 }
230}
231
232async fn unregister_message_listener<T>(
233 transport: &T,
234 listeners: &MessageListenerMap,
235 source_filter: &UUri,
236 sink_filter: Option<&UUri>,
237 listener: Arc<dyn UListener>,
238) -> Result<(), RegistrationError>
239where
240 T: UOwnedTransport + ?Sized,
241{
242 let key = MessageListenerKey::new(source_filter, sink_filter, &listener);
243 let Some(owned_listener) = listeners.lock().await.remove(&key) else {
244 return Err(RegistrationError::NoSuchListener);
245 };
246 if let Err(error) = transport
247 .unregister_owned_listener(source_filter, sink_filter, owned_listener)
248 .await
249 {
250 return Err(RegistrationError::from(error));
251 }
252 Ok(())
253}
254
255pub struct Subscriber<T>
263where
264 T: UOwnedTransport + ?Sized,
265{
266 transport: Arc<T>,
267 usubscription: Arc<dyn crate::core::usubscription::USubscription>,
268 listeners: MessageListenerMap,
269}
270
271impl<T> core::fmt::Debug for Subscriber<T>
272where
273 T: UOwnedTransport + ?Sized,
274{
275 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
276 f.debug_struct("Subscriber").finish_non_exhaustive()
277 }
278}
279
280impl<T> Subscriber<T>
281where
282 T: UOwnedTransport + ?Sized,
283{
284 pub fn new(
287 transport: Arc<T>,
288 usubscription: Arc<dyn crate::core::usubscription::USubscription>,
289 ) -> Self {
290 Self {
291 transport,
292 usubscription,
293 listeners: MessageListenerMap::default(),
294 }
295 }
296
297 pub async fn subscribe(
300 &self,
301 topic: &UUri,
302 handler: Arc<dyn UListener>,
303 subscription_change_handler: Option<Arc<dyn SubscriptionChangeHandler>>,
304 ) -> Result<(), RegistrationError> {
305 super::validate_listener_topic(topic)?;
306 let state = self
307 .usubscription
308 .subscribe(topic, None, None)
309 .await
310 .map_err(|status| RegistrationError::Unknown(Box::new(status)))?;
311 if state != SubscriptionStatus::Subscribed && state != SubscriptionStatus::SubscribePending
312 {
313 return Err(RegistrationError::Unknown(Box::new(
314 crate::UStatus::fail_with_code(
315 crate::UCode::FailedPrecondition,
316 format!("uSubscription service returned {state:?}"),
317 ),
318 )));
319 }
320 register_message_listener(&*self.transport, &self.listeners, topic, None, handler).await?;
321 if let Some(handler) = subscription_change_handler {
322 handler.on_subscription_change(topic.clone(), state);
323 }
324 Ok(())
325 }
326
327 pub async fn unsubscribe(
330 &self,
331 topic: &UUri,
332 handler: Arc<dyn UListener>,
333 ) -> Result<(), RegistrationError> {
334 super::validate_listener_topic(topic)?;
335 self.usubscription
336 .unsubscribe(topic)
337 .await
338 .map_err(|status| RegistrationError::Unknown(Box::new(status)))?;
339 unregister_message_listener(&*self.transport, &self.listeners, topic, None, handler).await
340 }
341}
342
343#[async_trait]
344impl<T> crate::communication::Subscriber for Subscriber<T>
345where
346 T: UOwnedTransport + ?Sized,
347{
348 async fn subscribe(
349 &self,
350 topic: &UUri,
351 handler: Arc<dyn UListener>,
352 subscription_change_handler: Option<Arc<dyn SubscriptionChangeHandler>>,
353 ) -> Result<(), RegistrationError> {
354 Subscriber::subscribe(self, topic, handler, subscription_change_handler).await
355 }
356
357 async fn unsubscribe(
358 &self,
359 topic: &UUri,
360 handler: Arc<dyn UListener>,
361 ) -> Result<(), RegistrationError> {
362 Subscriber::unsubscribe(self, topic, handler).await
363 }
364}
365
366pub struct Notifier<T, P>
368where
369 T: UOwnedTransport + ?Sized,
370 P: LocalUriProvider + ?Sized,
371{
372 transport: Arc<T>,
373 uri_provider: Arc<P>,
374 listeners: MessageListenerMap,
375}
376
377impl<T, P> core::fmt::Debug for Notifier<T, P>
378where
379 T: UOwnedTransport + ?Sized,
380 P: LocalUriProvider + ?Sized,
381{
382 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
383 f.debug_struct("Notifier").finish_non_exhaustive()
384 }
385}
386
387impl<T, P> Notifier<T, P>
388where
389 T: UOwnedTransport + ?Sized,
390 P: LocalUriProvider + ?Sized,
391{
392 #[must_use]
394 pub fn new(transport: Arc<T>, uri_provider: Arc<P>) -> Self {
395 Self {
396 transport,
397 uri_provider,
398 listeners: tokio::sync::Mutex::new(HashMap::new()),
399 }
400 }
401
402 fn build_frame(
403 &self,
404 resource_id: u16,
405 destination: &UUri,
406 call_options: CallOptions,
407 payload: Option<UPayload>,
408 ) -> Result<UOwnedFrame, NotificationError> {
409 let mut builder = UMessageBuilder::notification(
410 self.uri_provider.get_resource_uri(resource_id),
411 destination.clone(),
412 );
413 builder.with_ttl(call_options.ttl());
414 if let Some(message_id) = call_options.message_id() {
415 builder.with_message_id(message_id.clone());
416 }
417 if let Some(priority) = call_options.priority() {
418 builder.with_priority(priority);
419 }
420 let message = build_message_with_payload(&mut builder, payload)
421 .map_err(|error| NotificationError::InvalidArgument(error.to_string()))?;
422 frame_from_message(&message)
423 .map_err(|error| NotificationError::InvalidArgument(error.to_string()))
424 }
425
426 pub async fn notify(
428 &self,
429 resource_id: u16,
430 destination: &UUri,
431 call_options: CallOptions,
432 payload: Option<UPayload>,
433 ) -> Result<(), NotificationError> {
434 let frame = self.build_frame(resource_id, destination, call_options, payload)?;
435 self.transport
436 .send_owned(frame)
437 .await
438 .map_err(Box::from)
439 .map_err(NotificationError::NotifyError)
440 }
441
442 pub async fn start_listening(
444 &self,
445 topic: &UUri,
446 listener: Arc<dyn UListener>,
447 ) -> Result<(), RegistrationError> {
448 super::validate_listener_topic(topic)?;
449 register_message_listener(
450 &*self.transport,
451 &self.listeners,
452 topic,
453 Some(&self.uri_provider.get_source_uri()),
454 listener,
455 )
456 .await
457 }
458
459 pub async fn stop_listening(
461 &self,
462 topic: &UUri,
463 listener: Arc<dyn UListener>,
464 ) -> Result<(), RegistrationError> {
465 super::validate_listener_topic(topic)?;
466 unregister_message_listener(
467 &*self.transport,
468 &self.listeners,
469 topic,
470 Some(&self.uri_provider.get_source_uri()),
471 listener,
472 )
473 .await
474 }
475}
476
477#[async_trait]
478impl<T, P> crate::communication::Notifier for Notifier<T, P>
479where
480 T: UOwnedTransport + ?Sized,
481 P: LocalUriProvider + ?Sized,
482{
483 async fn notify(
484 &self,
485 resource_id: u16,
486 destination: &UUri,
487 call_options: CallOptions,
488 payload: Option<UPayload>,
489 ) -> Result<(), NotificationError> {
490 Notifier::notify(self, resource_id, destination, call_options, payload).await
491 }
492
493 async fn start_listening(
494 &self,
495 topic: &UUri,
496 listener: Arc<dyn UListener>,
497 ) -> Result<(), RegistrationError> {
498 Notifier::start_listening(self, topic, listener).await
499 }
500
501 async fn stop_listening(
502 &self,
503 topic: &UUri,
504 listener: Arc<dyn UListener>,
505 ) -> Result<(), RegistrationError> {
506 Notifier::stop_listening(self, topic, listener).await
507 }
508}
509
510pub struct RpcClient<T, P>
512where
513 T: UOwnedTransport + ?Sized,
514 P: LocalUriProvider + ?Sized,
515{
516 transport: Arc<T>,
517 uri_provider: Arc<P>,
518}
519
520impl<T, P> core::fmt::Debug for RpcClient<T, P>
521where
522 T: UOwnedTransport + ?Sized,
523 P: LocalUriProvider + ?Sized,
524{
525 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
526 f.debug_struct("RpcClient").finish_non_exhaustive()
527 }
528}
529
530impl<T, P> RpcClient<T, P>
531where
532 T: UOwnedTransport + ?Sized,
533 P: LocalUriProvider + ?Sized,
534{
535 #[must_use]
537 pub fn new(transport: Arc<T>, uri_provider: Arc<P>) -> Self {
538 Self {
539 transport,
540 uri_provider,
541 }
542 }
543
544 fn build_request_frame(
545 &self,
546 method: UUri,
547 call_options: &CallOptions,
548 message_id: UUID,
549 payload: Option<UPayload>,
550 ) -> Result<UOwnedFrame, ServiceInvocationError> {
551 let mut builder = UMessageBuilder::request(
552 method,
553 self.uri_provider.get_source_uri(),
554 call_options.ttl(),
555 );
556 builder.with_message_id(message_id);
557 if let Some(token) = call_options.token() {
558 builder.with_token(token.clone());
559 }
560 if let Some(priority) = call_options.priority() {
561 builder.with_priority(priority);
562 }
563 let message = build_message_with_payload(&mut builder, payload)
564 .map_err(|error| ServiceInvocationError::InvalidArgument(error.to_string()))?;
565 frame_from_message(&message)
566 .map_err(|error| ServiceInvocationError::InvalidArgument(error.to_string()))
567 }
568
569 fn response_payload(response: UMessage) -> Result<Option<UPayload>, ServiceInvocationError> {
570 match response.commstatus() {
571 Some(UCode::Ok) | None => {
572 let payload_format = response
573 .payload_format()
574 .unwrap_or(crate::UPayloadFormat::Unspecified);
575 Ok(response
576 .payload()
577 .map(|payload| UPayload::new(payload, payload_format)))
578 }
579 Some(code) => {
580 let status = response.extract_protobuf().unwrap_or_else(|_| {
581 UStatus::fail_with_code(code, "failed to invoke service operation")
582 });
583 Err(ServiceInvocationError::from(status))
584 }
585 }
586 }
587
588 pub async fn invoke_method(
590 &self,
591 method: UUri,
592 call_options: CallOptions,
593 payload: Option<UPayload>,
594 ) -> Result<Option<UPayload>, ServiceInvocationError> {
595 let message_id = call_options
596 .message_id()
597 .map_or_else(UUID::build, Clone::clone);
598 let request_frame =
599 self.build_request_frame(method.clone(), &call_options, message_id.clone(), payload)?;
600 self.transport.send_owned(request_frame).await?;
601 let response_frame = self
602 .transport
603 .receive_owned(&method, Some(&self.uri_provider.get_source_uri()))
604 .await?;
605 let response = crate::frame::metadata::try_project_frame_to_umessage(
606 response_frame.metadata().clone(),
607 response_frame.payload().cloned(),
608 )
609 .map_err(|error| ServiceInvocationError::InvalidArgument(error.to_string()))?;
610 if response.request_id_unchecked() != &message_id {
611 return Err(ServiceInvocationError::Internal(
612 "received RPC response for unexpected request ID".to_string(),
613 ));
614 }
615 Self::response_payload(response)
616 }
617}
618
619#[cfg(feature = "selected-wire-transport-adapter")]
620impl<T, P> RpcClient<T, P>
621where
622 T: UOwnedTransport + UHasWire + ?Sized,
623 P: LocalUriProvider + ?Sized,
624{
625 pub async fn invoke_typed<Request, Response>(
627 &self,
628 method: UUri,
629 call_options: CallOptions,
630 request: &Request,
631 ) -> Result<Response, ServiceInvocationError>
632 where
633 T::Wire: UWireEncode<Request> + UWireDecodeOwned<Response>,
634 {
635 let payload_bytes = <T::Wire as EncodePayload<Request>>::encode_payload_owned(request)
636 .map_err(|error| ServiceInvocationError::InvalidArgument(error.to_string()))?;
637 let payload_format = <T::Wire as PayloadCodec>::payload_encoding()
638 .to_legacy_format()
639 .ok_or_else(|| {
640 ServiceInvocationError::InvalidArgument(
641 "selected wire uses a native-only payload encoding that cannot be sent by P73U2 owned RPC"
642 .to_string(),
643 )
644 })?;
645 let response = self
646 .invoke_method(
647 method,
648 call_options,
649 Some(UPayload::new(payload_bytes, payload_format)),
650 )
651 .await?
652 .ok_or_else(|| ServiceInvocationError::InvalidArgument("No payload".to_string()))?;
653 <T::Wire as DecodePayload<'_, Response>>::decode_payload(response.payload())
654 .map_err(|error| ServiceInvocationError::InvalidArgument(error.to_string()))
655 }
656}
657
658#[async_trait]
659impl<T, P> crate::communication::RpcClient for RpcClient<T, P>
660where
661 T: UOwnedTransport + ?Sized + 'static,
662 P: LocalUriProvider + ?Sized,
663{
664 async fn invoke_method(
665 &self,
666 method: UUri,
667 call_options: CallOptions,
668 payload: Option<UPayload>,
669 ) -> Result<Option<UPayload>, ServiceInvocationError> {
670 RpcClient::invoke_method(self, method, call_options, payload).await
671 }
672}
673
674struct RequestListener<T>
675where
676 T: UOwnedTransport + ?Sized + 'static,
677{
678 request_handler: Arc<dyn crate::communication::RequestHandler>,
679 transport: Arc<T>,
680}
681
682impl<T> RequestListener<T>
683where
684 T: UOwnedTransport + ?Sized + 'static,
685{
686 async fn process_request(&self, request_frame: UOwnedFrame) {
687 let Ok(request_message) = crate::frame::metadata::try_project_frame_to_umessage(
688 request_frame.metadata().clone(),
689 request_frame.payload().cloned(),
690 ) else {
691 return;
692 };
693 if !request_message.is_request() {
694 return;
695 }
696 let resource_id = request_message.sink_unchecked().resource_id();
697 let payload_format = request_message
698 .payload_format()
699 .unwrap_or(crate::UPayloadFormat::Unspecified);
700 let request_payload = request_message
701 .payload()
702 .map(|payload| UPayload::new(payload, payload_format));
703 let mut response_builder =
704 UMessageBuilder::response_for_request(request_message.attributes());
705 let response = match self
706 .request_handler
707 .handle_request(resource_id, request_message.attributes(), request_payload)
708 .await
709 {
710 Ok(response_payload) => {
711 build_message_with_payload(&mut response_builder, response_payload)
712 }
713 Err(error) => {
714 let status = UStatus::from(error);
715 response_builder
716 .with_comm_status(status.code())
717 .build_with_protobuf_payload(&status)
718 }
719 };
720 if let Ok(response_message) = response {
721 if let Ok(response_frame) = frame_from_message(&response_message) {
722 let _ = self.transport.send_owned(response_frame).await;
723 }
724 }
725 }
726}
727
728#[async_trait]
729impl<T> UOwnedListener for RequestListener<T>
730where
731 T: UOwnedTransport + ?Sized + 'static,
732{
733 async fn on_receive_owned(&self, frame: UOwnedFrame) {
734 self.process_request(frame).await;
735 }
736}
737
738pub struct RpcServer<T, P>
740where
741 T: UOwnedTransport + ?Sized,
742 P: LocalUriProvider + ?Sized,
743{
744 transport: Arc<T>,
745 uri_provider: Arc<P>,
746 request_listeners: tokio::sync::Mutex<std::collections::HashMap<u16, Arc<dyn UOwnedListener>>>,
747}
748
749impl<T, P> core::fmt::Debug for RpcServer<T, P>
750where
751 T: UOwnedTransport + ?Sized,
752 P: LocalUriProvider + ?Sized,
753{
754 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
755 f.debug_struct("RpcServer").finish_non_exhaustive()
756 }
757}
758
759impl<T, P> RpcServer<T, P>
760where
761 T: UOwnedTransport + ?Sized + 'static,
762 P: LocalUriProvider + ?Sized,
763{
764 #[must_use]
766 pub fn new(transport: Arc<T>, uri_provider: Arc<P>) -> Self {
767 Self {
768 transport,
769 uri_provider,
770 request_listeners: tokio::sync::Mutex::new(std::collections::HashMap::new()),
771 }
772 }
773
774 fn validate_sink_filter(filter: &UUri) -> Result<(), RegistrationError> {
775 if !filter.is_rpc_method() {
776 return Err(RegistrationError::InvalidFilter(
777 "RPC endpoint's resource ID must be in range [0x0001, 0x7FFF]".to_string(),
778 ));
779 }
780 Ok(())
781 }
782
783 fn validate_origin_filter(filter: Option<&UUri>) -> Result<(), RegistrationError> {
784 if let Some(uri) = filter {
785 if !uri.is_rpc_response() {
786 return Err(RegistrationError::InvalidFilter(
787 "origin filter's resource ID must be 0".to_string(),
788 ));
789 }
790 }
791 Ok(())
792 }
793
794 pub async fn register_endpoint(
796 &self,
797 origin_filter: Option<&UUri>,
798 resource_id: u16,
799 request_handler: Arc<dyn crate::communication::RequestHandler>,
800 ) -> Result<(), RegistrationError> {
801 Self::validate_origin_filter(origin_filter)?;
802 let sink_filter = self.uri_provider.get_resource_uri(resource_id);
803 Self::validate_sink_filter(&sink_filter)?;
804 let mut listener_map = self.request_listeners.lock().await;
805 if listener_map.contains_key(&resource_id) {
806 return Err(RegistrationError::MaxListenersExceeded);
807 }
808 let listener: Arc<dyn UOwnedListener> = Arc::new(RequestListener {
809 request_handler,
810 transport: self.transport.clone(),
811 });
812 self.transport
813 .register_owned_listener(
814 origin_filter.unwrap_or(&UUri::any_with_resource_id(
815 crate::uri::RESOURCE_ID_RESPONSE,
816 )),
817 Some(&sink_filter),
818 listener.clone(),
819 )
820 .await
821 .map_err(RegistrationError::from)?;
822 listener_map.insert(resource_id, listener);
823 Ok(())
824 }
825
826 pub async fn unregister_endpoint(
828 &self,
829 origin_filter: Option<&UUri>,
830 resource_id: u16,
831 _request_handler: Arc<dyn crate::communication::RequestHandler>,
832 ) -> Result<(), RegistrationError> {
833 Self::validate_origin_filter(origin_filter)?;
834 let sink_filter = self.uri_provider.get_resource_uri(resource_id);
835 Self::validate_sink_filter(&sink_filter)?;
836 let mut listener_map = self.request_listeners.lock().await;
837 let Some(listener) = listener_map.remove(&resource_id) else {
838 return Err(RegistrationError::NoSuchListener);
839 };
840 self.transport
841 .unregister_owned_listener(
842 origin_filter.unwrap_or(&UUri::any_with_resource_id(
843 crate::uri::RESOURCE_ID_RESPONSE,
844 )),
845 Some(&sink_filter),
846 listener,
847 )
848 .await
849 .map_err(RegistrationError::from)
850 }
851}
852
853#[async_trait]
854impl<T, P> crate::communication::RpcServer for RpcServer<T, P>
855where
856 T: UOwnedTransport + ?Sized + 'static,
857 P: LocalUriProvider + ?Sized,
858{
859 async fn register_endpoint(
860 &self,
861 origin_filter: Option<&UUri>,
862 resource_id: u16,
863 request_handler: Arc<dyn crate::communication::RequestHandler>,
864 ) -> Result<(), RegistrationError> {
865 RpcServer::register_endpoint(self, origin_filter, resource_id, request_handler).await
866 }
867
868 async fn unregister_endpoint(
869 &self,
870 origin_filter: Option<&UUri>,
871 resource_id: u16,
872 request_handler: Arc<dyn crate::communication::RequestHandler>,
873 ) -> Result<(), RegistrationError> {
874 RpcServer::unregister_endpoint(self, origin_filter, resource_id, request_handler).await
875 }
876}
877
878pub struct Publisher<T, P>
880where
881 T: UOwnedTransport + ?Sized,
882 P: LocalUriProvider + ?Sized,
883{
884 transport: Arc<T>,
885 uri_provider: Arc<P>,
886}
887
888impl<T, P> core::fmt::Debug for Publisher<T, P>
889where
890 T: UOwnedTransport + ?Sized,
891 P: LocalUriProvider + ?Sized,
892{
893 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
894 f.debug_struct("Publisher").finish_non_exhaustive()
895 }
896}
897
898impl<T, P> Publisher<T, P>
899where
900 T: UOwnedTransport + ?Sized,
901 P: LocalUriProvider + ?Sized,
902{
903 #[must_use]
905 pub fn new(transport: Arc<T>, uri_provider: Arc<P>) -> Self {
906 Self {
907 transport,
908 uri_provider,
909 }
910 }
911
912 fn build_frame(
913 &self,
914 resource_id: u16,
915 call_options: CallOptions,
916 payload: Option<UPayload>,
917 ) -> Result<UOwnedFrame, PubSubError> {
918 let mut builder = UMessageBuilder::publish(self.uri_provider.get_resource_uri(resource_id));
919 builder.with_ttl(call_options.ttl());
920 if let Some(message_id) = call_options.message_id() {
921 builder.with_message_id(message_id.clone());
922 }
923 if let Some(priority) = call_options.priority() {
924 builder.with_priority(priority);
925 }
926 let message = match payload {
927 Some(payload) => {
928 builder.build_with_payload(payload.payload().clone(), payload.payload_format())
929 }
930 None => builder.build(),
931 }
932 .map_err(|error| {
933 PubSubError::InvalidArgument(format!(
934 "failed to create Publish message from parameters: {error}"
935 ))
936 })?;
937 let metadata = crate::frame::metadata::try_project_umessage_to_frame_metadata(&message)
938 .map_err(|error| {
939 PubSubError::InvalidArgument(format!(
940 "failed to create owned Publish frame metadata from parameters: {error}"
941 ))
942 })?;
943 UOwnedFrame::new(metadata, message.payload()).map_err(|error| {
944 PubSubError::InvalidArgument(format!(
945 "failed to create owned Publish frame from parameters: {error}"
946 ))
947 })
948 }
949
950 pub async fn publish(
952 &self,
953 resource_id: u16,
954 call_options: CallOptions,
955 payload: Option<UPayload>,
956 ) -> Result<(), PubSubError> {
957 let frame = self.build_frame(resource_id, call_options, payload)?;
958 self.transport
959 .send_owned(frame)
960 .await
961 .map_err(Box::from)
962 .map_err(PubSubError::PublishError)
963 }
964}
965
966#[cfg(feature = "selected-wire-transport-adapter")]
967impl<T, P> Publisher<T, P>
968where
969 T: UOwnedTransport + UHasWire + ?Sized,
970 P: LocalUriProvider + ?Sized,
971{
972 pub async fn publish_typed<Payload>(
974 &self,
975 resource_id: u16,
976 call_options: CallOptions,
977 payload: &Payload,
978 ) -> Result<(), PubSubError>
979 where
980 T::Wire: UWireEncode<Payload>,
981 {
982 let payload_bytes = <T::Wire as EncodePayload<Payload>>::encode_payload_owned(payload)
983 .map_err(|error| PubSubError::InvalidArgument(error.to_string()))?;
984 let payload_format = <T::Wire as PayloadCodec>::payload_encoding()
985 .to_legacy_format()
986 .ok_or_else(|| {
987 PubSubError::InvalidArgument(
988 "selected wire uses a native-only payload encoding that cannot be sent by P73U1 owned publish"
989 .to_string(),
990 )
991 })?;
992 self.publish(
993 resource_id,
994 call_options,
995 Some(UPayload::new(payload_bytes, payload_format)),
996 )
997 .await
998 }
999}
1000
1001#[async_trait]
1002impl<T, P> crate::communication::Publisher for Publisher<T, P>
1003where
1004 T: UOwnedTransport + ?Sized,
1005 P: LocalUriProvider + ?Sized,
1006{
1007 async fn publish(
1008 &self,
1009 resource_id: u16,
1010 call_options: CallOptions,
1011 payload: Option<UPayload>,
1012 ) -> Result<(), PubSubError> {
1013 Publisher::publish(self, resource_id, call_options, payload).await
1014 }
1015}
1016
1017#[cfg(test)]
1018mod tests {
1019 use std::collections::VecDeque;
1020 use std::sync::Mutex;
1021
1022 use super::*;
1023 use crate::{
1024 StaticUriProvider, UAttributes, UCode, UOwnedFrame, UOwnedTransportImpl, UPayloadFormat,
1025 UStatus,
1026 };
1027
1028 struct RecordingOwnedTransport {
1029 sent: Mutex<Vec<UOwnedFrame>>,
1030 received: Mutex<VecDeque<UOwnedFrame>>,
1031 listener: Mutex<Option<Arc<dyn UOwnedListener>>>,
1032 lifecycle: Option<Arc<Mutex<Vec<&'static str>>>>,
1033 }
1034
1035 impl RecordingOwnedTransport {
1036 fn new() -> Self {
1037 Self {
1038 sent: Mutex::new(Vec::new()),
1039 received: Mutex::new(VecDeque::new()),
1040 listener: Mutex::new(None),
1041 lifecycle: None,
1042 }
1043 }
1044
1045 fn with_lifecycle(lifecycle: Arc<Mutex<Vec<&'static str>>>) -> Self {
1046 Self {
1047 lifecycle: Some(lifecycle),
1048 ..Self::new()
1049 }
1050 }
1051
1052 fn record_lifecycle(&self, event: &'static str) {
1053 if let Some(lifecycle) = &self.lifecycle {
1054 lifecycle
1055 .lock()
1056 .expect("lifecycle lock poisoned")
1057 .push(event);
1058 }
1059 }
1060
1061 fn sent_frames(&self) -> Vec<UOwnedFrame> {
1062 self.sent.lock().expect("sent lock poisoned").clone()
1063 }
1064
1065 fn push_received(&self, frame: UOwnedFrame) {
1066 self.received
1067 .lock()
1068 .expect("received lock poisoned")
1069 .push_back(frame);
1070 }
1071
1072 fn registered_listener(&self) -> Arc<dyn UOwnedListener> {
1073 self.listener
1074 .lock()
1075 .expect("listener lock poisoned")
1076 .as_ref()
1077 .expect("listener registered")
1078 .clone()
1079 }
1080 }
1081
1082 #[async_trait]
1083 impl UOwnedTransportImpl for RecordingOwnedTransport {
1084 async fn send_validated_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus> {
1085 self.sent.lock().expect("sent lock poisoned").push(frame);
1086 Ok(())
1087 }
1088
1089 async fn receive_validated_owned(
1090 &self,
1091 _source_filter: &UUri,
1092 _sink_filter: Option<&UUri>,
1093 ) -> Result<UOwnedFrame, UStatus> {
1094 self.received
1095 .lock()
1096 .expect("received lock poisoned")
1097 .pop_front()
1098 .ok_or_else(|| UStatus::fail_with_code(UCode::NotFound, "no queued frame"))
1099 }
1100
1101 async fn register_validated_owned_listener(
1102 &self,
1103 _source_filter: &UUri,
1104 _sink_filter: Option<&UUri>,
1105 listener: Arc<dyn UOwnedListener>,
1106 ) -> Result<(), UStatus> {
1107 self.record_lifecycle("register");
1108 *self.listener.lock().expect("listener lock poisoned") = Some(listener);
1109 Ok(())
1110 }
1111
1112 async fn unregister_validated_owned_listener(
1113 &self,
1114 _source_filter: &UUri,
1115 _sink_filter: Option<&UUri>,
1116 _listener: Arc<dyn UOwnedListener>,
1117 ) -> Result<(), UStatus> {
1118 self.record_lifecycle("unregister");
1119 *self.listener.lock().expect("listener lock poisoned") = None;
1120 Ok(())
1121 }
1122 }
1123
1124 fn uri_provider() -> Arc<StaticUriProvider> {
1125 Arc::new(StaticUriProvider::new("", 0x0005, 0x02).expect("uri provider"))
1126 }
1127
1128 fn method_uri() -> UUri {
1129 uri_provider().get_resource_uri(0x1000)
1130 }
1131
1132 fn message_frame(message: &UMessage) -> UOwnedFrame {
1133 frame_from_message(message).expect("frame")
1134 }
1135
1136 #[derive(Default)]
1137 struct RecordingMessageListener {
1138 messages: Mutex<Vec<UMessage>>,
1139 }
1140
1141 impl RecordingMessageListener {
1142 fn messages(&self) -> Vec<UMessage> {
1143 self.messages
1144 .lock()
1145 .expect("messages lock poisoned")
1146 .clone()
1147 }
1148 }
1149
1150 #[async_trait]
1151 impl UListener for RecordingMessageListener {
1152 async fn on_receive(&self, msg: UMessage) {
1153 self.messages
1154 .lock()
1155 .expect("messages lock poisoned")
1156 .push(msg);
1157 }
1158 }
1159
1160 #[tokio::test]
1161 async fn publish_sends_owned_frame() {
1162 let transport = Arc::new(RecordingOwnedTransport::new());
1163 let publisher = Publisher::new(transport.clone(), uri_provider());
1164
1165 publisher
1166 .publish(
1167 0x9A00,
1168 CallOptions::for_publish(None, None, None),
1169 Some(UPayload::new("hello", UPayloadFormat::Text)),
1170 )
1171 .await
1172 .expect("publish succeeds");
1173
1174 let frames = transport.sent_frames();
1175 assert_eq!(frames.len(), 1);
1176 let frame = frames.first().expect("one sent frame");
1177 assert_eq!(frame.payload(), Some(&bytes::Bytes::from_static(b"hello")));
1178 assert_eq!(
1179 frame
1180 .metadata()
1181 .payload_encoding()
1182 .and_then(crate::PayloadEncoding::to_legacy_format),
1183 Some(UPayloadFormat::Text)
1184 );
1185 }
1186
1187 #[tokio::test]
1188 async fn publish_rejects_invalid_topic() {
1189 let transport = Arc::new(RecordingOwnedTransport::new());
1190 let publisher = Publisher::new(transport.clone(), uri_provider());
1191
1192 let result = publisher
1193 .publish(0x1000, CallOptions::for_publish(None, None, None), None)
1194 .await;
1195
1196 assert!(matches!(result, Err(PubSubError::InvalidArgument(_))));
1197 assert!(transport.sent_frames().is_empty());
1198 }
1199
1200 struct FailingOwnedTransport;
1201
1202 #[async_trait]
1203 impl UOwnedTransportImpl for FailingOwnedTransport {
1204 async fn send_validated_owned(&self, _frame: UOwnedFrame) -> Result<(), UStatus> {
1205 Err(UStatus::fail_with_code(
1206 UCode::Unavailable,
1207 "transport unavailable",
1208 ))
1209 }
1210 }
1211
1212 #[tokio::test]
1213 async fn publish_maps_transport_error() {
1214 let publisher = Publisher::new(Arc::new(FailingOwnedTransport), uri_provider());
1215
1216 let result = publisher
1217 .publish(0x9A00, CallOptions::for_publish(None, None, None), None)
1218 .await;
1219
1220 assert!(matches!(result, Err(PubSubError::PublishError(_))));
1221 }
1222
1223 #[derive(Default)]
1224 struct StubUSubscription {
1225 lifecycle: Option<Arc<Mutex<Vec<&'static str>>>>,
1226 }
1227
1228 impl StubUSubscription {
1229 fn recording(lifecycle: Arc<Mutex<Vec<&'static str>>>) -> Self {
1230 Self {
1231 lifecycle: Some(lifecycle),
1232 }
1233 }
1234
1235 fn record_lifecycle(&self, event: &'static str) {
1236 if let Some(lifecycle) = &self.lifecycle {
1237 lifecycle
1238 .lock()
1239 .expect("lifecycle lock poisoned")
1240 .push(event);
1241 }
1242 }
1243 }
1244
1245 #[async_trait]
1246 impl crate::core::usubscription::USubscription for StubUSubscription {
1247 async fn subscribe(
1248 &self,
1249 _topic: &UUri,
1250 _expiration: Option<u64>,
1251 _min_sample_period: Option<u32>,
1252 ) -> Result<SubscriptionStatus, crate::UStatus> {
1253 self.record_lifecycle("subscribe");
1254 Ok(SubscriptionStatus::Subscribed)
1255 }
1256 async fn unsubscribe(&self, _topic: &UUri) -> Result<(), crate::UStatus> {
1257 self.record_lifecycle("unsubscribe");
1258 Ok(())
1259 }
1260 async fn fetch_subscriptions_by_topic(
1261 &self,
1262 _topic: &UUri,
1263 ) -> Result<Vec<crate::core::usubscription::SubscriptionInfo>, crate::UStatus> {
1264 Ok(Vec::new())
1265 }
1266 async fn fetch_subscriptions_by_subscriber(
1267 &self,
1268 _subscriber: &UUri,
1269 ) -> Result<Vec<crate::core::usubscription::SubscriptionInfo>, crate::UStatus> {
1270 Ok(Vec::new())
1271 }
1272 async fn register_for_notifications(&self, _topic: &UUri) -> Result<(), crate::UStatus> {
1273 Ok(())
1274 }
1275 async fn unregister_for_notifications(&self, _topic: &UUri) -> Result<(), crate::UStatus> {
1276 Ok(())
1277 }
1278 async fn fetch_subscribers(&self, _topic: &UUri) -> Result<Vec<UUri>, crate::UStatus> {
1279 Ok(Vec::new())
1280 }
1281 async fn reset(
1282 &self,
1283 _reason: crate::core::usubscription::ResetReason,
1284 _message: Option<String>,
1285 ) -> Result<(), crate::UStatus> {
1286 Ok(())
1287 }
1288 }
1289
1290 #[tokio::test]
1291 async fn subscriber_registers_owned_listener_and_delivers_messages() {
1292 let transport = Arc::new(RecordingOwnedTransport::new());
1293 let subscriber = Endpoint::new(transport.clone(), uri_provider())
1294 .subscriber(Arc::new(StubUSubscription::default()));
1295 let topic = uri_provider().get_resource_uri(0x9A00);
1296 let listener = Arc::new(RecordingMessageListener::default());
1297
1298 subscriber
1299 .subscribe(&topic, listener.clone(), None)
1300 .await
1301 .expect("subscriber registered");
1302 let message = UMessageBuilder::publish(topic.clone())
1303 .build_with_payload("event", UPayloadFormat::Text)
1304 .expect("publish message");
1305 transport
1306 .registered_listener()
1307 .on_receive_owned(message_frame(&message))
1308 .await;
1309
1310 let messages = listener.messages();
1311 assert_eq!(messages.len(), 1);
1312 let message = messages.first().expect("one received message");
1313 assert!(message.is_publish());
1314 assert_eq!(message.payload(), Some(bytes::Bytes::from_static(b"event")));
1315
1316 subscriber
1317 .unsubscribe(&topic, listener)
1318 .await
1319 .expect("subscriber unregistered");
1320 }
1321
1322 #[tokio::test]
1323 async fn subscriber_contacts_usubscription_before_listener_changes() {
1324 let lifecycle = Arc::new(Mutex::new(Vec::new()));
1325 let transport = Arc::new(RecordingOwnedTransport::with_lifecycle(lifecycle.clone()));
1326 let subscriber = Endpoint::new(transport, uri_provider())
1327 .subscriber(Arc::new(StubUSubscription::recording(lifecycle.clone())));
1328 let topic = uri_provider().get_resource_uri(0x9A00);
1329 let listener = Arc::new(RecordingMessageListener::default());
1330
1331 subscriber
1332 .subscribe(&topic, listener.clone(), None)
1333 .await
1334 .expect("subscriber registered");
1335 subscriber
1336 .unsubscribe(&topic, listener)
1337 .await
1338 .expect("subscriber unregistered");
1339
1340 assert_eq!(
1341 *lifecycle.lock().expect("lifecycle lock poisoned"),
1342 ["subscribe", "register", "unsubscribe", "unregister"]
1343 );
1344 }
1345
1346 #[tokio::test]
1347 async fn notifier_sends_and_listens_with_owned_frames() {
1348 let transport = Arc::new(RecordingOwnedTransport::new());
1349 let notifier = Endpoint::new(transport.clone(), uri_provider()).notifier();
1350 let topic = uri_provider().get_resource_uri(0xD100);
1351 let listener = Arc::new(RecordingMessageListener::default());
1352
1353 notifier
1354 .start_listening(&topic, listener.clone())
1355 .await
1356 .expect("notifier listener registered");
1357 let notification =
1358 UMessageBuilder::notification(topic.clone(), uri_provider().get_source_uri())
1359 .build_with_payload("notification", UPayloadFormat::Text)
1360 .expect("notification message");
1361 transport
1362 .registered_listener()
1363 .on_receive_owned(message_frame(¬ification))
1364 .await;
1365
1366 notifier
1367 .notify(
1368 0xD100,
1369 &uri_provider().get_source_uri(),
1370 CallOptions::for_notification(None, None, None),
1371 Some(UPayload::new("notify", UPayloadFormat::Text)),
1372 )
1373 .await
1374 .expect("notification sent");
1375
1376 let messages = listener.messages();
1377 assert_eq!(messages.len(), 1);
1378 let message = messages.first().expect("one received message");
1379 assert!(message.is_notification());
1380 assert_eq!(
1381 message.payload(),
1382 Some(bytes::Bytes::from_static(b"notification"))
1383 );
1384 let frames = transport.sent_frames();
1385 assert_eq!(frames.len(), 1);
1386 let sent = message_from_frame(frames.first().expect("one sent frame").clone())
1387 .expect("sent notification");
1388 assert!(sent.is_notification());
1389 assert_eq!(sent.payload(), Some(bytes::Bytes::from_static(b"notify")));
1390
1391 notifier
1392 .stop_listening(&topic, listener)
1393 .await
1394 .expect("notifier listener unregistered");
1395 }
1396
1397 #[tokio::test]
1398 async fn rpc_client_sends_request_and_returns_response_payload() {
1399 let transport = Arc::new(RecordingOwnedTransport::new());
1400 let client = RpcClient::new(transport.clone(), uri_provider());
1401 let request_id = UUID::build();
1402 let request =
1403 UMessageBuilder::request(method_uri(), uri_provider().get_source_uri(), 5_000)
1404 .with_message_id(request_id.clone())
1405 .build_with_payload("request", UPayloadFormat::Text)
1406 .expect("request");
1407 let response = UMessageBuilder::response_for_request(request.attributes())
1408 .build_with_payload("response", UPayloadFormat::Text)
1409 .expect("response");
1410 transport.push_received(message_frame(&response));
1411
1412 let result = client
1413 .invoke_method(
1414 method_uri(),
1415 CallOptions::for_rpc_request(5_000, Some(request_id), None, None),
1416 Some(UPayload::new("request", UPayloadFormat::Text)),
1417 )
1418 .await
1419 .expect("rpc succeeds")
1420 .expect("response payload");
1421
1422 assert_eq!(result.payload(), &bytes::Bytes::from_static(b"response"));
1423 let frames = transport.sent_frames();
1424 assert_eq!(frames.len(), 1);
1425 let frame = frames.first().expect("one sent frame");
1426 let sent = crate::frame::metadata::try_project_frame_to_umessage(
1427 frame.metadata().clone(),
1428 frame.payload().cloned(),
1429 )
1430 .expect("sent request");
1431 assert!(sent.is_request());
1432 assert_eq!(sent.payload(), Some(bytes::Bytes::from_static(b"request")));
1433 }
1434
1435 struct EchoHandler;
1436
1437 #[async_trait]
1438 impl crate::communication::RequestHandler for EchoHandler {
1439 async fn handle_request(
1440 &self,
1441 resource_id: u16,
1442 message_attributes: &UAttributes,
1443 request_payload: Option<UPayload>,
1444 ) -> Result<Option<UPayload>, ServiceInvocationError> {
1445 assert_eq!(resource_id, 0x1000);
1446 assert!(message_attributes.is_request());
1447 Ok(request_payload)
1448 }
1449 }
1450
1451 #[tokio::test]
1452 async fn rpc_server_registers_listener_and_sends_response() {
1453 let transport = Arc::new(RecordingOwnedTransport::new());
1454 let server = RpcServer::new(transport.clone(), uri_provider());
1455 server
1456 .register_endpoint(None, 0x1000, Arc::new(EchoHandler))
1457 .await
1458 .expect("endpoint registered");
1459 let request =
1460 UMessageBuilder::request(method_uri(), uri_provider().get_source_uri(), 5_000)
1461 .build_with_payload("server request", UPayloadFormat::Text)
1462 .expect("request");
1463
1464 transport
1465 .registered_listener()
1466 .on_receive_owned(message_frame(&request))
1467 .await;
1468
1469 let frames = transport.sent_frames();
1470 assert_eq!(frames.len(), 1);
1471 let frame = frames.first().expect("one sent frame");
1472 let response = crate::frame::metadata::try_project_frame_to_umessage(
1473 frame.metadata().clone(),
1474 frame.payload().cloned(),
1475 )
1476 .expect("response");
1477 assert!(response.is_response());
1478 assert_eq!(
1479 response.payload(),
1480 Some(bytes::Bytes::from_static(b"server request"))
1481 );
1482 assert_eq!(response.request_id_unchecked(), request.id());
1483 }
1484}
1485
1486#[cfg(all(test, feature = "protobuf-support"))]
1487mod selected_wire_tests {
1488 use std::collections::VecDeque;
1489 use std::sync::Mutex;
1490
1491 use protobuf::well_known_types::wrappers::StringValue;
1492
1493 use super::*;
1494 use crate::{
1495 NativePrefixFrameMetadataCodec, ProtobufWire, UHasWire, UOwnedFrame, UOwnedTransportCore,
1496 UOwnedTransportImpl, UStatus, UWithNativePrefixWire,
1497 };
1498
1499 #[derive(Clone, Default)]
1500 struct RecordingOwnedCore {
1501 sent: Arc<Mutex<Vec<crate::PreparedOwnedFrame>>>,
1502 }
1503
1504 impl RecordingOwnedCore {
1505 fn sent_payloads(&self) -> Vec<Option<bytes::Bytes>> {
1506 self.sent
1507 .lock()
1508 .expect("sent lock poisoned")
1509 .iter()
1510 .map(|frame| frame.payload().cloned())
1511 .collect()
1512 }
1513 }
1514
1515 #[async_trait]
1516 impl UOwnedTransportCore for RecordingOwnedCore {
1517 async fn send_prepared_owned(
1518 &self,
1519 frame: crate::PreparedOwnedFrame,
1520 ) -> Result<(), UStatus> {
1521 self.sent.lock().expect("sent lock poisoned").push(frame);
1522 Ok(())
1523 }
1524 }
1525
1526 fn uri_provider() -> Arc<crate::StaticUriProvider> {
1527 Arc::new(crate::StaticUriProvider::new("", 0x0005, 0x02).expect("uri provider"))
1528 }
1529
1530 #[tokio::test]
1531 async fn publish_typed_uses_selected_wire() {
1532 let core = RecordingOwnedCore::default();
1533 let transport = Arc::new(core.clone().into_native_prefix_wire_transport(ProtobufWire));
1534 assert_eq!(transport.wire(), &ProtobufWire);
1535 let publisher = Publisher::new(transport, uri_provider());
1536 let payload = StringValue {
1537 value: "typed".to_string(),
1538 ..Default::default()
1539 };
1540
1541 publisher
1542 .publish_typed(0x9A00, CallOptions::for_publish(None, None, None), &payload)
1543 .await
1544 .expect("typed publish succeeds");
1545
1546 let payloads = core.sent_payloads();
1547 assert_eq!(payloads.len(), 1);
1548 assert!(payloads
1549 .first()
1550 .expect("one sent payload")
1551 .as_ref()
1552 .is_some_and(|payload| !payload.is_empty()));
1553 let _: NativePrefixFrameMetadataCodec = NativePrefixFrameMetadataCodec;
1554 }
1555
1556 struct DirectSelectedOwnedTransport {
1557 wire: ProtobufWire,
1558 sent: Mutex<Vec<UOwnedFrame>>,
1559 received: Mutex<VecDeque<UOwnedFrame>>,
1560 }
1561
1562 impl DirectSelectedOwnedTransport {
1563 fn new() -> Self {
1564 Self {
1565 wire: ProtobufWire,
1566 sent: Mutex::new(Vec::new()),
1567 received: Mutex::new(VecDeque::new()),
1568 }
1569 }
1570
1571 fn sent_frames(&self) -> Vec<UOwnedFrame> {
1572 self.sent.lock().expect("sent lock poisoned").clone()
1573 }
1574
1575 fn push_received(&self, frame: UOwnedFrame) {
1576 self.received
1577 .lock()
1578 .expect("received lock poisoned")
1579 .push_back(frame);
1580 }
1581 }
1582
1583 impl UHasWire for DirectSelectedOwnedTransport {
1584 type Wire = ProtobufWire;
1585
1586 fn wire(&self) -> &Self::Wire {
1587 &self.wire
1588 }
1589 }
1590
1591 #[async_trait]
1592 impl UOwnedTransportImpl for DirectSelectedOwnedTransport {
1593 async fn send_validated_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus> {
1594 self.sent.lock().expect("sent lock poisoned").push(frame);
1595 Ok(())
1596 }
1597
1598 async fn receive_validated_owned(
1599 &self,
1600 _source_filter: &UUri,
1601 _sink_filter: Option<&UUri>,
1602 ) -> Result<UOwnedFrame, UStatus> {
1603 self.received
1604 .lock()
1605 .expect("received lock poisoned")
1606 .pop_front()
1607 .ok_or_else(|| crate::UStatus::fail_with_code(crate::UCode::NotFound, "no frame"))
1608 }
1609 }
1610
1611 #[tokio::test]
1612 async fn invoke_typed_uses_selected_wire() {
1613 let transport = Arc::new(DirectSelectedOwnedTransport::new());
1614 assert_eq!(transport.wire(), &ProtobufWire);
1615 let client = RpcClient::new(transport.clone(), uri_provider());
1616 let method = uri_provider().get_resource_uri(0x1000);
1617 let request_id = crate::UUID::build();
1618 let request =
1619 UMessageBuilder::request(method.clone(), uri_provider().get_source_uri(), 5_000)
1620 .with_message_id(request_id.clone())
1621 .build()
1622 .expect("request");
1623 let response_value = StringValue {
1624 value: "typed response".to_string(),
1625 ..Default::default()
1626 };
1627 let response_bytes =
1628 <ProtobufWire as EncodePayload<StringValue>>::encode_payload_owned(&response_value)
1629 .expect("encoded response");
1630 let response_format = <ProtobufWire as PayloadCodec>::payload_encoding()
1631 .to_legacy_format()
1632 .expect("standard format");
1633 let response = UMessageBuilder::response_for_request(request.attributes())
1634 .build_with_payload(response_bytes, response_format)
1635 .expect("response");
1636 transport.push_received(frame_from_message(&response).expect("response frame"));
1637 let request_value = StringValue {
1638 value: "typed request".to_string(),
1639 ..Default::default()
1640 };
1641
1642 let result: StringValue = client
1643 .invoke_typed(
1644 method,
1645 CallOptions::for_rpc_request(5_000, Some(request_id), None, None),
1646 &request_value,
1647 )
1648 .await
1649 .expect("typed rpc succeeds");
1650
1651 assert_eq!(result.value, "typed response");
1652 let frames = transport.sent_frames();
1653 assert_eq!(frames.len(), 1);
1654 assert!(frames
1655 .first()
1656 .expect("one sent frame")
1657 .payload()
1658 .is_some_and(|payload| !payload.is_empty()));
1659 }
1660}