remote_trait_object/service/export_import.rs
1use super::Dispatch;
2use super::*;
3use serde::{Deserialize, Serialize};
4use std::sync::Arc;
5
6/// An identifier of the skeleton.
7///
8/// This represents an identifier of a skeleton, and can create a proxy object using [`import_service_from_handle()`]
9///
10/// Note that you will never need this if you do only plain export & import using [`ServiceRef`], [`ServiceToExport`], or [`ServiceToImport`].
11/// See the [module-level documentation] to understand when to use this.
12///
13/// [`import_service_from_handle()`]: ../raw_exchange/fn.import_service_from_handle.html
14/// [`ServiceToExport`]: ../struct.ServiceToExport.html
15/// [`ServiceToImport`]: ../struct.ServiceToImport.html
16/// [`ServiceRef`]: ../enum.ServiceRef.html
17/// [module-level documentation]: ../raw_exchange/index.html
18#[derive(PartialEq, Serialize, Deserialize, Debug, Clone, Copy)]
19pub struct HandleToExchange(pub(crate) ServiceObjectId);
20
21impl HandleToExchange {
22 /// Creates a null handle.
23 ///
24 /// Any proxy object made from this will always panic for all methods.
25 /// If such proxy object is dropped, it won't send any delete request, so never fails.
26 ///
27 /// It is useful when you have a proxy object which has to be initialized later.
28 pub fn create_null() -> Self {
29 Self(crate::forwarder::NULL_ID)
30 }
31}
32
33/// An opaque service to register in the context.
34///
35/// See the general description of the concept _skeleton_ [here](https://github.com/CodeChain-io/remote-trait-object)
36/// and the definition in this crate [here](https://github.com/CodeChain-io/remote-trait-object).
37///
38/// It is constructed with a service object with whichever smart pointer you want.
39/// Depending on use of `&mut self` in the methods in the service trait, some or all `Box<>`, `Arc<>`, `Arc<RwLock<>>` will implement
40/// [`IntoSkeleton`] automatically by the proc macro.
41/// Please see [this section](https://github.com/CodeChain-io/remote-trait-object) for more detail about smart pointers.
42///
43/// `Skeleton` is useful when you want to erase the trait, and hold it as an opaque service that will be registered later.
44///
45/// Note that you will never need this if you do only plain export & import using [`ServiceRef`], [`ServiceToExport`], or [`ServiceToImport`].
46/// See the [module-level documentation] to understand when to use this.
47///
48/// [`IntoSkeleton`]: trait.IntoSkeleton.html
49/// [`ServiceToExport`]: ../struct.ServiceToExport.html
50/// [`ServiceToImport`]: ../struct.ServiceToImport.html
51/// [`ServiceRef`]: ../enum.ServiceRef.html
52/// [module-level documentation]: ../raw_exchange/index.html
53pub struct Skeleton {
54 pub(crate) raw: Arc<dyn Dispatch>,
55}
56
57impl Clone for Skeleton {
58 /// Clones a `Skeleton`, while sharing the actual single service object.
59 ///
60 /// This is useful when you want to export a single service object to multiple connections.
61 fn clone(&self) -> Self {
62 Self {
63 raw: Arc::clone(&self.raw),
64 }
65 }
66}
67
68impl Skeleton {
69 pub fn new<T: ?Sized + Service>(service: impl IntoSkeleton<T>) -> Self {
70 service.into_skeleton()
71 }
72}
73
74impl std::fmt::Debug for Skeleton {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 f.write_str("remote-trait-object skeleton")
77 }
78}
79
80// This belongs to macro_env
81pub fn create_skeleton(raw: Arc<dyn Dispatch>) -> Skeleton {
82 Skeleton { raw }
83}
84
85// This belongs to macro_env
86pub fn get_dispatch(skeleton: &Skeleton) -> &dyn Dispatch {
87 skeleton.raw.as_ref()
88}
89
90// These traits are associated with some specific service trait.
91// These tratis will be implement by `dyn ServiceTrait` where `T = dyn ServiceTrait` as well.
92// Macro will implement this trait with the target(expanding) service trait.
93
94/// Conversion into a [`Skeleton`], from a smart pointer of a service object.
95///
96/// By attaching `[remote_trait_object::service]` on a trait, smart pointers of the trait will automatically implement this.
97/// This is required if you want to create a [`Skeleton`] or [`ServiceToExport`].
98///
99/// [`ServiceToExport`]: ../struct.ServiceToExport.html
100// Unused T is for avoiding violation of the orphan rule
101// T will be local type for the crate, and that makes it possible to
102// impl IntoSkeleton<dyn MyService> for Arc<dyn MyService>
103pub trait IntoSkeleton<T: ?Sized + Service> {
104 fn into_skeleton(self) -> Skeleton;
105}
106
107/// Conversion into a smart pointer of a service object, from [`HandleToExchange`].
108///
109/// By attaching `[remote_trait_object::service]` on a trait, smart pointers of the trait will automatically implement this.
110/// This is required if you want to create a proxy object from [`HandleToExchange`] or [`ServiceToImport`].
111///
112/// [`ServiceToImport`]: ../struct.ServiceToImport.html
113// Unused T is for avoiding violation of the orphan rule, like `IntoSkeleton`
114pub trait ImportProxy<T: ?Sized + Service>: Sized {
115 fn import_proxy(port: Weak<dyn Port>, handle: HandleToExchange) -> Self;
116}
117
118#[doc(hidden)]
119pub trait FromSkeleton<T: ?Sized + Service>: Sized {
120 fn from_skeleton(skeleton: Skeleton) -> Self;
121}
122
123/// Exports a skeleton and returns a handle to it.
124///
125/// Once you create an instance of skeleton, you will eventually export it calling this.
126/// Take the handle to the other side's context and call [`import_service_from_handle`] to import it into a proxy object.
127/// If not, the service object will remain in the Context forever doing nothing.
128pub fn export_service_into_handle(
129 context: &crate::context::Context,
130 service: Skeleton,
131) -> HandleToExchange {
132 context
133 .get_port()
134 .upgrade()
135 .unwrap()
136 .register_service(service.raw)
137}
138
139/// Imports a handle into a proxy object.
140///
141/// Once you receive an instance of [`HandleToExchange`], you will eventually import it calling this.
142/// Such handles must be from the other side's context.
143pub fn import_service_from_handle<T: ?Sized + Service, P: ImportProxy<T>>(
144 context: &crate::context::Context,
145 handle: HandleToExchange,
146) -> P {
147 P::import_proxy(context.get_port(), handle)
148}
149
150/// Create a proxy object that always panic for all methods.
151///
152/// This is same as using [`create_null()`] and [`import_service_from_handle()`] but you don't even have to specify the [`Context`] here.
153///
154/// [`create_null()`]: ./struct.HandleToExchange.html#method.create_null
155/// [`import_service_from_handle()`]: ./fn.import_service_from_handle.html
156/// [`Context`]: ../struct.Context.html
157pub fn import_null_proxy<T: ?Sized + Service, P: ImportProxy<T>>() -> P {
158 P::import_proxy(
159 crate::port::null_weak_port(),
160 HandleToExchange::create_null(),
161 )
162}