Commit 77826b7b authored by Katharina Fey's avatar Katharina Fey

qrpc-sdk: refactoring crate and documentation structure

parent 576b18e6
......@@ -5,6 +5,9 @@ version = "0.1.0"
authors = ["Kaiden Fey <kookie@spacekookie.de>"]
edition = "2018"
[features]
internals = []
[dependencies]
identity = { path = "../../ratman/identity", version = "0.4", package = "ratman-identity" }
......
......@@ -2,7 +2,9 @@ use capnpc::CompilerCommand as Cc;
fn main() {
Cc::new()
.file("schema/carrier.capnp")
.file("schema/base.capnp") // base wire wrapper
.file("schema/types.capnp") // sdk-data types
.file("schema/cap.capnp") // rpc-api types
.run()
.expect("Failed compiling schema/carrier.capnp!");
}
@0xf9778e7153e5e2bf;
# Base message definitions for the qrpc protocol
# A message is always from one component on the bus to another, meaning that it
# has an address and data section. The broker doesn't need to understand the
# data types, so only the address is important
struct RpcMessage {
addr @0 :Text;
data @1 :Data;
}
@0xf48ba5c2f4889b61;
# qrpc-sdk capabilities section
#
# Because qrpc doesn't use the capnproto RPC layer, each function call is
# mapped to a type. The top-level of this abstraction is the `Capabilities`
# type, which is simply a tagged union (enum) to wrap around possible values.
using import "types.capnp".Service;
# Reply to any of the previous commands
struct SdkReply {
union {
hashId @0 :Text;
success @1 :Bool;
}
}
struct Capabilities {
union {
register @0 :Register;
unregister @1 :Unregister;
upgrade @2 :Upgrade;
}
}
struct Register {
service @0 :Service;
}
struct Unregister {
hashId @0 :Text;
}
struct Upgrade {
service @0 :Service;
hashId @1 :Text;
}
@0x97aa1eae99a0bced; # this was generated by capnp id
@0xd20f567e3e118ea6;
# Type definitions for the qrpc-sdk
# The SDK handles the creation and management of services, and as such the
# Service type is the only real data type provided by this layer. It's used by
# all components to register themselves and abstract away other components on
# the bus.
struct Service {
# Names follow reverse FQD specification: net.qaul.example
# Names follow reverse FQD specification: net.qaul.my_app
name @0 :Text;
# Versions are up to services to interpret. A simple
# incrementing number is fine, but feel free to encode semver
......@@ -11,25 +17,3 @@ struct Service {
description @2 :Text;
}
struct Register {
service @0 :Service;
respHashId @1 :Text;
}
struct Unregister {
hashId @0 :Text;
resp @1 :Bool;
}
struct Upgrade {
service @0 :Service;
hashId @1 :Text;
resp @2 :Bool;
}
struct Carrier {
target @0 :Text;
data @1 :Data;
}
\ No newline at end of file
//! A set of message builder utilities
//!
//! It's recommended to write similar abstraction layers in your own
//! crate, so to make it easy for other third-party developers to use
......@@ -11,32 +11,35 @@ use crate::Service;
use identity::Identity;
/// Generate an registry message for this service
pub fn register(service: &Service) -> (String, Vec<u8>) {
pub fn register(_service: &Service) -> (String, Vec<u8>) {
todo!()
}
/// Generate an unregistry message for this service
pub fn unregister(hash_id: Identity) -> (String, Vec<u8>) {
pub fn unregister(_hash_id: Identity) -> (String, Vec<u8>) {
todo!()
}
pub fn upgrade(s: &Service, hash_id: Identity) -> (String, Vec<u8>) {
/// Generate an upgrade message for this service
pub fn upgrade(_service: &Service, _hash_id: Identity) -> (String, Vec<u8>) {
todo!()
}
/// This module is only included for debugging reasons. There's
/// basically no reason to call this function directly.
#[doc(hidden)]
#[cfg_attr(not(feature = "internals"), doc(hidden))]
pub mod _internal {
use crate::{io::MsgReader, rpc::*, Service};
use crate::{io::MsgReader, types::rpc_message};
use byteorder::{BigEndian, ByteOrder};
use capnp::{message::Builder as Bld, serialize_packed};
use socket2::Socket;
pub fn to(target: String, data: Vec<u8>) -> Vec<u8> {
/// Take address and data and turn it into a basic rpc message
pub fn to(addr: String, data: Vec<u8>) -> Vec<u8> {
let mut msg = Bld::new_default();
let mut carrier = msg.init_root::<carrier::Builder>();
carrier.set_target(&target);
let mut carrier = msg.init_root::<rpc_message::Builder>();
carrier.set_addr(&addr);
carrier.set_data(&data);
let mut buffer = vec![];
......@@ -50,10 +53,16 @@ pub mod _internal {
message
}
/// Read an rpc message from the socket
///
/// Feel free to use this function in your
///
/// The first field in the tuple is the destination address, the
/// second is the data payload.
pub fn from(socket: &Socket) -> (String, Vec<u8>) {
let mut len = vec![0; 8];
loop {
let (l, a) = socket.peek_from(&mut len).unwrap();
let (l, _) = socket.peek_from(&mut len).unwrap();
if l == 8 {
break;
}
......@@ -65,10 +74,10 @@ pub mod _internal {
socket.recv_from(&mut buffer).unwrap();
let msg = MsgReader::new(buffer).unwrap();
let carrier: carrier::Reader = msg.get_root().unwrap();
let target = carrier.get_target().unwrap();
let carrier: rpc_message::Reader = msg.get_root().unwrap();
let addr = carrier.get_addr().unwrap();
let data = carrier.get_data().unwrap();
(target.to_string(), data.to_vec())
(addr.to_string(), data.to_vec())
}
}
......@@ -6,7 +6,14 @@
//!
//! These crate docs describe the API and basic usage. For an
//! overview of the core concepts of this ecosystem, consult the
//! [contributors manual][manual]
//! [contributors manual][manual].
//!
//! Additionally, you can access documentation of the internal
//! utilities by passing `--features internal` to your cargo
//! invocation. These components are exposed via the API either way,
//! but only documented on demand to not clutter the main
//! documentation.
//!
//!
//! [manual]: https://docs.qaul.net/contributors/technical/rpc-layer
//!
......@@ -43,6 +50,8 @@
//! have to implement this mechanism to be usable by other services on
//! the RPC bus.
//!
//! [`ServiceConnector`]: ./trait.ServiceConnector.html
//!
//! After that you can call functions on the public API type of the
//! component. You can get a copy of it via your service handle:
//! `service.component("net.qaul.libqaul")`.
......@@ -57,35 +66,49 @@ pub mod io;
// FIXME: currently the protocols have to be in the root of the crate
// because of [this issue][i] in the capnproto codegen units:
// [i]: https://github.com/capnproto/capnproto-rust/issues/194
pub(crate) mod carrier_capnp {
pub(crate) mod base_capnp {
#![allow(unused)] // don't bother me pls
include!(concat!(env!("OUT_DIR"), "/schema/base_capnp.rs"));
}
pub(crate) mod types_capnp {
#![allow(unused)] // don't bother me pls
include!(concat!(env!("OUT_DIR"), "/schema/carrier_capnp.rs"));
include!(concat!(env!("OUT_DIR"), "/schema/types_capnp.rs"));
}
pub(crate) mod cap_capnp {
#![allow(unused)] // don't bother me pls
include!(concat!(env!("OUT_DIR"), "/schema/cap_capnp.rs"));
}
/// Basic qrpc trasmission types
/// qrpc message types
///
/// This interface is exposed to let other parts of the qrpc ecosystem
/// parse and generate these types. When using this library directly,
/// try to avoid using them. Use the main type interface documented
/// in the root of the crate instead.
#[cfg_attr(not(feature = "internals"), doc(hidden))]
pub mod types {
pub use crate::carrier_capnp::service;
pub use crate::base_capnp::rpc_message;
pub use crate::types_capnp::service;
}
/// Unterlying RPC message types
/// RPC message types used by the qrpc-sdk
///
/// As with the data types used by this crate, try to avoid using them
/// directly. Instead use the main API of the crate which invoces
/// these types internally
#[cfg_attr(not(feature = "internals"), doc(hidden))]
pub mod rpc {
pub use crate::carrier_capnp::{carrier, register, unregister, upgrade};
pub use crate::cap_capnp::{capabilities, register, sdk_reply, unregister, upgrade};
}
mod service;
mod socket;
pub mod builders;
pub mod errors;
pub mod socket;
pub use service::Service;
pub use service::{Service, ServiceConnector};
pub use socket::{default_socket_path, RpcSocket};
#[cfg_attr(not(feature = "internals"), doc(hidden))]
pub use socket::{SockAddr as PosixAddr, Socket as PosixSocket};
......@@ -14,9 +14,9 @@ fn _socket(s: &Service) -> &Arc<RpcSocket> {
/// update any data you want your service to broadcast to other
/// participants on the QRPC system.
pub struct Service {
name: String,
version: u16,
description: String,
pub name: String,
pub version: u16,
pub description: String,
hash_id: Option<Identity>,
socket: Option<Arc<RpcSocket>>,
}
......@@ -40,13 +40,25 @@ impl Service {
pub async fn register(&mut self, socket: Arc<RpcSocket>) -> RpcResult<Identity> {
self.socket = Some(socket);
let (target, reg_msg) = builders::register(&self);
use crate::rpc::register;
// Send a message to the backend and handle the reply, which
// needs to contain a hash_id which we parse and then return
// from this function as an Identity.
use crate::rpc::sdk_reply::{Reader as ReplReader, Which as ReplWhich};
_socket(self)
.send_msg(target, reg_msg, |reader| {
let r: register::Reader = reader.get_root().unwrap();
todo!()
let r: ReplReader = reader.get_root().unwrap();
match r.which() {
Ok(ReplWhich::HashId(Ok(id))) => Ok(Identity::from_string(&id.to_string())),
_ => todo!(), // This can still happen but I'm lazy right now
}
})
.await
.map(|id| {
// self-assign the hash-id
self.hash_id = Some(id);
id
})
}
/// Get the `hash_id` field of this service, if it's set
......
......@@ -38,7 +38,7 @@ pub fn default_socket_path() -> PathBuf {
/// that your service can be used by other services)
pub struct RpcSocket {
inner: Socket,
addr: SockAddr,
_addr: SockAddr,
run: AtomicBool,
listening: AtomicBool,
timeout: Duration,
......@@ -76,13 +76,13 @@ impl RpcSocket {
P: AsRef<Path>,
F: Fn(Socket, SockAddr) + Send + Sync + 'static,
{
let (inner, addr) = Self::new_socket(path)?;
inner.bind(&addr)?;
let (inner, _addr) = Self::new_socket(path)?;
inner.bind(&_addr)?;
inner.listen(32)?;
let arc = Arc::new(Self {
inner,
addr,
_addr,
timeout: Duration::from_secs(5),
run: AtomicBool::from(true),
listening: AtomicBool::from(true),
......@@ -108,12 +108,12 @@ impl RpcSocket {
/// Setup is the same as when calling `new`, except that you can
/// choose an explicit timeout, instead of the default.
pub fn with_duration<P: AsRef<Path>>(path: P, timeout: Duration) -> Result<Arc<Self>> {
let (inner, addr) = Self::new_socket(path)?;
inner.connect(&addr)?;
let (inner, _addr) = Self::new_socket(path)?;
inner.connect(&_addr)?;
Ok(Arc::new(Self {
inner,
addr,
_addr,
timeout,
run: AtomicBool::from(true),
listening: AtomicBool::from(false),
......@@ -156,7 +156,11 @@ impl RpcSocket {
S: Into<String>,
M: FromPointerReader<'s>,
{
// First send out the message
let msg = builders::_internal::to(target.into(), msg);
self.inner.send(&msg).unwrap();
// Wait for a reply to handle
let _self = Arc::clone(self);
self.with_timeout(async move {
let (_, buf) = builders::_internal::from(&_self.inner);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment