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}