Skip to main content

remote_fs/
client.rs

1use std::path::Path;
2
3use async_trait::async_trait;
4
5use crate::error::{Error, Result};
6use crate::ftp::FtpClient;
7use crate::sftp::SftpClient;
8use crate::smb::SmbClient;
9use crate::traits::RemoteFileSystem;
10use crate::types::{Config, FileEntry, FileMeta, FtpConfig, Protocol, SftpConfig, SmbConfig};
11
12/// Unified client that dispatches to SMB / FTP / SFTP backends.
13pub enum Client {
14    Smb(SmbClient),
15    Ftp(FtpClient),
16    Sftp(SftpClient),
17}
18
19impl Client {
20    /// Create a client from a generic config and connect.
21    pub async fn connect_with(config: &Config) -> Result<Self> {
22        let mut client = match config {
23            Config::Smb(cfg) => Client::Smb(SmbClient::new(cfg.clone())),
24            Config::Ftp(cfg) => Client::Ftp(FtpClient::new(cfg.clone())),
25            Config::Sftp(cfg) => Client::Sftp(SftpClient::new(cfg.clone())),
26        };
27        client.connect().await?;
28        Ok(client)
29    }
30
31    /// Create an SMB client and connect.
32    pub async fn smb(config: &SmbConfig) -> Result<Self> {
33        Self::connect_with(&Config::Smb(config.clone())).await
34    }
35
36    /// Create an FTP client and connect.
37    pub async fn ftp(config: &FtpConfig) -> Result<Self> {
38        Self::connect_with(&Config::Ftp(config.clone())).await
39    }
40
41    /// Create an SFTP client and connect.
42    pub async fn sftp(config: &SftpConfig) -> Result<Self> {
43        Self::connect_with(&Config::Sftp(config.clone())).await
44    }
45
46    /// Active protocol of this client.
47    pub fn protocol(&self) -> Protocol {
48        match self {
49            Client::Smb(_) => Protocol::Smb,
50            Client::Ftp(_) => Protocol::Ftp,
51            Client::Sftp(_) => Protocol::Sftp,
52        }
53    }
54}
55
56#[async_trait]
57impl RemoteFileSystem for Client {
58    async fn connect(&mut self) -> Result<()> {
59        match self {
60            Client::Smb(c) => c.connect().await,
61            Client::Ftp(c) => c.connect().await,
62            Client::Sftp(c) => c.connect().await,
63        }
64    }
65
66    async fn disconnect(&mut self) -> Result<()> {
67        match self {
68            Client::Smb(c) => c.disconnect().await,
69            Client::Ftp(c) => c.disconnect().await,
70            Client::Sftp(c) => c.disconnect().await,
71        }
72    }
73
74    fn is_connected(&self) -> bool {
75        match self {
76            Client::Smb(c) => c.is_connected(),
77            Client::Ftp(c) => c.is_connected(),
78            Client::Sftp(c) => c.is_connected(),
79        }
80    }
81
82    async fn list(&mut self, path: &str) -> Result<Vec<FileEntry>> {
83        match self {
84            Client::Smb(c) => c.list(path).await,
85            Client::Ftp(c) => c.list(path).await,
86            Client::Sftp(c) => c.list(path).await,
87        }
88    }
89
90    async fn metadata(&mut self, path: &str) -> Result<FileMeta> {
91        match self {
92            Client::Smb(c) => c.metadata(path).await,
93            Client::Ftp(c) => c.metadata(path).await,
94            Client::Sftp(c) => c.metadata(path).await,
95        }
96    }
97
98    async fn exists(&mut self, path: &str) -> Result<bool> {
99        match self {
100            Client::Smb(c) => c.exists(path).await,
101            Client::Ftp(c) => c.exists(path).await,
102            Client::Sftp(c) => c.exists(path).await,
103        }
104    }
105
106    async fn mkdir(&mut self, path: &str) -> Result<()> {
107        match self {
108            Client::Smb(c) => c.mkdir(path).await,
109            Client::Ftp(c) => c.mkdir(path).await,
110            Client::Sftp(c) => c.mkdir(path).await,
111        }
112    }
113
114    async fn mkdir_all(&mut self, path: &str) -> Result<()> {
115        match self {
116            Client::Smb(c) => c.mkdir_all(path).await,
117            Client::Ftp(c) => c.mkdir_all(path).await,
118            Client::Sftp(c) => c.mkdir_all(path).await,
119        }
120    }
121
122    async fn rmdir(&mut self, path: &str) -> Result<()> {
123        match self {
124            Client::Smb(c) => c.rmdir(path).await,
125            Client::Ftp(c) => c.rmdir(path).await,
126            Client::Sftp(c) => c.rmdir(path).await,
127        }
128    }
129
130    async fn remove_file(&mut self, path: &str) -> Result<()> {
131        match self {
132            Client::Smb(c) => c.remove_file(path).await,
133            Client::Ftp(c) => c.remove_file(path).await,
134            Client::Sftp(c) => c.remove_file(path).await,
135        }
136    }
137
138    async fn remove(&mut self, path: &str) -> Result<()> {
139        match self {
140            Client::Smb(c) => c.remove(path).await,
141            Client::Ftp(c) => c.remove(path).await,
142            Client::Sftp(c) => c.remove(path).await,
143        }
144    }
145
146    async fn rename(&mut self, from: &str, to: &str) -> Result<()> {
147        match self {
148            Client::Smb(c) => c.rename(from, to).await,
149            Client::Ftp(c) => c.rename(from, to).await,
150            Client::Sftp(c) => c.rename(from, to).await,
151        }
152    }
153
154    async fn read(&mut self, path: &str) -> Result<Vec<u8>> {
155        match self {
156            Client::Smb(c) => c.read(path).await,
157            Client::Ftp(c) => c.read(path).await,
158            Client::Sftp(c) => c.read(path).await,
159        }
160    }
161
162    async fn write(&mut self, path: &str, data: &[u8]) -> Result<()> {
163        match self {
164            Client::Smb(c) => c.write(path, data).await,
165            Client::Ftp(c) => c.write(path, data).await,
166            Client::Sftp(c) => c.write(path, data).await,
167        }
168    }
169
170    async fn download(&mut self, remote: &str, local: &Path) -> Result<()> {
171        match self {
172            Client::Smb(c) => c.download(remote, local).await,
173            Client::Ftp(c) => c.download(remote, local).await,
174            Client::Sftp(c) => c.download(remote, local).await,
175        }
176    }
177
178    async fn upload(&mut self, local: &Path, remote: &str) -> Result<()> {
179        match self {
180            Client::Smb(c) => c.upload(local, remote).await,
181            Client::Ftp(c) => c.upload(local, remote).await,
182            Client::Sftp(c) => c.upload(local, remote).await,
183        }
184    }
185}
186
187/// Builder for constructing a [`Client`].
188#[derive(Debug, Default)]
189pub struct ClientBuilder {
190    config: Option<Config>,
191}
192
193impl ClientBuilder {
194    pub fn new() -> Self {
195        Self::default()
196    }
197
198    pub fn smb(mut self, config: SmbConfig) -> Self {
199        self.config = Some(Config::Smb(config));
200        self
201    }
202
203    pub fn ftp(mut self, config: FtpConfig) -> Self {
204        self.config = Some(Config::Ftp(config));
205        self
206    }
207
208    pub fn sftp(mut self, config: SftpConfig) -> Self {
209        self.config = Some(Config::Sftp(config));
210        self
211    }
212
213    pub fn config(mut self, config: Config) -> Self {
214        self.config = Some(config);
215        self
216    }
217
218    pub async fn build(self) -> Result<Client> {
219        let config = self
220            .config
221            .ok_or_else(|| Error::Other("missing connection config".into()))?;
222        Client::connect_with(&config).await
223    }
224}