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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;

/// A Frodobuf message
#[derive(Debug)]
pub struct Message<'m> {
    /// Message name, usually in the form 'Trait.method'
    pub method: &'m str,
    /// parameter serialized as a byte array. If the method takes no args, the arraya will be
    /// zero length.
    pub arg: Cow<'m, [u8]>,
}

/*
// why do i need this?
impl Default for Message<'_> {
    /// constructs default Message
    fn default() -> Self {
        Message {
            method: "_void",
            arg: Cow::Owned(Vec::new()),
        }
    }
}
 */

/// context data
pub mod context {

    /// Context - message passing metadata used by wasmhost Actors and Capability Providers
    #[derive(Default, Debug)]
    pub struct Context<'msg> {
        /// Messages received by Context Provider will have actor set to the actor's public key
        pub actor: Option<&'msg str>,

        /// Span name/context for tracing. This is a placeholder for now
        pub span: Option<String>,
    }
}

/// client is the caller side of any interface
pub mod client {
    /// Client config defines the intended recipient of a message
    #[derive(Debug)]
    pub struct ClientConfig {
        /// Host/link name, usually "default" for the current host
        pub host: String,
        /// Recipient of message, such as actor's public key or provider id
        pub target: String,
    }

    impl ClientConfig {
        /// Constructs a new client with host and target
        /// when sending to a capability provider,
        pub fn new<H: Into<String>, T: Into<String>>(host: H, target: T) -> ClientConfig {
            ClientConfig {
                host: host.into(),
                target: target.into(),
            }
        }

        /// Create a ClientConfig for sending to an actor
        pub fn actor<T: Into<String>>(target: T) -> ClientConfig {
            ClientConfig {
                host: "default".into(),
                target: target.into(),
            }
        }

        /// Create a ClientConfig using the default host and specified target
        pub fn target<T: Into<String>>(target: T) -> ClientConfig {
            ClientConfig {
                host: "default".into(),
                target: target.into(),
            }
        }
    }
}

/// Transport determines how messages are sent
/// Alternate implementations could be mock-server, or test-fuzz-server / test-fuzz-client
#[async_trait]
pub trait Transport: Send {
    async fn send(
        &self,
        ctx: &context::Context<'_>,
        config: &client::ClientConfig,
        req: Message<'_>,
    ) -> std::result::Result<Message<'static>, RpcError>;
}

#[derive(Clone, Debug, Default)]
pub struct WasmHost {}

//#[cfg(target_arch = "wasm32")]
#[async_trait]
impl Transport for WasmHost {
    async fn send(
        &self,
        _ctx: &context::Context<'_>,
        config: &client::ClientConfig,
        req: Message<'_>,
    ) -> std::result::Result<Message<'static>, RpcError> {
        // TODO: currently makes no distinction between sending to actor and provider
        // this is an actor call
        let res = crate::host_call(
            &config.host,   // "default", or capability provider ID
            &config.target, // actor_ref, or capability name (e.g. wasmcloud::messaging)
            req.method,
            req.arg.as_ref(),
        )?;
        Ok(Message {
            method: "_reply",
            arg: Cow::Owned(res),
        })
    }
}

pub fn deserialize<'de, T: Deserialize<'de>>(buf: &'de [u8]) -> Result<T, RpcError> {
    //serde_json::from_slice(buf).map_err(|e| RpcError::Deser(e.to_string()))
    rmp_serde::from_slice(buf).map_err(|e| RpcError::Deser(e.to_string()))
}

pub fn serialize<T: Serialize>(data: &T) -> Result<Vec<u8>, RpcError> {
    //serde_json::to_vec(data).map_err(|e| RpcError::Ser(e.to_string()))
    rmp_serde::to_vec(data).map_err(|e| RpcError::Ser(e.to_string()))
}

/// An error that can occur in the processing of an RPC. This is not request-specific errors but
/// rather cross-cutting errors that can always occur.
#[derive(thiserror::Error, Debug, Serialize, Deserialize)]
pub enum RpcError {
    /// The request exceeded its deadline.
    #[error("the request exceeded its deadline")]
    DeadlineExceeded,

    /// A capability provider was called before its configure_dispatch was called.
    #[error("the capability provider has not been initialized")]
    NotInitialized,

    /// The message was invalid
    #[error("the message was invalid")]
    Invalid(String),

    #[error("method not handled {0}")]
    MethodNotHandled(String),

    /// Error that can be returned if server has not implemented
    /// an optional interface method
    #[error("method not implemented")]
    NotImplemented,

    #[error("Host send error {0}")]
    HostError(String),

    #[error("deserialization: {0}")]
    Deser(String),

    #[error("serialization: {0}")]
    Ser(String),

    #[error("invalid parameter: {0}")]
    InvalidParameter(String),

    /// Error occurred in actor's rpc handler
    #[error("actor: {0}")]
    ActorHandler(String),

    /// Anything else
    #[error("{0}")]
    Other(String),
}

#[async_trait]
pub trait MessageDispatch {
    async fn dispatch(
        &self,
        ctx: &context::Context<'_>,
        message: Message<'_>,
    ) -> Result<Message<'static>, RpcError>;
}

//macro_rules! implement_service {
//    ( ( $trait:ident, $impl:ident ),*) => {
// need to do a few things
//  1. build list of interfaces tha I respond to, so I can return my own api
//  2. a: return list of serve() functions, so dispatcher can call them
//  2. b: or, build my own dispatcher function that tries each one
//
//    };
//}