remote_trait_object/service/serde_support.rs
1use super::export_import::*;
2use super::*;
3use crate::raw_exchange::HandleToExchange;
4use serde::de::{Deserialize, Deserializer};
5use serde::ser::{Serialize, Serializer};
6use std::cell::RefCell;
7use std::marker::PhantomData;
8use std::sync::Arc;
9
10/// Some data format traverses over data **twice**, first for size estimation and second for real encoding.
11/// Thus we use prepare secondary phase `Exported`.
12enum ExportEntry {
13 ReadyToExport(Skeleton),
14 Exported(HandleToExchange),
15}
16
17/// A special wrapper of _skeleton_, used to export a service object.
18///
19/// You can make any smart pointer of a trait object into a `ServiceToExport`
20/// as far as the trait is a subtrait of [`Service`] and marked with the macro [`service`].
21///
22/// The only way to actually export a `ServiceToExport` is either
23/// 1. Returning it while handling a method in another service
24/// 2. Passing as an argument in a remote call
25///
26/// `ServiceToExport` is a compatible type with [`ServiceToImport`] and [`ServiceRef`]
27/// You can import a proxy object by putting one of those two types on the same place in the client side.
28/// See [Service Compatibility] section
29///
30/// `ServiceToExport` is a wrapper of [`Skeleton`] that hides the detail exchange process.
31/// However you don't have to know what [`Skeleton`] is, unless you're going to perform [raw export and import].
32///
33/// **NOTE**: it implements [`Serialize`], but you must **NEVER** try to serialize it.
34/// It has a side effect of registering the service object in the context,
35/// and so should be called only by `remote-trait-object`'s internal process.
36/**
37## Example
38```
39use remote_trait_object::*;
40use std::sync::Arc;
41use parking_lot::RwLock;
42
43#[service(no_proxy)]
44pub trait Hello: Service {
45 fn hello(&self) -> Vec<ServiceToExport<dyn Hello>>;
46}
47
48struct A;
49impl Service for A {}
50impl Hello for A {
51 fn hello(&self) -> Vec<ServiceToExport<dyn Hello>> {
52 // Export by return
53 vec![
54 ServiceToExport::new(Box::new(A) as Box<dyn Hello>),
55 ServiceToExport::new(Arc::new(A) as Arc<dyn Hello>),
56 ServiceToExport::new(Arc::new(RwLock::new(A)) as Arc<RwLock<dyn Hello>>),
57 ]
58 }
59}
60```
61**/
62/// [`service`]: attr.service.html
63/// [Service compatibility]: ./index.html#service_compatibility
64/// [raw export and import]: raw_exchange/index.html
65pub struct ServiceToExport<T: ?Sized + Service> {
66 service: RefCell<ExportEntry>,
67 _marker: PhantomData<T>,
68}
69
70impl<T: ?Sized + Service> ServiceToExport<T> {
71 /// Creates a new instance from a smart pointer of a service object.
72 pub fn new(service: impl IntoSkeleton<T>) -> Self {
73 Self {
74 service: RefCell::new(ExportEntry::ReadyToExport(service.into_skeleton())),
75 _marker: PhantomData,
76 }
77 }
78
79 pub(crate) fn get_raw_export(self) -> Skeleton {
80 match self.service.into_inner() {
81 ExportEntry::ReadyToExport(s) => s,
82 _ => panic!(),
83 }
84 }
85}
86
87/// A special wrapper of _handle_, used to import a service.
88///
89/// You can make an instance of this into any smart pointer of a trait object
90/// as far as the trait is a subtrait of [`Service`] and marked with the macro [`service`].
91///
92/// The only way to actually import a `ServiceToImport` is either
93/// 1. Receiving it as a return value, in a remote call
94/// 2. Receiving as an argument while a method in handling another service
95///
96/// `ServiceToImport` is a compatible type with [`ServiceToExport`] and [`ServiceRef`]
97/// You can export a service object by putting one of those two types on the same place on the server side.
98/// See [Service Compatibility] section for more.
99///
100/// `ServiceToImport` is a wrapper of [`HandleToExchange`] that hides the detail exchange process.
101/// However you don't have to know what [`HandleToExchange`] is, unless you're going to perform [raw export and import].
102///
103/// **NOTE**: it implements [`Deserialize`], but you must **NEVER** try to deserialize it.
104/// It has a side effect of registering the handle in the context,
105/// and so should be called only by `remote-trait-object`'s internal process.
106/**
107## Example
108```
109use remote_trait_object::*;
110use std::sync::Arc;
111use parking_lot::RwLock;
112
113#[service(no_skeleton)]
114pub trait Hello: Service {
115 fn hello(&self) -> Vec<ServiceToImport<dyn Hello>>;
116}
117
118fn do_some_imports(x: Box<dyn Hello>) {
119 let mut v = x.hello();
120 let a: Box<dyn Hello> = v.pop().unwrap().into_proxy();
121 let b: Arc<dyn Hello> = v.pop().unwrap().into_proxy();
122 let c: Arc<RwLock<dyn Hello>> = v.pop().unwrap().into_proxy();
123}
124```
125**/
126/// [`service`]: attr.service.html
127/// [Service compatibility]: ./index.html#service_compatibility
128/// [raw export and import]: raw_exchange/index.html
129pub struct ServiceToImport<T: ?Sized + Service> {
130 handle: HandleToExchange,
131 port: Weak<dyn Port>,
132 _marker: PhantomData<T>,
133}
134
135impl<T: ?Sized + Service> ServiceToImport<T> {
136 /// Converts itself into a smart pointer of the trait, which is a _proxy object_.
137 pub fn into_proxy<P: ImportProxy<T>>(self) -> P {
138 P::import_proxy(self.port, self.handle)
139 }
140
141 /// Casts into another `ServiceToImport` with a different service trait.
142 ///
143 /// If the target trait is not compatible with the original one, it returns `Err`.
144 ///
145 /// See [Service Compatiblity] section for more.
146 ///
147 /// [Service compatiblity]: ./index.html#service_compatibility
148 #[allow(clippy::result_unit_err)]
149 pub fn cast_service<U: ?Sized + Service>(self) -> Result<ServiceToImport<U>, ()> {
150 // TODO: Check the compatibility between traits using IDL
151 Ok(ServiceToImport {
152 handle: self.handle,
153 port: self.port,
154 _marker: PhantomData,
155 })
156 }
157
158 /// Casts into another `ServiceToImport` with a different service trait, without check.
159 ///
160 /// If the target trait is not compatible with the original one, any method call of the proxy object imported with this
161 /// will cause a serious error.
162 ///
163 /// See [Service Compatiblity] section for more.
164 ///
165 /// [Service compatiblity]: ./index.html#service_compatibility
166 pub fn cast_service_without_compatibility_check<U: ?Sized + Service>(
167 self,
168 ) -> ServiceToImport<U> {
169 ServiceToImport {
170 handle: self.handle,
171 port: self.port,
172 _marker: PhantomData,
173 }
174 }
175
176 pub(crate) fn from_raw_import(handle: HandleToExchange, port: Weak<dyn Port>) -> Self {
177 Self {
178 handle,
179 port,
180 _marker: PhantomData,
181 }
182 }
183}
184
185/// A union of [`ServiceToExport`] and [`ServiceToImport`]
186///
187/// **In most case, you will mostly use only this struct to export and import services.**
188///
189/// This is needed when you want to export and import a service via another service,
190/// but using a common single trait as a channel.
191///
192/// Suppose you want to export `Box<dyn Pizza>` by returning it in another service's method, `fn order_pizza()`.
193/// One way of doing this is defining two traits, one for export and one for import.
194/// Crate that wants to implement & export a `PizzaStore` and export `Pizza` will have following code
195/**
196```
197use remote_trait_object::*;
198
199#[service]
200pub trait Pizza: Service {}
201
202#[service(no_proxy)]
203pub trait PizzaStore: Service {
204 fn order_pizza(&self) -> ServiceToExport<dyn Pizza>;
205}
206```
207
208On the other hand, crate that wants to import & remotely call a `PizzaStore` and import `Pizza` will have following code
209
210```
211use remote_trait_object::*;
212
213#[service]
214pub trait Pizza: Service {}
215
216#[service(no_skeleton)]
217pub trait PizzaStore: Service {
218 fn order_pizza(&self) -> ServiceToImport<dyn Pizza>;
219}
220```
221**/
222/// This works perfectly fine, by returning `ServiceToExport::new(..)` in the former and
223/// calling `ServiceToImport::into_remote(the_return_value)` in the latter,
224/// because the two `PizzaStore`s are compatible since [`ServiceToImport`] and [`ServiceToExport`] are compatible.
225///
226/// However, suppose the case where the two parties may have a common dependent crate which would have defined such a common service trait,
227/// or even the case where the two parties are in the same crate.
228/// It becomes somewhat bothersome to have both duplicated traits only because of [`ServiceToExport`] and [`ServiceToImport`].
229///
230/// `ServiceRef` is for such purpose. Instead of denoting both [`ServiceToExport`] and [`ServiceToImport`] for two separate traits, just use
231/// a single common trait with those two types replaced with `ServiceRef` in the very same place.
232///
233/// The above two traits now become a single trait.
234/**
235```
236use remote_trait_object::*;
237
238#[service]
239pub trait Pizza: Service {}
240
241#[service]
242pub trait PizzaStore: Service {
243 fn order_pizza(&self) -> ServiceRef<dyn Pizza>;
244}
245```
246**/
247/// And this `PizzaStore` can be both
248/// - implemented and exported - you will be using `Import` variant for an argument, and `Export` variant for the return value.
249/// - imported and locally invoked - you will be using `Export` variant for an argument, and `Import` variant for the return value.
250///
251/// ## Example
252/**
253```ignore
254// EXPORTER SIDE
255impl PizzaStore for SomeType {
256 fn order_pizza(&self) -> ServiceRef<dyn Pizza> {
257 ServiceRef::create_export(Box::new(SomePizza) as Box<dyn Pizza>)
258 }
259}
260
261// IMPORTER SIDE
262let store: Box<dyn PizzaStore> = some_store();
263let pizza: Box<dyn Pizza> = store.order_pizza().unwrap_import().into_proxy();
264```
265**/
266pub enum ServiceRef<T: ?Sized + Service> {
267 Export(ServiceToExport<T>),
268 Import(ServiceToImport<T>),
269}
270
271impl<T: ?Sized + Service> ServiceRef<T> {
272 /// Creates an `Export` variant from a smart pointer of a service object.
273 ///
274 /// It simply `ServiceRef::Export(ServiceToExport::new(service))`.
275 pub fn create_export(service: impl IntoSkeleton<T>) -> Self {
276 ServiceRef::Export(ServiceToExport::new(service))
277 }
278
279 /// Unwraps as an `Import` variant.
280 ///
281 /// It panics if it is `Export`.
282 pub fn unwrap_import(self) -> ServiceToImport<T> {
283 match self {
284 ServiceRef::Import(x) => x,
285 _ => panic!("You can import ony imported ServiceRef"),
286 }
287 }
288
289 /// Converts into the object with whatever smart pointer type you want, for both variants.
290 ///
291 /// If `ServiceRef` is constructed with `Import` (the common case), it is same as `unwrap_import().into_proxy()`.
292 /// If `ServiceRef` is constructed with `Export` (where the `ServiceRef` is given by local object),
293 /// it just create a light-weight object that simply wraps the skeleton, not involving any connections.
294 pub fn into_object<P: ImportProxy<T> + FromSkeleton<T>>(self) -> P {
295 match self {
296 ServiceRef::Import(x) => x.into_proxy(),
297 ServiceRef::Export(x) => P::from_skeleton(x.get_raw_export()),
298 }
299 }
300}
301
302/// This manages thread-local pointer of the port, which will be used in serialization of
303/// service objects wrapped in the S* pointers. Cuttently it is the only way to deliver the port
304/// within the de/serialization context.
305/// TODO: check that serde doens't spawn a thread while serializing.
306pub(crate) mod port_thread_local {
307 use super::*;
308 use std::cell::RefCell;
309
310 // TODO
311 // If a service call another service, this PORT setting might be stacked (at most twice).
312 // We check that the consistency of stacking for an assertion purpose.
313 // However it might be costly considering the frequency of this operations,
314 // so please replace this with unchecking logic
315 // after the library becomes stabilized.
316 thread_local!(static PORT: RefCell<Vec<Weak<dyn Port>>> = RefCell::new(Vec::new()));
317
318 pub fn set_port(port: Weak<dyn Port>) {
319 PORT.with(|k| {
320 k.try_borrow_mut().unwrap().push(port);
321 assert!(k.borrow().len() <= 2);
322 })
323 }
324
325 pub fn get_port() -> Weak<dyn Port> {
326 PORT.with(|k| k.borrow().last().unwrap().clone())
327 }
328
329 pub fn remove_port() {
330 PORT.with(|k| {
331 k.try_borrow_mut().unwrap().pop().unwrap();
332 })
333 }
334}
335
336impl<T: ?Sized + Service> Serialize for ServiceToExport<T> {
337 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
338 where
339 S: Serializer,
340 {
341 let error = "You must not de/serialize ServiceRef by yourself. If you not, this is a bug.";
342 let (handle, have_to_replace) = match &*self.service.borrow() {
343 ExportEntry::ReadyToExport(service) => {
344 debug_assert_eq!(Arc::strong_count(&service.raw), 1);
345 (
346 port_thread_local::get_port()
347 .upgrade()
348 .expect(error)
349 .register_service(Arc::clone(&service.raw)),
350 true,
351 )
352 }
353 ExportEntry::Exported(handle) => (*handle, false),
354 };
355 if have_to_replace {
356 *self.service.borrow_mut() = ExportEntry::Exported(handle)
357 }
358 handle.serialize(serializer)
359 }
360}
361
362impl<'de, T: ?Sized + Service> Deserialize<'de> for ServiceToImport<T> {
363 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
364 where
365 D: Deserializer<'de>,
366 {
367 let handle = HandleToExchange::deserialize(deserializer)?;
368 Ok(ServiceToImport {
369 handle,
370 port: port_thread_local::get_port(),
371 _marker: std::marker::PhantomData,
372 })
373 }
374}
375
376impl<T: ?Sized + Service> Serialize for ServiceRef<T> {
377 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
378 where
379 S: Serializer,
380 {
381 match self {
382 ServiceRef::Export(x) => x.serialize(serializer),
383 ServiceRef::Import(_) => panic!(
384 "If you want to re-export an imported object, first completely import it with `into_proxy()` and make it into `ServiceToExport`."
385 ),
386 }
387 }
388}
389
390impl<'de, T: ?Sized + Service> Deserialize<'de> for ServiceRef<T> {
391 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
392 where
393 D: Deserializer<'de>,
394 {
395 Ok(ServiceRef::Import(ServiceToImport::deserialize(
396 deserializer,
397 )?))
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 mod serialize_test {
404 use super::super::ServiceRef;
405 use crate::macro_env::*;
406 use crate::packet::*;
407 use crate::port::Port;
408 use crate::service::ServiceObjectId;
409 use crate::*;
410 use std::sync::atomic::{AtomicU32, Ordering};
411 use std::sync::{Arc, Weak};
412
413 #[derive(Debug)]
414 pub struct MockPort {
415 count: AtomicU32,
416 }
417
418 impl Port for MockPort {
419 fn call(&self, _packet: PacketView) -> Packet {
420 unimplemented!()
421 }
422
423 fn register_service(&self, _service_object: Arc<dyn Dispatch>) -> HandleToExchange {
424 self.count.fetch_add(1, Ordering::SeqCst);
425 HandleToExchange(123)
426 }
427
428 fn delete_request(&self, _id: ServiceObjectId) {
429 unimplemented!()
430 }
431 }
432
433 trait Foo: Service {}
434
435 struct FooImpl;
436 impl Foo for FooImpl {}
437 impl Service for FooImpl {}
438 impl Dispatch for FooImpl {
439 fn dispatch_and_call(&self, _method: MethodId, _args: &[u8]) -> Vec<u8> {
440 unimplemented!()
441 }
442 }
443
444 impl IntoSkeleton<dyn Foo> for Arc<dyn Foo> {
445 fn into_skeleton(self) -> crate::macro_env::Skeleton {
446 crate::macro_env::create_skeleton(Arc::new(FooImpl))
447 }
448 }
449
450 /// This test checks SArc<dyn Test> is serialized as HandleToExchange or not
451 #[test]
452 fn test_serialize() {
453 let port = Arc::new(MockPort {
454 count: AtomicU32::new(0),
455 });
456 let weak_port = Arc::downgrade(&port) as Weak<dyn Port>;
457 super::super::port_thread_local::set_port(weak_port);
458
459 {
460 let foo_arc: Arc<dyn Foo> = Arc::new(FooImpl);
461 let foo_sarc = ServiceRef::create_export(foo_arc.clone());
462 let bytes = serde_json::to_vec(&foo_sarc).unwrap();
463 let handle_to_exchange: HandleToExchange = serde_json::from_slice(&bytes).unwrap();
464 assert_eq!(handle_to_exchange.0, 123);
465 assert_eq!(port.count.load(Ordering::SeqCst), 1);
466 }
467
468 {
469 let foo_arc: Arc<dyn Foo> = Arc::new(FooImpl);
470 let foo_sarc = ServiceRef::create_export(foo_arc.clone());
471 let bytes = serde_cbor::to_vec(&foo_sarc).unwrap();
472 let handle_to_exchange: HandleToExchange = serde_cbor::from_slice(&bytes).unwrap();
473 assert_eq!(handle_to_exchange.0, 123);
474 assert_eq!(port.count.load(Ordering::SeqCst), 2);
475 }
476
477 {
478 let foo_arc: Arc<dyn Foo> = Arc::new(FooImpl);
479 let foo_sarc = ServiceRef::create_export(foo_arc.clone());
480 let bytes = bincode::serialize(&foo_sarc).unwrap();
481 let handle_to_exchange: HandleToExchange = bincode::deserialize(&bytes).unwrap();
482 assert_eq!(handle_to_exchange.0, 123);
483 assert_eq!(port.count.load(Ordering::SeqCst), 3);
484 }
485 }
486 }
487
488 mod deserialize_test {
489 use super::super::ServiceRef;
490 use crate::port::Port;
491 use crate::{raw_exchange::*, Service};
492 use std::sync::Weak;
493
494 trait Foo: Service {
495 fn get_handle_to_exchange(&self) -> HandleToExchange;
496 }
497 struct FooImpl {
498 handle_to_exchange: HandleToExchange,
499 }
500 impl Foo for FooImpl {
501 fn get_handle_to_exchange(&self) -> HandleToExchange {
502 self.handle_to_exchange
503 }
504 }
505 impl Service for FooImpl {}
506 impl ImportProxy<dyn Foo> for Box<dyn Foo> {
507 fn import_proxy(_port: Weak<dyn Port>, handle: HandleToExchange) -> Box<dyn Foo> {
508 Box::new(FooImpl {
509 handle_to_exchange: handle,
510 })
511 }
512 }
513
514 #[test]
515 fn test_deserialize() {
516 super::super::port_thread_local::set_port(crate::port::null_weak_port());
517
518 {
519 let handle_to_exchange = HandleToExchange(32);
520 let serialized_handle = serde_cbor::to_vec(&handle_to_exchange).unwrap();
521 let dyn_foo: ServiceRef<dyn Foo> =
522 serde_cbor::from_slice(&serialized_handle).unwrap();
523 assert_eq!(
524 dyn_foo
525 .unwrap_import()
526 .into_proxy::<Box<dyn Foo>>()
527 .get_handle_to_exchange()
528 .0,
529 32
530 );
531 }
532
533 {
534 let handle_to_exchange = HandleToExchange(2);
535 let serialized_handle = serde_cbor::to_vec(&handle_to_exchange).unwrap();
536 let dyn_foo: ServiceRef<dyn Foo> =
537 serde_cbor::from_slice(&serialized_handle).unwrap();
538 assert_eq!(
539 dyn_foo
540 .unwrap_import()
541 .into_proxy::<Box<dyn Foo>>()
542 .get_handle_to_exchange()
543 .0,
544 2
545 );
546 }
547 }
548 }
549}