capnproto-rust
capnproto-rust copied to clipboard
Improve documentation of capability list
I like this package a lot, but I'm struggling with how to create a capability list. The documentation for the module just says "List of capabilities." I assume it should somehow be done using the Builder, but even then, I can't figure out how to use it to create a capability list from a Vec of RPC clients.
For me and probably other users, some more documentation around how to actually create a capability list would be useful.
having the same issue.
Alright, I managed to find a solution, 2 actually... though this still doesn't feel correct.
Using client and extracting its hook:
It is possible to extract the client hook from a server object by making it into a client first:
let server_implementations = ...; // Some Vec of server object implementations
let mut list = ...; // Your capability list builder, usually obtained by calling results.init_field_name(data.len());
for (index, server) in server_implementations.into_iter().enumerate() {
let client_hook = capnp_rpc::new_client::<your_capnp::ty::Client, _>(server).client.hook;
list.set(index as u32, client_hook);
}
Using your own wrapper type
Alternatively, here is a way which probably has tiny bit less overhead by not constructing a client object:
use std::marker::PhantomData;
use capnp::capability::{FromClientHook, FromServer};
use capnp::private::capability::ClientHook;
pub struct ClientHookProxy<T, S>
where
T: FromServer<S>,
{
hook: Box<dyn ClientHook>,
_data_t: PhantomData<T>,
_data_s: PhantomData<S>,
}
impl<T, S> ClientHookProxy<T, S>
where
T: FromServer<S>,
{
pub fn apply(server: S) -> Box<dyn ClientHook> {
capnp_rpc::new_client::<Self, _>(server).into()
}
}
impl<T, S> FromClientHook for ClientHookProxy<T, S>
where
T: FromServer<S>,
{
fn new(hook: Box<dyn ClientHook>) -> Self {
Self {
hook,
_data_t: PhantomData,
_data_s: PhantomData,
}
}
}
impl<T, S> FromServer<S> for ClientHookProxy<T, S>
where
T: FromServer<S>,
{
type Dispatch = T::Dispatch;
fn from_server(s: S) -> Self::Dispatch {
T::from_server(s)
}
}
impl<T, S> Into<Box<dyn ClientHook>> for ClientHookProxy<T, S>
where
T: FromServer<S>,
{
fn into(self) -> Box<dyn ClientHook> {
self.hook
}
}
Use such as:
let server_implementations = ...; // Some Vec of server object implementations
let mut list = ...; // Your capability list builder, usually obtained by calling results.init_field_name(data.len());
for (index, server) in server_implementations.into_iter().enumerate() {
let client_hook = ClientHookProxy::<your_capnp::ty::Client, _>::apply(server);
list.set(index as u32, client_hook);
}
Futher work
I don't think thats the intended way. I'm not sure if I'm overlooking something, but in case there is no better way to do that with the current library, improvement is required. I consider both of the approaches above workarounds - it does the job for now, but documentation and usability needs to improve.
One thing that could help is if we add an IntoInternalClientHook trait, similar to the existing IntoInternalStructReader method: https://github.com/capnproto/capnproto-rust/blob/fad4f953194cd1cce9da8cef51234a7608364161/capnp/src/traits.rs#L36
Then capability_list::Builder::set() could have a friendlier type signature.