1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use super::Dispatch;
use super::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;

/// An identifier of the skeleton.
///
/// This represents an identifier of a skeleton, and can create a proxy object using [`import_service_from_handle()`]
///
/// Note that you will never need this if you do only plain export & import using [`ServiceRef`], [`ServiceToExport`], or [`ServiceToImport`].
/// See the [module-level documentation] to understand when to use this.
///
/// [`import_service_from_handle()`]: ../raw_exchange/fn.import_service_from_handle.html
/// [`ServiceToExport`]: ../struct.ServiceToExport.html
/// [`ServiceToImport`]: ../struct.ServiceToImport.html
/// [`ServiceRef`]: ../enum.ServiceRef.html
/// [module-level documentation]: ../raw_exchange/index.html
#[derive(PartialEq, Serialize, Deserialize, Debug, Clone, Copy)]
pub struct HandleToExchange(pub(crate) ServiceObjectId);

impl HandleToExchange {
    /// Creates a null handle.
    ///
    /// Any proxy object made from this will always panic for all methods.
    /// If such proxy object is dropped, it won't send any delete request, so never fails.
    ///
    /// It is useful when you have a proxy object which has to be initialized later.
    pub fn create_null() -> Self {
        Self(crate::forwarder::NULL_ID)
    }
}

/// An opaque service to register in the context.
///
/// See the general description of the concept _skeleton_ [here](https://github.com/CodeChain-io/remote-trait-object)
/// and the definition in this crate [here](https://github.com/CodeChain-io/remote-trait-object).
///
/// It is constructed with a service object with whichever smart pointer you want.
/// Depending on use of `&mut self` in the methods in the service trait, some or all `Box<>`, `Arc<>`, `Arc<RwLock<>>` will implement
/// [`IntoSkeleton`] automatically by the proc macro.
/// Please see [this section](https://github.com/CodeChain-io/remote-trait-object) for more detail about smart pointers.
///
/// `Skeleton` is useful when you want to erase the trait, and hold it as an opaque service that will be registered later.
///
/// Note that you will never need this if you do only plain export & import using [`ServiceRef`], [`ServiceToExport`], or [`ServiceToImport`].
/// See the [module-level documentation] to understand when to use this.
///
/// [`IntoSkeleton`]: trait.IntoSkeleton.html
/// [`ServiceToExport`]: ../struct.ServiceToExport.html
/// [`ServiceToImport`]: ../struct.ServiceToImport.html
/// [`ServiceRef`]: ../enum.ServiceRef.html
/// [module-level documentation]: ../raw_exchange/index.html
pub struct Skeleton {
    pub(crate) raw: Arc<dyn Dispatch>,
}

impl Clone for Skeleton {
    /// Clones a `Skeleton`, while sharing the actual single service object.
    ///
    /// This is useful when you want to export a single service object to multiple connections.
    fn clone(&self) -> Self {
        Self {
            raw: Arc::clone(&self.raw),
        }
    }
}

impl Skeleton {
    pub fn new<T: ?Sized + Service>(service: impl IntoSkeleton<T>) -> Self {
        service.into_skeleton()
    }
}

// This belongs to macro_env
pub fn create_skeleton(raw: Arc<dyn Dispatch>) -> Skeleton {
    Skeleton {
        raw,
    }
}

// These traits are associated with some specific service trait.
// These tratis will be implement by `dyn ServiceTrait` where `T = dyn ServiceTrait` as well.
// Macro will implement this trait with the target(expanding) service trait.

/// Conversion into a [`Skeleton`], from a smart pointer of a service object.
///
/// By attaching `[remote_trait_object::service]` on a trait, smart pointers of the trait will automatically implement this.
/// This is required if you want to create a [`Skeleton`] or [`ServiceToExport`].
///
/// [`ServiceToExport`]: ../struct.ServiceToExport.html
// Unused T is for avoiding violation of the orphan rule
// T will be local type for the crate, and that makes it possible to
// impl IntoSkeleton<dyn MyService> for Arc<dyn MyService>
pub trait IntoSkeleton<T: ?Sized + Service> {
    fn into_skeleton(self) -> Skeleton;
}

/// Conversion into a smart pointer of a service object, from [`HandleToExchange`].
///
/// By attaching `[remote_trait_object::service]` on a trait, smart pointers of the trait will automatically implement this.
/// This is required if you want to create a proxy object from [`HandleToExchange`] or [`ServiceToImport`].
///
/// [`ServiceToImport`]: ../struct.ServiceToImport.html
// Unused T is for avoiding violation of the orphan rule, like `IntoSkeleton`
pub trait ImportProxy<T: ?Sized + Service>: Sized {
    fn import_proxy(port: Weak<dyn Port>, handle: HandleToExchange) -> Self;
}

/// Exports a skeleton and returns a handle to it.
///
/// Once you create an instance of skeleton, you will eventually export it calling this.
/// Take the handle to the other side's context and call [`import_service_from_handle`] to import it into a proxy object.
/// If not, the service object will remain in the Context forever doing nothing.
pub fn export_service_into_handle(context: &crate::context::Context, service: Skeleton) -> HandleToExchange {
    context.get_port().upgrade().unwrap().register_service(service.raw)
}

/// Imports a handle into a proxy object.
///
/// Once you receive an instance of [`HandleToExchange`], you will eventually import it calling this.
/// Such handles must be from the other side's context.
pub fn import_service_from_handle<T: ?Sized + Service, P: ImportProxy<T>>(
    context: &crate::context::Context,
    handle: HandleToExchange,
) -> P {
    P::import_proxy(context.get_port(), handle)
}

/// Create a proxy object that always panic for all methods.
///
/// This is same as using [`create_null()`] and [`import_service_from_handle()`] but you don't even have to specify the [`Context`] here.
///
/// [`create_null()`]: ./struct.HandleToExchange.html#method.create_null
/// [`import_service_from_handle()`]: ./fn.import_service_from_handle.html
/// [`Context`]: ../struct.Context.html
pub fn import_null_proxy<T: ?Sized + Service, P: ImportProxy<T>>() -> P {
    P::import_proxy(crate::port::null_weak_port(), HandleToExchange::create_null())
}