1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
#![deny(missing_docs)]
#![forbid(unsafe_code)]
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
//! Networking and sync protocol types for [Save Our Secrets](https://saveoursecrets.com).
// There are two layers to the types in this module; the wire
// types which are defined in the protobuf files are prefixed
// with `Wire` and then there are the binding types.
//
// Each binding type wraps an inner wire type and converts
// infallibly to the inner wire type and fallibly from
// the inner wire type which allows us to convert between
// the limited protobuf types and the enforced optionality
// of protobufs.
//
// Encoding and decoding is provided by a blanket implementation
// so that we can provide `encode()` and `decode()` functions for
// types declared in the SDK library.
//
// A 64-bit machine is assumed as we cast between `u64` and `usize`
// for convenience, the code may panic on 32-bit machines.
mod bindings;
mod error;
mod sync;
pub use bindings::*;
pub use error::Error;
pub use sync::*;
use prost::{bytes::Buf, Message};
#[cfg(test)]
mod tests;
pub use sos_sdk as sdk;
/// Result type for the wire protocol.
pub type Result<T> = std::result::Result<T, Error>;
/// Trait for encoding and decoding protobuf generated types.
///
/// A blanket implementation adds this to any [prost::Message]
/// and runs the encoding and decoding using `spawn_blocking`.
#[doc(hidden)]
pub trait ProtoMessage {
/// Encode this message.
#[allow(async_fn_in_trait)]
async fn encode_proto(self) -> Result<Vec<u8>>;
/// Decode a message.
#[allow(async_fn_in_trait)]
async fn decode_proto<B>(buffer: B) -> Result<Self>
where
B: Buf + Send + 'static,
Self: Sized;
}
impl<T> ProtoMessage for T
where
T: Message + Default + 'static,
{
async fn encode_proto(self) -> Result<Vec<u8>> {
tokio::task::spawn_blocking(move || {
let mut buf = Vec::new();
buf.reserve(self.encoded_len());
self.encode(&mut buf)?;
Ok(buf)
})
.await?
}
async fn decode_proto<B>(buffer: B) -> Result<Self>
where
B: Buf + Send + 'static,
Self: Sized,
{
tokio::task::spawn_blocking(move || Ok(Self::decode(buffer)?)).await?
}
}
/// Marker trait to indicate a binding type that
/// converts to a protobuf type.
trait ProtoBinding {
type Inner: Message + Default;
}
/// Trait for wire protocol encoding and decoding.
#[doc(hidden)]
pub trait WireEncodeDecode {
/// Encode this request.
#[allow(async_fn_in_trait)]
async fn encode(self) -> Result<Vec<u8>>;
/// Decode this request.
#[allow(async_fn_in_trait)]
async fn decode<B>(buffer: B) -> Result<Self>
where
B: Buf + Send + 'static,
Self: Sized;
}
impl<T> WireEncodeDecode for T
where
T: ProtoBinding + Send + 'static,
<T as ProtoBinding>::Inner: From<T> + 'static,
T: TryFrom<<T as ProtoBinding>::Inner, Error = Error>,
{
async fn encode(self) -> Result<Vec<u8>> {
tokio::task::spawn_blocking(move || {
let value: <Self as ProtoBinding>::Inner = self.into();
let mut buf = Vec::new();
buf.reserve(value.encoded_len());
value.encode(&mut buf)?;
Ok(buf)
})
.await?
}
async fn decode<B>(buffer: B) -> Result<Self>
where
B: Buf + Send + 'static,
Self: Sized,
{
tokio::task::spawn_blocking(move || {
let result = <<Self as ProtoBinding>::Inner>::decode(buffer)?;
Ok(result.try_into()?)
})
.await?
}
}
fn decode_uuid(id: &[u8]) -> Result<uuid::Uuid> {
let id: [u8; 16] = id.try_into()?;
Ok(uuid::Uuid::from_bytes(id))
}
fn encode_uuid(id: &uuid::Uuid) -> Vec<u8> {
id.as_bytes().to_vec()
}