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
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);

/// 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)
}