sos_protocol/
lib.rs

1#![deny(missing_docs)]
2#![forbid(unsafe_code)]
3#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
4//! Networking and sync protocol types for the
5//! [Save Our Secrets](https://saveoursecrets.com) SDK.
6
7// There are two layers to the types in this module; the wire
8// types which are defined in the protobuf files are prefixed
9// with `Wire` and then there are the binding types.
10//
11// Each binding type wraps an inner wire type and converts
12// infallibly to the inner wire type and fallibly from
13// the inner wire type which allows us to convert between
14// the limited protobuf types and the enforced optionality
15// of protobufs.
16//
17// Encoding and decoding is provided by a blanket implementation
18// so that we can provide `encode()` and `decode()` functions for
19// types declared in the SDK library.
20//
21// A 64-bit machine is assumed as we cast between `u64` and `usize`
22// for convenience, the code may panic on 32-bit machines.
23
24mod bindings;
25pub mod constants;
26mod diff;
27mod error;
28#[cfg(feature = "network-client")]
29pub mod network_client;
30mod traits;
31
32#[cfg(any(
33    feature = "files",
34    feature = "listen",
35    feature = "network-client"
36))]
37pub mod transfer;
38
39#[cfg(feature = "hashcheck")]
40pub mod hashcheck;
41
42pub use bindings::*;
43pub use diff::*;
44pub use error::{AsConflict, ConflictError, Error, ErrorReply, NetworkError};
45pub use traits::*;
46
47use prost::{bytes::Buf, Message};
48
49#[cfg(feature = "network-client")]
50pub use reqwest;
51
52#[cfg(any(feature = "listen", feature = "pairing"))]
53pub use tokio_tungstenite;
54
55/// Result type for the wire protocol.
56pub(crate) type Result<T> = std::result::Result<T, Error>;
57
58/// How to resolve hard conflicts.
59#[derive(Default, Debug)]
60pub enum HardConflictResolver {
61    /// Automatically fetch and overwrite account data.
62    #[default]
63    AutomaticFetch,
64}
65
66/// Options for sync operation.
67#[derive(Default, Debug)]
68pub struct SyncOptions {
69    /// Only sync these origins.
70    pub origins: Vec<sos_core::Origin>,
71    /// Resolver for hard conflicts.
72    pub hard_conflict_resolver: HardConflictResolver,
73}
74
75/// Trait for encoding and decoding protobuf generated types.
76///
77/// A blanket implementation adds this to any [prost::Message]
78/// and runs the encoding and decoding using `spawn_blocking`.
79#[doc(hidden)]
80pub trait ProtoMessage {
81    /// Encode this message.
82    #[allow(async_fn_in_trait)]
83    async fn encode_proto(self) -> Result<Vec<u8>>;
84
85    /// Decode a message.
86    #[allow(async_fn_in_trait)]
87    async fn decode_proto<B>(buffer: B) -> Result<Self>
88    where
89        B: Buf + Send + 'static,
90        Self: Sized;
91}
92
93impl<T> ProtoMessage for T
94where
95    T: Message + Default + 'static,
96{
97    async fn encode_proto(self) -> Result<Vec<u8>> {
98        tokio::task::spawn_blocking(move || {
99            let mut buf = Vec::new();
100            buf.reserve(self.encoded_len());
101            self.encode(&mut buf)?;
102            Ok(buf)
103        })
104        .await?
105    }
106
107    async fn decode_proto<B>(buffer: B) -> Result<Self>
108    where
109        B: Buf + Send + 'static,
110        Self: Sized,
111    {
112        tokio::task::spawn_blocking(move || Ok(Self::decode(buffer)?)).await?
113    }
114}
115
116/// Marker trait to indicate a binding type that
117/// converts to a protobuf type.
118trait ProtoBinding {
119    type Inner: Message + Default;
120}
121
122/// Trait for wire protocol encoding and decoding.
123#[doc(hidden)]
124pub trait WireEncodeDecode {
125    /// Encode this request.
126    #[allow(async_fn_in_trait)]
127    async fn encode(self) -> Result<Vec<u8>>;
128
129    /// Decode this request.
130    #[allow(async_fn_in_trait)]
131    async fn decode<B>(buffer: B) -> Result<Self>
132    where
133        B: Buf + Send + 'static,
134        Self: Sized;
135}
136
137impl<T> WireEncodeDecode for T
138where
139    T: ProtoBinding + Send + 'static,
140    <T as ProtoBinding>::Inner: From<T> + 'static,
141    T: TryFrom<<T as ProtoBinding>::Inner, Error = Error>,
142{
143    #[cfg(not(target_arch = "wasm32"))]
144    async fn encode(self) -> Result<Vec<u8>> {
145        tokio::task::spawn_blocking(move || {
146            let value: <Self as ProtoBinding>::Inner = self.into();
147            let mut buf = Vec::new();
148            buf.reserve(value.encoded_len());
149            value.encode(&mut buf)?;
150            Ok(buf)
151        })
152        .await?
153    }
154
155    #[cfg(not(target_arch = "wasm32"))]
156    async fn decode<B>(buffer: B) -> Result<Self>
157    where
158        B: Buf + Send + 'static,
159        Self: Sized,
160    {
161        tokio::task::spawn_blocking(move || {
162            let result = <<Self as ProtoBinding>::Inner>::decode(buffer)?;
163            Ok(result.try_into()?)
164        })
165        .await?
166    }
167
168    #[cfg(target_arch = "wasm32")]
169    async fn encode(self) -> Result<Vec<u8>> {
170        let value: <Self as ProtoBinding>::Inner = self.into();
171        let mut buf = Vec::new();
172        buf.reserve(value.encoded_len());
173        value.encode(&mut buf)?;
174        Ok(buf)
175    }
176
177    #[cfg(target_arch = "wasm32")]
178    async fn decode<B>(buffer: B) -> Result<Self>
179    where
180        B: Buf + Send + 'static,
181        Self: Sized,
182    {
183        let result = <<Self as ProtoBinding>::Inner>::decode(buffer)?;
184        Ok(result.try_into()?)
185    }
186}
187
188fn decode_uuid(id: &[u8]) -> Result<uuid::Uuid> {
189    let id: [u8; 16] = id.try_into()?;
190    Ok(uuid::Uuid::from_bytes(id))
191}
192
193fn encode_uuid(id: &uuid::Uuid) -> Vec<u8> {
194    id.as_bytes().to_vec()
195}
196
197/// Determine if the offline environment variable is set.
198pub fn is_offline() -> bool {
199    use sos_core::constants::SOS_OFFLINE;
200    std::env::var(SOS_OFFLINE).ok().is_some()
201}