sos_protocol/
lib.rs

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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#![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).
//!
//! When the `account` feature is enabled [SyncStorage] will be
//! implemented for `LocalAccount`.

// 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;
pub mod compression;
pub mod constants;
mod error;
#[cfg(feature = "integration")]
pub mod integration;
pub mod local_transport;
pub mod server_helpers;
mod sync;
mod traits;

#[cfg(any(feature = "files", feature = "listen"))]
pub mod transfer;

pub use bindings::*;
pub use error::{AsConflict, ConflictError, Error, NetworkError};
pub use sync::*;
pub use traits::*;

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>,
{
    #[cfg(not(target_arch = "wasm32"))]
    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?
    }

    #[cfg(not(target_arch = "wasm32"))]
    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?
    }

    #[cfg(target_arch = "wasm32")]
    async fn encode(self) -> Result<Vec<u8>> {
        let value: <Self as ProtoBinding>::Inner = self.into();
        let mut buf = Vec::new();
        buf.reserve(value.encoded_len());
        value.encode(&mut buf)?;
        Ok(buf)
    }

    #[cfg(target_arch = "wasm32")]
    async fn decode<B>(buffer: B) -> Result<Self>
    where
        B: Buf + Send + 'static,
        Self: Sized,
    {
        let result = <<Self as ProtoBinding>::Inner>::decode(buffer)?;
        Ok(result.try_into()?)
    }
}

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