sos_protocol/
transfer.rs

1//! Types for file transfers.
2
3/// Information about a cancellation.
4#[derive(Default, Debug, Clone, Hash, Eq, PartialEq)]
5pub enum CancelReason {
6    /// Unknown reason.
7    #[default]
8    Unknown,
9    /// Event loop is being shutdown.
10    Shutdown,
11    /// Websocket connection was closed.
12    Closed,
13    /// Cancellation was from a user interaction.
14    UserCanceled,
15    /// Aborted due to conflict with a subsequent operation.
16    ///
17    /// For example, a move or delete transfer operation must abort
18    /// any existing upload or download.
19    Aborted,
20}
21
22#[cfg(feature = "files")]
23mod files {
24    //! Manage pending file transfer operations.
25    use sos_core::events::FileEvent;
26
27    use sos_core::ExternalFile;
28    use sos_external_files::FileMutationEvent;
29
30    use crate::transfer::CancelReason;
31    use async_trait::async_trait;
32    use http::StatusCode;
33    use indexmap::IndexSet;
34    use std::path::Path;
35    use tokio::sync::watch;
36
37    /// Channel for upload and download progress notifications.
38    pub type ProgressChannel = tokio::sync::mpsc::Sender<(u64, Option<u64>)>;
39
40    /// Request to queue a file transfer.
41    pub type FileTransferQueueRequest = Vec<FileOperation>;
42
43    /// Sender to queue a file transfer.
44    pub type FileTransferQueueSender =
45        tokio::sync::broadcast::Sender<FileTransferQueueRequest>;
46
47    /// Set of files built from the state on disc.
48    #[derive(Debug, Default, Clone, PartialEq, Eq)]
49    pub struct FileSet(pub IndexSet<ExternalFile>);
50
51    /// Sets of files that should be uploaded and
52    /// downloaded from a remote server.
53    #[derive(Debug, Default, Clone, PartialEq, Eq)]
54    pub struct FileTransfersSet {
55        /// Files that exist on local but not on remote.
56        pub uploads: FileSet,
57        /// Files that exist on remote but not on local.
58        pub downloads: FileSet,
59    }
60
61    /// Operations for file transfers.
62    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
63    pub enum TransferOperation {
64        /// Upload a file.
65        Upload,
66        /// Download a file.
67        Download,
68        /// Delete a file.
69        Delete,
70        /// Move a file.
71        Move(ExternalFile),
72    }
73
74    /// File and transfer information.
75    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
76    pub struct FileOperation(pub ExternalFile, pub TransferOperation);
77
78    impl From<&FileMutationEvent> for FileOperation {
79        fn from(value: &FileMutationEvent) -> Self {
80            match value {
81                FileMutationEvent::Create { event, .. } => event.into(),
82                FileMutationEvent::Move(event) => event.into(),
83                FileMutationEvent::Delete(event) => event.into(),
84            }
85        }
86    }
87
88    impl From<&FileEvent> for FileOperation {
89        fn from(value: &FileEvent) -> Self {
90            match value {
91                FileEvent::CreateFile(owner, file_name) => FileOperation(
92                    ExternalFile::new(*owner, *file_name),
93                    TransferOperation::Upload,
94                ),
95                FileEvent::DeleteFile(owner, file_name) => FileOperation(
96                    ExternalFile::new(*owner, *file_name),
97                    TransferOperation::Delete,
98                ),
99                FileEvent::MoveFile { name, from, dest } => FileOperation(
100                    ExternalFile::new(*from, *name),
101                    TransferOperation::Move(ExternalFile::new(*dest, *name)),
102                ),
103                _ => panic!("attempt to convert noop file event"),
104            }
105        }
106    }
107
108    /// Client that can synchronize files.
109    #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
110    #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
111    pub trait FileSyncClient {
112        /// Error type for file sync client.
113        type Error: std::error::Error + std::fmt::Debug;
114
115        /// Send a file.
116        async fn upload_file(
117            &self,
118            file_info: &ExternalFile,
119            path: &Path,
120            progress: ProgressChannel,
121            cancel: watch::Receiver<CancelReason>,
122        ) -> Result<StatusCode, Self::Error>;
123
124        /// Receive a file.
125        async fn download_file(
126            &self,
127            file_info: &ExternalFile,
128            path: &Path,
129            progress: ProgressChannel,
130            cancel: watch::Receiver<CancelReason>,
131        ) -> Result<StatusCode, Self::Error>;
132
133        /// Delete a file on the remote server.
134        async fn delete_file(
135            &self,
136            file_info: &ExternalFile,
137        ) -> Result<StatusCode, Self::Error>;
138
139        /// Move a file on the remote server.
140        async fn move_file(
141            &self,
142            from: &ExternalFile,
143            to: &ExternalFile,
144        ) -> Result<StatusCode, Self::Error>;
145
146        /// Compare local files with a remote server.
147        ///
148        /// Used to build a transfer queue that will eventually ensure
149        /// external files are in sync.
150        ///
151        /// Comparing sets of files is expensive as both local and remote
152        /// need to read the external files state from disc so only use this
153        /// when necessary.
154        async fn compare_files(
155            &self,
156            local_files: FileSet,
157        ) -> Result<FileTransfersSet, Self::Error>;
158    }
159}
160
161#[cfg(feature = "files")]
162pub use files::*;