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/// Trait implemented (via codegen) by all user-defined capability client types.
316#[cfg(feature = "alloc")]
317pub trait FromClientHook {
318    /// Wraps a client hook to create a new client.
319    fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self;
320
321    /// Unwraps client to get the underlying client hook.
322    fn into_client_hook(self) -> alloc::boxed::Box<dyn ClientHook>;
323
324    /// Gets a reference to the underlying client hook.
325    fn as_client_hook(&self) -> &dyn ClientHook;
326
327    /// Casts `self` to another instance of `FromClientHook`. This always succeeds,
328    /// but if the underlying capability does not actually implement `T`'s interface,
329    /// then method calls will fail with "unimplemented" errors.
330    fn cast_to<T: FromClientHook + Sized>(self) -> T
331    where
332        Self: Sized,
333    {
334        FromClientHook::new(self.into_client_hook())
335    }
336}
337
338#[cfg(feature = "alloc")]
339impl FromClientHook for alloc::boxed::Box<dyn ClientHook> {
340    fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self {
341        hook
342    }
343
344    fn into_client_hook(self) -> alloc::boxed::Box<dyn ClientHook> {
345        self
346    }
347
348    fn as_client_hook(&self) -> &dyn ClientHook {
349        self.as_ref()
350    }
351}
352
353/// An untyped client.
354#[cfg(feature = "alloc")]
355pub struct Client {
356    pub hook: alloc::boxed::Box<dyn ClientHook>,
357}
358
359#[cfg(feature = "alloc")]
360impl Client {
361    pub fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self {
362        Self { hook }
363    }
364
365    pub fn new_call<Params, Results>(
366        &self,
367        interface_id: u64,
368        method_id: u16,
369        size_hint: Option<MessageSize>,
370    ) -> Request<Params, Results> {
371        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
372        Request {
373            hook: typeless.hook,
374            marker: PhantomData,
375        }
376    }
377
378    pub fn new_streaming_call<Params>(
379        &self,
380        interface_id: u64,
381        method_id: u16,
382        size_hint: Option<MessageSize>,
383    ) -> StreamingRequest<Params> {
384        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
385        StreamingRequest {
386            hook: typeless.hook,
387            marker: PhantomData,
388        }
389    }
390
391    /// If the capability is actually only a promise, the returned promise resolves once the
392    /// capability itself has resolved to its final destination (or propagates the exception if
393    /// the capability promise is rejected).  This is mainly useful for error-checking in the case
394    /// where no calls are being made.  There is no reason to wait for this before making calls; if
395    /// the capability does not resolve, the call results will propagate the error.
396    pub fn when_resolved(&self) -> Promise<(), Error> {
397        self.hook.when_resolved()
398    }
399}
400
401#[cfg(feature = "alloc")]
402impl FromClientHook for Client {
403    fn new(hook: alloc::boxed::Box<dyn ClientHook>) -> Self {
404        Client::new(hook)
405    }
406
407    fn into_client_hook(self) -> alloc::boxed::Box<dyn ClientHook> {
408        self.hook
409    }
410
411    fn as_client_hook(&self) -> &dyn ClientHook {
412        self.hook.as_ref()
413    }
414}
415
416#[cfg(feature = "alloc")]
417impl Clone for Client {
418    fn clone(&self) -> Self {
419        Self {
420            hook: self.hook.add_ref(),
421        }
422    }
423}
424
425/// The return value of Server::dispatch_call().
426#[cfg(feature = "alloc")]
427pub struct DispatchCallResult {
428    /// Promise for completion of the call.
429    pub promise: Promise<(), Error>,
430
431    /// If true, this method was declared as `-> stream;`. If this call throws
432    /// an exception, then all future calls on the capability will throw the
433    /// same exception.
434    pub is_streaming: bool,
435}
436
437#[cfg(feature = "alloc")]
438impl DispatchCallResult {
439    pub fn new(promise: Promise<(), Error>, is_streaming: bool) -> Self {
440        Self {
441            promise,
442            is_streaming,
443        }
444    }
445}
446
447/// An untyped server.
448#[cfg(feature = "alloc")]
449pub trait Server {
450    fn dispatch_call(
451        &mut self,
452        interface_id: u64,
453        method_id: u16,
454        params: Params<any_pointer::Owned>,
455        results: Results<any_pointer::Owned>,
456    ) -> DispatchCallResult;
457}
458
459/// Trait to track the relationship between generated Server traits and Client structs.
460#[cfg(feature = "alloc")]
461pub trait FromServer<S>: FromClientHook {
462    // Implemented by the generated ServerDispatch struct.
463    type Dispatch: Server + 'static + core::ops::DerefMut<Target = S>;
464
465    fn from_server(s: S) -> Self::Dispatch;
466}
467
468/// Gets the "resolved" version of a capability. One place this is useful is for pre-resolving
469/// the argument to `capnp_rpc::CapabilityServerSet::get_local_server_of_resolved()`.
470#[cfg(feature = "alloc")]
471pub async fn get_resolved_cap<C: FromClientHook>(cap: C) -> C {
472    let mut hook = cap.into_client_hook();
473    let _ = hook.when_resolved().await;
474    while let Some(resolved) = hook.get_resolved() {
475        hook = resolved;
476    }
477    FromClientHook::new(hook)
478}