Skip to main content

capnp/
capability.rs

1// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22//! Hooks for the RPC system.
23//!
24//! Roughly corresponds to capability.h in the C++ implementation.
25
26#[cfg(feature = "alloc")]
27use core::future::Future;
28#[cfg(feature = "alloc")]
29use core::marker::PhantomData;
30#[cfg(feature = "rpc_try")]
31use core::ops::Try;
32#[cfg(feature = "alloc")]
33use core::pin::Pin;
34#[cfg(feature = "alloc")]
35use core::task::Poll;
36
37use crate::any_pointer;
38#[cfg(feature = "alloc")]
39use crate::private::capability::{ClientHook, ParamsHook, RequestHook, ResponseHook, ResultsHook};
40#[cfg(feature = "alloc")]
41use crate::traits::{Owned, Pipelined};
42#[cfg(feature = "alloc")]
43use crate::{Error, MessageSize};
44
45/// Type alias for `dyn ClientHook`. We define this here because so that generated code
46/// can avoid needing to refer to `dyn` types directly; in Rust 2015 the syntax for
47/// `dyn` types requires extra parentheses that trigger warnings in newer editions.
48#[cfg(feature = "alloc")]
49pub type DynClientHook = dyn ClientHook;
50
51/// A computation that might eventually resolve to a value of type `T` or to an error
52///  of type `E`. Dropping the promise cancels the computation.
53#[cfg(feature = "alloc")]
54#[must_use = "futures do nothing unless polled"]
55pub struct Promise<T, E> {
56    inner: PromiseInner<T, E>,
57}
58
59#[cfg(feature = "alloc")]
60enum PromiseInner<T, E> {
61    Immediate(Result<T, E>),
62    Deferred(Pin<alloc::boxed::Box<dyn Future<Output = core::result::Result<T, E>> + 'static>>),
63    Empty,
64}
65
66// Allow Promise<T,E> to be Unpin, regardless of whether T and E are.
67#[cfg(feature = "alloc")]
68impl<T, E> Unpin for PromiseInner<T, E> {}
69
70#[cfg(feature = "alloc")]
71impl<T, E> Promise<T, E> {
72    pub fn ok(value: T) -> Self {
73        Self {
74            inner: PromiseInner::Immediate(Ok(value)),
75        }
76    }
77
78    pub fn err(error: E) -> Self {
79        Self {
80            inner: PromiseInner::Immediate(Err(error)),
81        }
82    }
83
84    pub fn from_future<F>(f: F) -> Self
85    where
86        F: Future<Output = core::result::Result<T, E>> + 'static,
87    {
88        Self {
89            inner: PromiseInner::Deferred(alloc::boxed::Box::pin(f)),
90        }
91    }
92}
93
94#[cfg(feature = "alloc")]
95impl<T, E> Future for Promise<T, E> {
96    type Output = core::result::Result<T, E>;
97
98    fn poll(self: Pin<&mut Self>, cx: &mut ::core::task::Context) -> Poll<Self::Output> {
99        match self.get_mut().inner {
100            PromiseInner::Empty => panic!("Promise polled after done."),
101            ref mut imm @ PromiseInner::Immediate(_) => {
102                match core::mem::replace(imm, PromiseInner::Empty) {
103                    PromiseInner::Immediate(r) => Poll::Ready(r),
104                    _ => unreachable!(),
105                }
106            }
107            PromiseInner::Deferred(ref mut f) => f.as_mut().poll(cx),
108        }
109    }
110}
111
112#[cfg(feature = "alloc")]
113impl<T, E> From<Result<T, E>> for Promise<T, E> {
114    fn from(value: Result<T, E>) -> Self {
115        Self {
116            inner: PromiseInner::Immediate(value),
117        }
118    }
119}
120
121#[cfg(feature = "alloc")]
122#[cfg(feature = "rpc_try")]
123impl<T> core::ops::Try for Promise<T, crate::Error> {
124    type Output = Self;
125    type Residual = Result<core::convert::Infallible, crate::Error>;
126
127    fn from_output(output: Self::Output) -> Self {
128        output
129    }
130
131    fn branch(self) -> core::ops::ControlFlow<Self::Residual, Self::Output> {
132        unimplemented!();
133    }
134}
135
136#[cfg(feature = "alloc")]
137#[cfg(feature = "rpc_try")]
138impl<T> core::ops::FromResidual for Promise<T, crate::Error> {
139    fn from_residual(residual: <Self as Try>::Residual) -> Self {
140        match residual {
141            Ok(_) => unimplemented!(),
142            Err(e) => Self::err(e),
143        }
144    }
145}
146
147/// A promise for a result from a method call.
148#[cfg(feature = "alloc")]
149#[must_use]
150pub struct RemotePromise<Results>
151where
152    Results: Pipelined + Owned + 'static,
153{
154    pub promise: Promise<Response<Results>, crate::Error>,
155    pub pipeline: Results::Pipeline,
156}
157
158/// A response from a method call, as seen by the client.
159#[cfg(feature = "alloc")]
160pub struct Response<Results> {
161    pub marker: PhantomData<Results>,
162    pub hook: alloc::boxed::Box<dyn ResponseHook>,
163}
164
165#[cfg(feature = "alloc")]
166impl<Results> Response<Results>
167where
168    Results: Pipelined + Owned,
169{
170    pub fn new(hook: alloc::boxed::Box<dyn ResponseHook>) -> Self {
171        Self {
172            marker: PhantomData,
173            hook,
174        }
175    }
176    pub fn get(&self) -> crate::Result<Results::Reader<'_>> {
177        self.hook.get()?.get_as()
178    }
179}
180
181/// A method call that has not been sent yet.
182#[cfg(feature = "alloc")]
183pub struct Request<Params, Results> {
184    pub marker: PhantomData<(Params, Results)>,
185    pub hook: alloc::boxed::Box<dyn RequestHook>,
186}
187
188#[cfg(feature = "alloc")]
189impl<Params, Results> Request<Params, Results>
190where
191    Params: Owned,
192{
193    pub fn new(hook: alloc::boxed::Box<dyn RequestHook>) -> Self {
194        Self {
195            hook,
196            marker: PhantomData,
197        }
198    }
199
200    pub fn get(&mut self) -> Params::Builder<'_> {
201        self.hook.get().get_as().unwrap()
202    }
203
204    pub fn set(&mut self, from: Params::Reader<'_>) -> crate::Result<()> {
205        self.hook.get().set_as(from)
206    }
207}
208
209#[cfg(feature = "alloc")]
210impl<Params, Results> Request<Params, Results>
211where
212    Results: Pipelined + Owned + 'static + Unpin,
213    <Results as Pipelined>::Pipeline: FromTypelessPipeline,
214{
215    pub fn send(self) -> RemotePromise<Results> {
216        let RemotePromise {
217            promise, pipeline, ..
218        } = self.hook.send();
219        let typed_promise = Promise::from_future(async move {
220            Ok(Response {
221                hook: promise.await?.hook,
222                marker: PhantomData,
223            })
224        });
225        RemotePromise {
226            promise: typed_promise,
227            pipeline: FromTypelessPipeline::new(pipeline),
228        }
229    }
230}
231
232/// A method call that has not been sent yet.
233#[cfg(feature = "alloc")]
234pub struct StreamingRequest<Params> {
235    pub marker: PhantomData<Params>,
236    pub hook: alloc::boxed::Box<dyn RequestHook>,
237}
238
239#[cfg(feature = "alloc")]
240impl<Params> StreamingRequest<Params>
241where
242    Params: Owned,
243{
244    pub fn get(&mut self) -> Params::Builder<'_> {
245        self.hook.get().get_as().unwrap()
246    }
247
248    pub fn send(self) -> Promise<(), Error> {
249        self.hook.send_streaming()
250    }
251}
252
253/// The values of the parameters passed to a method call, as seen by the server.
254#[cfg(feature = "alloc")]
255pub struct Params<T> {
256    pub marker: PhantomData<T>,
257    pub hook: alloc::boxed::Box<dyn ParamsHook>,
258}
259
260#[cfg(feature = "alloc")]
261impl<T> Params<T> {
262    pub fn new(hook: alloc::boxed::Box<dyn ParamsHook>) -> Self {
263        Self {
264            marker: PhantomData,
265            hook,
266        }
267    }
268    pub fn get(&self) -> crate::Result<T::Reader<'_>>
269    where
270        T: Owned,
271    {
272        self.hook.get()?.get_as()
273    }
274}
275
276/// The return values of a method, written in-place by the method body.
277#[cfg(feature = "alloc")]
278pub struct Results<T> {
279    pub marker: PhantomData<T>,
280    pub hook: alloc::boxed::Box<dyn ResultsHook>,
281}
282
283#[cfg(feature = "alloc")]
284impl<T> Results<T>
285where
286    T: Owned,
287{
288    pub fn new(hook: alloc::boxed::Box<dyn ResultsHook>) -> Self {
289        Self {
290            marker: PhantomData,
291            hook,
292        }
293    }
294
295    pub fn get(&mut self) -> T::Builder<'_> {
296        self.hook.get().unwrap().get_as().unwrap()
297    }
298
299    pub fn set(&mut self, other: T::Reader<'_>) -> crate::Result<()> {
300        self.hook.get().unwrap().set_as(other)
301    }
302
303    /// Call this method to signal that all of the capabilities have been filled in for this
304    /// `Results` and that pipelined calls should be allowed to start using those capabilities.
305    /// (Usually pipelined calls are enqueued until the initial call completes.)
306    pub fn set_pipeline(&mut self) -> crate::Result<()> {
307        self.hook.set_pipeline()
308    }
309}
310
311pub trait FromTypelessPipeline {
312    fn new(typeless: any_pointer::Pipeline) -> Self;
313}
314
315#[cfg(feature = "alloc")]
316impl<T: FromClientHook> FromTypelessPipeline for T {
317    fn new(typeless: any_pointer::Pipeline) -> Self {
318        Self::new(typeless.as_cap())
319    }
320}
321
322/// Trait implemented (via codegen) by all user-defined capability client types.
323#[cfg(feature = "alloc")]
324pub trait FromClientHook {
325    /// Wraps a client hook to create a new client.
326    fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self;
327
328    /// Unwraps client to get the underlying client hook.
329    fn into_client_hook(self) -> alloc::boxed::Box<dyn ClientHook>;
330
331    /// Gets a reference to the underlying client hook.
332    fn as_client_hook(&self) -> &dyn ClientHook;
333
334    /// Casts `self` to another instance of `FromClientHook`. This always succeeds,
335    /// but if the underlying capability does not actually implement `T`'s interface,
336    /// then method calls will fail with "unimplemented" errors.
337    fn cast_to<T: FromClientHook + Sized>(self) -> T
338    where
339        Self: Sized,
340    {
341        FromClientHook::new(self.into_client_hook())
342    }
343}
344
345#[cfg(feature = "alloc")]
346impl FromClientHook for alloc::boxed::Box<dyn ClientHook> {
347    fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self {
348        hook
349    }
350
351    fn into_client_hook(self) -> alloc::boxed::Box<dyn ClientHook> {
352        self
353    }
354
355    fn as_client_hook(&self) -> &dyn ClientHook {
356        self.as_ref()
357    }
358}
359
360/// An untyped client.
361#[cfg(feature = "alloc")]
362pub struct Client {
363    pub hook: alloc::boxed::Box<dyn ClientHook>,
364}
365
366#[cfg(feature = "alloc")]
367impl Client {
368    pub fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self {
369        Self { hook }
370    }
371
372    pub fn new_call<Params, Results>(
373        &self,
374        interface_id: u64,
375        method_id: u16,
376        size_hint: Option<MessageSize>,
377    ) -> Request<Params, Results> {
378        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
379        Request {
380            hook: typeless.hook,
381            marker: PhantomData,
382        }
383    }
384
385    pub fn new_streaming_call<Params>(
386        &self,
387        interface_id: u64,
388        method_id: u16,
389        size_hint: Option<MessageSize>,
390    ) -> StreamingRequest<Params> {
391        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
392        StreamingRequest {
393            hook: typeless.hook,
394            marker: PhantomData,
395        }
396    }
397
398    /// If the capability is actually only a promise, the returned promise resolves once the
399    /// capability itself has resolved to its final destination (or propagates the exception if
400    /// the capability promise is rejected).  This is mainly useful for error-checking in the case
401    /// where no calls are being made.  There is no reason to wait for this before making calls; if
402    /// the capability does not resolve, the call results will propagate the error.
403    pub fn when_resolved(&self) -> Promise<(), Error> {
404        self.hook.when_resolved()
405    }
406}
407
408#[cfg(feature = "alloc")]
409impl FromClientHook for Client {
410    fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self {
411        Client::new(hook)
412    }
413
414    fn into_client_hook(self) -> alloc::boxed::Box<dyn ClientHook> {
415        self.hook
416    }
417
418    fn as_client_hook(&self) -> &dyn ClientHook {
419        self.hook.as_ref()
420    }
421}
422
423#[cfg(feature = "alloc")]
424impl Clone for Client {
425    fn clone(&self) -> Self {
426        Self {
427            hook: self.hook.add_ref(),
428        }
429    }
430}
431
432/// The return value of Server::dispatch_call().
433#[cfg(feature = "alloc")]
434pub struct DispatchCallResult {
435    /// Promise for completion of the call.
436    pub promise: Promise<(), Error>,
437
438    /// If true, this method was declared as `-> stream;`. If this call throws
439    /// an exception, then all future calls on the capability will throw the
440    /// same exception.
441    pub is_streaming: bool,
442}
443
444#[cfg(feature = "alloc")]
445impl DispatchCallResult {
446    pub fn new(promise: Promise<(), Error>, is_streaming: bool) -> Self {
447        Self {
448            promise,
449            is_streaming,
450        }
451    }
452}
453
454/// Type alias that allows us to avoid using `alloc` directly in generated code,
455/// which would require an `extern crate alloc` in the crate root.
456#[cfg(feature = "alloc")]
457pub type Rc<T> = alloc::rc::Rc<T>;
458
459/// An untyped server.
460#[cfg(feature = "alloc")]
461pub trait Server {
462    fn dispatch_call(
463        self,
464        interface_id: u64,
465        method_id: u16,
466        params: Params<any_pointer::Owned>,
467        results: Results<any_pointer::Owned>,
468    ) -> DispatchCallResult;
469
470    fn as_ptr(&self) -> usize;
471}
472
473/// Trait to track the relationship between generated Server traits and Client structs.
474#[cfg(feature = "alloc")]
475pub trait FromServer<S>: FromClientHook {
476    // Implemented by the generated ServerDispatch struct.
477    type Dispatch: Server + 'static + core::ops::Deref<Target = S> + Clone;
478
479    fn from_server(s: Rc<S>) -> Self::Dispatch;
480}
481
482/// Gets the "resolved" version of a capability. One place this is useful is for pre-resolving
483/// the argument to `capnp_rpc::CapabilityServerSet::get_local_server_of_resolved()`.
484#[cfg(feature = "alloc")]
485pub async fn get_resolved_cap<C: FromClientHook>(cap: C) -> C {
486    let mut hook = cap.into_client_hook();
487    let _ = hook.when_resolved().await;
488    while let Some(resolved) = hook.get_resolved() {
489        hook = resolved;
490    }
491    FromClientHook::new(hook)
492}