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()
}