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