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}