include!(concat!(env!("OUT_DIR"), "/sync.rs"));
use crate::{decode_uuid, encode_uuid, Error, ProtoBinding, Result};
use indexmap::{IndexMap, IndexSet};
use sos_core::{
commit::Comparison,
events::{
patch::{Diff, Patch},
EventRecord,
},
Origin,
};
use sos_sync::{
CreateSet, MaybeDiff, MergeOutcome, SyncCompare, SyncDiff, SyncPacket,
SyncStatus, TrackedAccountChange, TrackedChanges, TrackedDeviceChange,
TrackedFolderChange, UpdateSet,
};
use std::collections::HashMap;
impl ProtoBinding for Origin {
type Inner = WireOrigin;
}
impl TryFrom<WireOrigin> for Origin {
type Error = Error;
fn try_from(value: WireOrigin) -> Result<Self> {
Ok(Self::new(value.name, value.url.parse()?))
}
}
impl From<Origin> for WireOrigin {
fn from(value: Origin) -> Self {
Self {
name: value.name().to_string(),
url: value.url().to_string(),
}
}
}
impl ProtoBinding for SyncStatus {
type Inner = WireSyncStatus;
}
impl TryFrom<WireSyncStatus> for SyncStatus {
type Error = Error;
fn try_from(value: WireSyncStatus) -> Result<Self> {
#[cfg(feature = "files")]
let files = if let Some(files) = value.files {
Some(files.try_into()?)
} else {
None
};
let mut folders = IndexMap::with_capacity(value.folders.len());
for folder in value.folders {
folders.insert(
decode_uuid(&folder.folder_id)?,
folder.state.unwrap().try_into()?,
);
}
Ok(Self {
root: value.root.unwrap().try_into()?,
identity: value.identity.unwrap().try_into()?,
account: value.account.unwrap().try_into()?,
device: value.device.unwrap().try_into()?,
#[cfg(feature = "files")]
files,
folders,
})
}
}
impl From<SyncStatus> for WireSyncStatus {
fn from(value: SyncStatus) -> Self {
Self {
root: Some(value.root.into()),
identity: Some(value.identity.into()),
account: Some(value.account.into()),
device: Some(value.device.into()),
#[cfg(feature = "files")]
files: value.files.map(|s| s.into()),
#[cfg(not(feature = "files"))]
files: None,
folders: value
.folders
.into_iter()
.map(|(k, v)| WireSyncFolderState {
folder_id: encode_uuid(&k),
state: Some(v.into()),
})
.collect(),
}
}
}
impl ProtoBinding for Comparison {
type Inner = WireComparison;
}
impl TryFrom<WireComparison> for Comparison {
type Error = Error;
fn try_from(value: WireComparison) -> Result<Self> {
let inner = value.inner.unwrap();
Ok(match inner {
wire_comparison::Inner::Equal(_) => Self::Equal,
wire_comparison::Inner::Contains(value) => Self::Contains(
value.indices.into_iter().map(|i| i as usize).collect(),
),
wire_comparison::Inner::Unknown(_) => Self::Unknown,
})
}
}
impl From<Comparison> for WireComparison {
fn from(value: Comparison) -> Self {
match value {
Comparison::Equal => WireComparison {
inner: Some(wire_comparison::Inner::Equal(true)),
},
Comparison::Contains(indices) => WireComparison {
inner: Some(wire_comparison::Inner::Contains(Contains {
indices: indices.into_iter().map(|i| i as u64).collect(),
})),
},
Comparison::Unknown => WireComparison {
inner: Some(wire_comparison::Inner::Unknown(true)),
},
}
}
}
impl<T> ProtoBinding for Patch<T> {
type Inner = WirePatch;
}
impl<T> TryFrom<WirePatch> for Patch<T> {
type Error = Error;
fn try_from(value: WirePatch) -> Result<Self> {
let mut records = Vec::with_capacity(value.records.len());
for record in value.records {
records.push(record.try_into()?);
}
Ok(Self::new(records))
}
}
impl<T> From<Patch<T>> for WirePatch {
fn from(value: Patch<T>) -> Self {
let records: Vec<EventRecord> = value.into();
Self {
records: records.into_iter().map(|r| r.into()).collect(),
}
}
}
impl<T> ProtoBinding for Diff<T> {
type Inner = WireDiff;
}
impl<T> TryFrom<WireDiff> for Diff<T> {
type Error = Error;
fn try_from(value: WireDiff) -> Result<Self> {
let last_commit = if let Some(last_commit) = value.last_commit {
Some(last_commit.try_into()?)
} else {
None
};
Ok(Self {
last_commit,
patch: value.patch.unwrap().try_into()?,
checkpoint: value.checkpoint.unwrap().try_into()?,
})
}
}
impl<T> From<Diff<T>> for WireDiff {
fn from(value: Diff<T>) -> Self {
Self {
last_commit: value.last_commit.map(|c| c.into()),
patch: Some(value.patch.into()),
checkpoint: Some(value.checkpoint.into()),
}
}
}
impl<T> ProtoBinding for MaybeDiff<T> {
type Inner = WireMaybeDiff;
}
impl<T> TryFrom<WireMaybeDiff> for MaybeDiff<T>
where
T: TryFrom<WireDiff, Error = Error>,
{
type Error = Error;
fn try_from(value: WireMaybeDiff) -> Result<Self> {
let inner = value.inner.unwrap();
match inner {
wire_maybe_diff::Inner::Diff(value) => {
Ok(Self::Diff(value.diff.unwrap().try_into()?))
}
wire_maybe_diff::Inner::Compare(value) => {
let compare = if let Some(compare) = value.compare {
Some(compare.try_into()?)
} else {
None
};
Ok(Self::Compare(compare))
}
}
}
}
impl<T> From<MaybeDiff<T>> for WireMaybeDiff
where
T: Into<WireDiff>,
{
fn from(value: MaybeDiff<T>) -> Self {
match value {
MaybeDiff::<T>::Diff(diff) => WireMaybeDiff {
inner: Some(wire_maybe_diff::Inner::Diff(
WireMaybeDiffHasDiff {
diff: Some(diff.into()),
},
)),
},
MaybeDiff::<T>::Compare(compare) => WireMaybeDiff {
inner: Some(wire_maybe_diff::Inner::Compare(
WireMaybeDiffNeedsCompare {
compare: compare.map(|c| c.into()),
},
)),
},
}
}
}
impl ProtoBinding for CreateSet {
type Inner = WireCreateSet;
}
impl TryFrom<WireCreateSet> for CreateSet {
type Error = Error;
fn try_from(value: WireCreateSet) -> Result<Self> {
let mut folders = HashMap::with_capacity(value.folders.len());
for folder in value.folders {
folders.insert(
decode_uuid(&folder.folder_id)?,
folder.patch.unwrap().try_into()?,
);
}
Ok(Self {
identity: value.identity.unwrap().try_into()?,
account: value.account.unwrap().try_into()?,
device: value.device.unwrap().try_into()?,
#[cfg(feature = "files")]
files: value.files.unwrap().try_into()?,
folders,
})
}
}
impl From<CreateSet> for WireCreateSet {
fn from(value: CreateSet) -> Self {
Self {
identity: Some(value.identity.into()),
account: Some(value.account.into()),
device: Some(value.device.into()),
#[cfg(feature = "files")]
files: Some(value.files.into()),
#[cfg(not(feature = "files"))]
files: None,
folders: value
.folders
.into_iter()
.map(|(k, v)| WireSyncFolderPatch {
folder_id: encode_uuid(&k),
patch: Some(v.into()),
})
.collect(),
}
}
}
impl ProtoBinding for UpdateSet {
type Inner = WireUpdateSet;
}
impl TryFrom<WireUpdateSet> for UpdateSet {
type Error = Error;
fn try_from(value: WireUpdateSet) -> Result<Self> {
let identity = if let Some(identity) = value.identity {
Some(identity.try_into()?)
} else {
None
};
let account = if let Some(account) = value.account {
Some(account.try_into()?)
} else {
None
};
let device = if let Some(device) = value.device {
Some(device.try_into()?)
} else {
None
};
#[cfg(feature = "files")]
let files = if let Some(files) = value.files {
Some(files.try_into()?)
} else {
None
};
let mut folders = HashMap::with_capacity(value.folders.len());
for folder in value.folders {
folders.insert(
decode_uuid(&folder.folder_id)?,
folder.diff.unwrap().try_into()?,
);
}
Ok(Self {
identity,
account,
device,
#[cfg(feature = "files")]
files,
folders,
})
}
}
impl From<UpdateSet> for WireUpdateSet {
fn from(value: UpdateSet) -> Self {
Self {
identity: value.identity.map(|d| d.into()),
account: value.account.map(|d| d.into()),
device: value.device.map(|d| d.into()),
#[cfg(feature = "files")]
files: value.files.map(|d| d.into()),
#[cfg(not(feature = "files"))]
files: None,
folders: value
.folders
.into_iter()
.map(|(k, v)| WireSyncFolderDiff {
folder_id: encode_uuid(&k),
diff: Some(v.into()),
})
.collect(),
}
}
}
impl ProtoBinding for SyncDiff {
type Inner = WireSyncDiff;
}
impl TryFrom<WireSyncDiff> for SyncDiff {
type Error = Error;
fn try_from(value: WireSyncDiff) -> Result<Self> {
let identity = if let Some(identity) = value.identity {
Some(identity.try_into()?)
} else {
None
};
let account = if let Some(account) = value.account {
Some(account.try_into()?)
} else {
None
};
let device = if let Some(device) = value.device {
Some(device.try_into()?)
} else {
None
};
#[cfg(feature = "files")]
let files = if let Some(files) = value.files {
Some(files.try_into()?)
} else {
None
};
let mut folders = IndexMap::with_capacity(value.folders.len());
for folder in value.folders {
folders.insert(
decode_uuid(&folder.folder_id)?,
folder.maybe_diff.unwrap().try_into()?,
);
}
Ok(Self {
identity,
account,
device,
#[cfg(feature = "files")]
files,
folders,
})
}
}
impl From<SyncDiff> for WireSyncDiff {
fn from(value: SyncDiff) -> Self {
Self {
identity: value.identity.map(|d| d.into()),
account: value.account.map(|d| d.into()),
device: value.device.map(|d| d.into()),
#[cfg(feature = "files")]
files: value.files.map(|d| d.into()),
#[cfg(not(feature = "files"))]
files: None,
folders: value
.folders
.into_iter()
.map(|(k, v)| WireSyncFolderMaybeDiff {
folder_id: encode_uuid(&k),
maybe_diff: Some(v.into()),
})
.collect(),
}
}
}
impl ProtoBinding for SyncCompare {
type Inner = WireSyncCompare;
}
impl TryFrom<WireSyncCompare> for SyncCompare {
type Error = Error;
fn try_from(value: WireSyncCompare) -> Result<Self> {
let identity = if let Some(identity) = value.identity {
Some(identity.try_into()?)
} else {
None
};
let account = if let Some(account) = value.account {
Some(account.try_into()?)
} else {
None
};
let device = if let Some(device) = value.device {
Some(device.try_into()?)
} else {
None
};
#[cfg(feature = "files")]
let files = if let Some(files) = value.files {
Some(files.try_into()?)
} else {
None
};
let mut folders = IndexMap::with_capacity(value.folders.len());
for folder in value.folders {
folders.insert(
decode_uuid(&folder.folder_id)?,
folder.compare.unwrap().try_into()?,
);
}
Ok(Self {
identity,
account,
device,
#[cfg(feature = "files")]
files,
folders,
})
}
}
impl From<SyncCompare> for WireSyncCompare {
fn from(value: SyncCompare) -> Self {
Self {
identity: value.identity.map(|d| d.into()),
account: value.account.map(|d| d.into()),
device: value.device.map(|d| d.into()),
#[cfg(feature = "files")]
files: value.files.map(|d| d.into()),
#[cfg(not(feature = "files"))]
files: None,
folders: value
.folders
.into_iter()
.map(|(k, v)| WireSyncFolderComparison {
folder_id: encode_uuid(&k),
compare: Some(v.into()),
})
.collect(),
}
}
}
impl ProtoBinding for SyncPacket {
type Inner = WireSyncPacket;
}
impl TryFrom<WireSyncPacket> for SyncPacket {
type Error = Error;
fn try_from(value: WireSyncPacket) -> Result<Self> {
let compare = if let Some(compare) = value.compare {
Some(compare.try_into()?)
} else {
None
};
Ok(Self {
status: value.status.unwrap().try_into()?,
diff: value.diff.unwrap().try_into()?,
compare,
})
}
}
impl From<SyncPacket> for WireSyncPacket {
fn from(value: SyncPacket) -> Self {
Self {
status: Some(value.status.into()),
diff: Some(value.diff.into()),
compare: value.compare.map(|c| c.into()),
}
}
}
impl ProtoBinding for MergeOutcome {
type Inner = WireMergeOutcome;
}
impl TryFrom<WireMergeOutcome> for MergeOutcome {
type Error = Error;
fn try_from(value: WireMergeOutcome) -> Result<Self> {
Ok(Self {
changes: value.changes,
tracked: value.tracked.unwrap().try_into()?,
#[cfg(feature = "files")]
external_files: IndexSet::new(),
})
}
}
impl From<MergeOutcome> for WireMergeOutcome {
fn from(value: MergeOutcome) -> Self {
Self {
changes: value.changes,
tracked: Some(value.tracked.into()),
}
}
}
impl ProtoBinding for TrackedChanges {
type Inner = WireTrackedChanges;
}
impl TryFrom<WireTrackedChanges> for TrackedChanges {
type Error = Error;
fn try_from(value: WireTrackedChanges) -> Result<Self> {
let mut identity = IndexSet::with_capacity(value.identity.len());
for change in value.identity {
identity.insert(change.try_into()?);
}
let mut account = IndexSet::with_capacity(value.account.len());
for change in value.account {
account.insert(change.try_into()?);
}
let mut device = IndexSet::with_capacity(value.device.len());
for change in value.device {
device.insert(change.try_into()?);
}
#[cfg(feature = "files")]
let files = {
let mut files = IndexSet::with_capacity(value.files.len());
for change in value.files {
files.insert(change.try_into()?);
}
files
};
let mut folders = HashMap::with_capacity(value.folders.len());
for folder in value.folders {
let mut changes = IndexSet::with_capacity(folder.changes.len());
for change in folder.changes {
changes.insert(change.try_into()?);
}
folders.insert(decode_uuid(&folder.folder_id)?, changes);
}
Ok(Self {
identity,
account,
device,
#[cfg(feature = "files")]
files,
folders,
})
}
}
impl From<TrackedChanges> for WireTrackedChanges {
fn from(value: TrackedChanges) -> Self {
Self {
identity: value.identity.into_iter().map(|c| c.into()).collect(),
account: value.account.into_iter().map(|c| c.into()).collect(),
device: value.device.into_iter().map(|c| c.into()).collect(),
#[cfg(feature = "files")]
files: value.files.into_iter().map(|c| c.into()).collect(),
#[cfg(not(feature = "files"))]
files: Default::default(),
folders: value
.folders
.into_iter()
.map(|(k, v)| WireTrackedUserFolderChange {
folder_id: encode_uuid(&k),
changes: v.into_iter().map(|c| c.into()).collect(),
})
.collect(),
}
}
}
impl ProtoBinding for TrackedAccountChange {
type Inner = WireTrackedAccountChange;
}
impl TryFrom<WireTrackedAccountChange> for TrackedAccountChange {
type Error = Error;
fn try_from(value: WireTrackedAccountChange) -> Result<Self> {
Ok(match value.inner.unwrap() {
wire_tracked_account_change::Inner::FolderCreated(inner) => {
TrackedAccountChange::FolderCreated(decode_uuid(
&inner.folder_id,
)?)
}
wire_tracked_account_change::Inner::FolderUpdated(inner) => {
TrackedAccountChange::FolderUpdated(decode_uuid(
&inner.folder_id,
)?)
}
wire_tracked_account_change::Inner::FolderDeleted(inner) => {
TrackedAccountChange::FolderDeleted(decode_uuid(
&inner.folder_id,
)?)
}
})
}
}
impl From<TrackedAccountChange> for WireTrackedAccountChange {
fn from(value: TrackedAccountChange) -> Self {
match value {
TrackedAccountChange::FolderCreated(folder_id) => {
WireTrackedAccountChange {
inner: Some(
wire_tracked_account_change::Inner::FolderCreated(
WireTrackedAccountFolderCreated {
folder_id: encode_uuid(&folder_id),
},
),
),
}
}
TrackedAccountChange::FolderUpdated(folder_id) => {
WireTrackedAccountChange {
inner: Some(
wire_tracked_account_change::Inner::FolderUpdated(
WireTrackedAccountFolderUpdated {
folder_id: encode_uuid(&folder_id),
},
),
),
}
}
TrackedAccountChange::FolderDeleted(folder_id) => {
WireTrackedAccountChange {
inner: Some(
wire_tracked_account_change::Inner::FolderDeleted(
WireTrackedAccountFolderDeleted {
folder_id: encode_uuid(&folder_id),
},
),
),
}
}
}
}
}
impl ProtoBinding for TrackedDeviceChange {
type Inner = WireTrackedDeviceChange;
}
impl TryFrom<WireTrackedDeviceChange> for TrackedDeviceChange {
type Error = Error;
fn try_from(value: WireTrackedDeviceChange) -> Result<Self> {
Ok(match value.inner.unwrap() {
wire_tracked_device_change::Inner::Trusted(inner) => {
TrackedDeviceChange::Trusted(
inner.device_public_key.as_slice().try_into()?,
)
}
wire_tracked_device_change::Inner::Revoked(inner) => {
TrackedDeviceChange::Revoked(
inner.device_public_key.as_slice().try_into()?,
)
}
})
}
}
impl From<TrackedDeviceChange> for WireTrackedDeviceChange {
fn from(value: TrackedDeviceChange) -> Self {
match value {
TrackedDeviceChange::Trusted(device_public_key) => {
WireTrackedDeviceChange {
inner: Some(wire_tracked_device_change::Inner::Trusted(
WireTrackedDeviceChangeTrusted {
device_public_key: device_public_key
.as_ref()
.to_vec(),
},
)),
}
}
TrackedDeviceChange::Revoked(device_public_key) => {
WireTrackedDeviceChange {
inner: Some(wire_tracked_device_change::Inner::Revoked(
WireTrackedDeviceChangeRevoked {
device_public_key: device_public_key
.as_ref()
.to_vec(),
},
)),
}
}
}
}
}
#[cfg(feature = "files")]
mod files {
use super::{
wire_tracked_file_change, WireTrackedFileChange,
WireTrackedFileDeleted, WireTrackedFileMoved,
};
use crate::{
bindings::sync::WireTrackedFileCreated, Error, ProtoBinding, Result,
};
use sos_sync::TrackedFileChange;
impl ProtoBinding for TrackedFileChange {
type Inner = WireTrackedFileChange;
}
impl TryFrom<WireTrackedFileChange> for TrackedFileChange {
type Error = Error;
fn try_from(value: WireTrackedFileChange) -> Result<Self> {
Ok(match value.inner.unwrap() {
wire_tracked_file_change::Inner::Created(inner) => {
TrackedFileChange::Created(
inner.owner.unwrap().try_into()?,
inner.file_name.as_slice().try_into()?,
)
}
wire_tracked_file_change::Inner::Moved(inner) => {
TrackedFileChange::Moved {
name: inner.name.as_slice().try_into()?,
from: inner.from.unwrap().try_into()?,
dest: inner.dest.unwrap().try_into()?,
}
}
wire_tracked_file_change::Inner::Deleted(inner) => {
TrackedFileChange::Deleted(
inner.owner.unwrap().try_into()?,
inner.file_name.as_slice().try_into()?,
)
}
})
}
}
impl From<TrackedFileChange> for WireTrackedFileChange {
fn from(value: TrackedFileChange) -> Self {
match value {
TrackedFileChange::Created(owner, file_name) => {
WireTrackedFileChange {
inner: Some(
wire_tracked_file_change::Inner::Created(
WireTrackedFileCreated {
owner: Some(owner.into()),
file_name: file_name.as_ref().to_vec(),
},
),
),
}
}
TrackedFileChange::Moved { name, from, dest } => {
WireTrackedFileChange {
inner: Some(wire_tracked_file_change::Inner::Moved(
WireTrackedFileMoved {
name: name.as_ref().to_vec(),
from: Some(from.into()),
dest: Some(dest.into()),
},
)),
}
}
TrackedFileChange::Deleted(owner, file_name) => {
WireTrackedFileChange {
inner: Some(
wire_tracked_file_change::Inner::Deleted(
WireTrackedFileDeleted {
owner: Some(owner.into()),
file_name: file_name.as_ref().to_vec(),
},
),
),
}
}
}
}
}
}
impl ProtoBinding for TrackedFolderChange {
type Inner = WireTrackedFolderChange;
}
impl TryFrom<WireTrackedFolderChange> for TrackedFolderChange {
type Error = Error;
fn try_from(value: WireTrackedFolderChange) -> Result<Self> {
Ok(match value.inner.unwrap() {
wire_tracked_folder_change::Inner::Created(inner) => {
TrackedFolderChange::Created(decode_uuid(&inner.secret_id)?)
}
wire_tracked_folder_change::Inner::Updated(inner) => {
TrackedFolderChange::Updated(decode_uuid(&inner.secret_id)?)
}
wire_tracked_folder_change::Inner::Deleted(inner) => {
TrackedFolderChange::Deleted(decode_uuid(&inner.secret_id)?)
}
})
}
}
impl From<TrackedFolderChange> for WireTrackedFolderChange {
fn from(value: TrackedFolderChange) -> Self {
match value {
TrackedFolderChange::Created(secret_id) => {
WireTrackedFolderChange {
inner: Some(wire_tracked_folder_change::Inner::Created(
WireTrackedFolderChangeCreated {
secret_id: encode_uuid(&secret_id),
},
)),
}
}
TrackedFolderChange::Updated(secret_id) => {
WireTrackedFolderChange {
inner: Some(wire_tracked_folder_change::Inner::Updated(
WireTrackedFolderChangeUpdated {
secret_id: encode_uuid(&secret_id),
},
)),
}
}
TrackedFolderChange::Deleted(secret_id) => {
WireTrackedFolderChange {
inner: Some(wire_tracked_folder_change::Inner::Deleted(
WireTrackedFolderChangeDeleted {
secret_id: encode_uuid(&secret_id),
},
)),
}
}
}
}
}