libqaul_sdk/
lib.rs

1//! qaul development SDK.
2//!
3//!
4//!
5//! The API surface is exposed via the `QaulRpc` type, while data
6//! types are exposed via the `libqaul-types` crate (re-exported from
7//! this crate via [`types`]).
8//!
9//! Check the qrpc-sdk documentation to learn how to use this crate.
10
11pub use libqaul_types::*;
12pub use qrpc_sdk::{
13    default_socket_path,
14    error::{RpcError, RpcResult},
15    io::Message,
16    RpcSocket, Service,
17};
18pub use std::{str, sync::Arc};
19
20use rpc::{Capabilities, Reply, UserCapabilities, UserReply, ADDRESS};
21use users::UserAuth;
22
23/// A qrpc wrapper for libqaul
24///
25/// This component exposes a public API surface to mirror the libqaul
26/// crate.  This means that other clients on the qrpc bus can include
27/// this surface to get access to all libqaul functions, thate are
28/// transparently mapped to the underlying libqaul instance
29/// potentially running in a different process.
30pub struct QaulRpc {
31    socket: Arc<RpcSocket>,
32    addr: String,
33}
34
35impl QaulRpc {
36    pub fn connect(service: &Service) -> RpcResult<Self> {
37        let socket = service.get_socket();
38        let addr = service.name.clone();
39        Ok(Self { socket, addr })
40    }
41
42    pub fn users<'q>(&'q self) -> UserRpc<'q> {
43        UserRpc { rpc: self }
44    }
45
46    async fn send(&self, cap: Capabilities) -> RpcResult<Reply> {
47        let json = cap.to_json();
48        let msg = Message::to_addr(ADDRESS, &self.addr, json.as_bytes().to_vec());
49
50        self.socket
51            .send(msg, |Message { data, .. }| {
52                match str::from_utf8(data.as_slice())
53                    .ok()
54                    .and_then(|json| Reply::from_json(json))
55                {
56                    // Map the Reply::Error field to a Rust error
57                    Some(Reply::Error(e)) => Err(RpcError::Other(e.to_string())),
58                    None => Err(RpcError::EncoderFault("Invalid json payload!".into())),
59                    Some(r) => Ok(r),
60                }
61            })
62            .await
63    }
64}
65
66pub struct UserRpc<'q> {
67    rpc: &'q QaulRpc,
68}
69
70impl<'q> UserRpc<'q> {
71    pub async fn create<S: Into<String>>(&'q self, pw: S) -> RpcResult<UserAuth> {
72        if let Reply::Users(UserReply::Auth(auth)) = self
73            .rpc
74            .send(Capabilities::Users(UserCapabilities::Create {
75                pw: pw.into(),
76            }))
77            .await?
78        {
79            Ok(auth)
80        } else {
81            Err(RpcError::EncoderFault("Invalid reply payload!".into()))
82        }
83    }
84}