qrpc_sdk/
parser.rs

1//! An I/O abstraction module for the qrpc system
2//!
3//! The qrpc system heavily builds on capnproto as an exchange and RPC
4//! format.  Unfortunately the capnproto-rs interface is pretty shit
5//! (this is rude, I know but it's just not great...).  Having to
6//! interact with it to write services for qaul.net might be a dealbreaker.
7//!
8//! And so... this module tries to abstract as much of the low-level
9//! ugliness away.  Instead, you pass it a buffer with a message, and
10//! it parses it for you, with some simple type constraits that are
11//! easy to enforce in your application.  Additionally, it exposes
12//! some more convenient builders as well (although the builder APIs
13//! in the original crate are quite good).
14
15use crate::{
16    error::{RpcError, RpcResult},
17    io::Message,
18    Identity,
19};
20use capnp::{
21    message::{Reader, ReaderOptions},
22    serialize::OwnedSegments,
23    serialize_packed as ser,
24    traits::FromPointerReader,
25};
26use std::marker::PhantomData;
27
28/// A result-wrapper for capnproto related failures
29pub type Result<T> = capnp::Result<T>;
30
31/// A utility type to read capnproto message types
32pub struct MsgReader<'s, T: FromPointerReader<'s>> {
33    r: Reader<OwnedSegments>,
34    _t: &'s PhantomData<T>,
35}
36
37impl<'s, T: FromPointerReader<'s>> MsgReader<'s, T> {
38    /// Parse a message buffer into a set of owned segments
39    pub fn new(buf: Vec<u8>) -> Result<Self> {
40        ser::read_message(buf.as_slice(), ReaderOptions::new()).map(|r| Self {
41            r,
42            _t: &PhantomData,
43        })
44    }
45
46    /// Get the root object from this reader, if it exists
47    ///
48    /// This function returns a reference to the inner reader for you.
49    /// Because the way this trait is implemented, the parent can't go
50    /// out of scope.
51    ///
52    /// To get access to the fields of a type, you need to type-cast
53    /// it as a `T::Reader`, so to read a `service` type (such as the
54    /// one provided by this sdk crate), you would cast it as
55    /// `service::Reader`.
56    ///
57    /// ```
58    /// # use qrpc_sdk::parser::Result;
59    /// use qrpc_sdk::{parser::MsgReader, types::service};
60    ///
61    /// # fn run_code() -> Result<()> {
62    /// # let buf = vec![];
63    /// let msg = MsgReader::new(buf)?;
64    /// let r: service::Reader = msg.get_root()?;
65    /// println!("DESC: {}", r.get_description()?);
66    /// # Ok(())
67    /// # }
68    /// ```
69    ///
70    /// Some types will be capability sets, encoded in an unnamed
71    /// union.  Because this is a very common pattern, here is an
72    /// example usage of how to implement matching for the types
73    /// defined in this crate.
74    ///
75    /// ```
76    /// use qrpc_sdk::{parser::MsgReader, error::RpcError, rpc::capabilities::{Reader, Which}};
77    /// # fn foo() -> Result<(), RpcError> {
78    /// # let reader = qrpc_sdk::builders::parse_rpc_msg(vec![]).unwrap();
79    ///
80    /// // Get the `reader` by calling `builders::parse_rpc_msg(...)`
81    /// let r: Reader = reader.get_root().unwrap();
82    /// match r.which() {
83    ///     Ok(Which::Register(Ok(reg))) => handle_register(reg),
84    ///     Ok(Which::Unregister(Ok(unreg))) => handle_unregister(unreg),
85    ///     Ok(Which::Upgrade(Ok(upgr))) => handle_upgrade(upgr),
86    ///     _ => eprintln!("Invalid variant/ decode!"),
87    /// }
88    /// # Ok(())
89    /// # }
90    ///
91    /// use qrpc_sdk::rpc::{register, unregister, upgrade};
92    ///
93    /// fn handle_register(_: register::Reader) { /* ... */}
94    /// fn handle_unregister(_: unregister::Reader) { /* ... */}
95    /// fn handle_upgrade(_: upgrade::Reader) { /* ... */}
96    /// ```
97    ///
98    /// The above code can be found in the [qrpc-broker] crate.  Your
99    /// own service code will differ, but this should give you a good
100    /// idea how to start!
101    ///
102    /// [qrpc-broker]: https://docs.qaul.net/api/qrpc_broker/index.html
103    pub fn get_root(&'s self) -> Result<T> {
104        self.r.get_root()
105    }
106}
107
108/// Parse a message into a new ID
109pub fn resp_id(msg: Message) -> RpcResult<Identity> {
110    use crate::rpc::sdk_reply::{HashId, Reader};
111
112    let Message {
113        id: _,
114        to: _,
115        from: _,
116        data,
117    } = msg;
118
119    let r = MsgReader::new(data)?;
120    let reader: Reader = r.get_root()?;
121    match reader.which() {
122        Ok(HashId(Ok(id))) => Ok(Identity::from_string(&id.to_string())),
123        _ => Err(RpcError::EncoderFault(
124            "Operation failed: unknown component address!".into(),
125        )),
126    }
127}