use std::path::Path;
use async_trait::async_trait;
use crate::error::{Error, Result};
use crate::ftp::FtpClient;
use crate::sftp::SftpClient;
use crate::smb::SmbClient;
use crate::traits::RemoteFileSystem;
use crate::types::{Config, FileEntry, FileMeta, FtpConfig, Protocol, SftpConfig, SmbConfig};
pub enum Client {
Smb(SmbClient),
Ftp(FtpClient),
Sftp(SftpClient),
}
impl Client {
pub async fn connect_with(config: &Config) -> Result<Self> {
let mut client = match config {
Config::Smb(cfg) => Client::Smb(SmbClient::new(cfg.clone())),
Config::Ftp(cfg) => Client::Ftp(FtpClient::new(cfg.clone())),
Config::Sftp(cfg) => Client::Sftp(SftpClient::new(cfg.clone())),
};
client.connect().await?;
Ok(client)
}
pub async fn smb(config: &SmbConfig) -> Result<Self> {
Self::connect_with(&Config::Smb(config.clone())).await
}
pub async fn ftp(config: &FtpConfig) -> Result<Self> {
Self::connect_with(&Config::Ftp(config.clone())).await
}
pub async fn sftp(config: &SftpConfig) -> Result<Self> {
Self::connect_with(&Config::Sftp(config.clone())).await
}
pub fn protocol(&self) -> Protocol {
match self {
Client::Smb(_) => Protocol::Smb,
Client::Ftp(_) => Protocol::Ftp,
Client::Sftp(_) => Protocol::Sftp,
}
}
}
#[async_trait]
impl RemoteFileSystem for Client {
async fn connect(&mut self) -> Result<()> {
match self {
Client::Smb(c) => c.connect().await,
Client::Ftp(c) => c.connect().await,
Client::Sftp(c) => c.connect().await,
}
}
async fn disconnect(&mut self) -> Result<()> {
match self {
Client::Smb(c) => c.disconnect().await,
Client::Ftp(c) => c.disconnect().await,
Client::Sftp(c) => c.disconnect().await,
}
}
fn is_connected(&self) -> bool {
match self {
Client::Smb(c) => c.is_connected(),
Client::Ftp(c) => c.is_connected(),
Client::Sftp(c) => c.is_connected(),
}
}
async fn list(&mut self, path: &str) -> Result<Vec<FileEntry>> {
match self {
Client::Smb(c) => c.list(path).await,
Client::Ftp(c) => c.list(path).await,
Client::Sftp(c) => c.list(path).await,
}
}
async fn metadata(&mut self, path: &str) -> Result<FileMeta> {
match self {
Client::Smb(c) => c.metadata(path).await,
Client::Ftp(c) => c.metadata(path).await,
Client::Sftp(c) => c.metadata(path).await,
}
}
async fn exists(&mut self, path: &str) -> Result<bool> {
match self {
Client::Smb(c) => c.exists(path).await,
Client::Ftp(c) => c.exists(path).await,
Client::Sftp(c) => c.exists(path).await,
}
}
async fn mkdir(&mut self, path: &str) -> Result<()> {
match self {
Client::Smb(c) => c.mkdir(path).await,
Client::Ftp(c) => c.mkdir(path).await,
Client::Sftp(c) => c.mkdir(path).await,
}
}
async fn mkdir_all(&mut self, path: &str) -> Result<()> {
match self {
Client::Smb(c) => c.mkdir_all(path).await,
Client::Ftp(c) => c.mkdir_all(path).await,
Client::Sftp(c) => c.mkdir_all(path).await,
}
}
async fn rmdir(&mut self, path: &str) -> Result<()> {
match self {
Client::Smb(c) => c.rmdir(path).await,
Client::Ftp(c) => c.rmdir(path).await,
Client::Sftp(c) => c.rmdir(path).await,
}
}
async fn remove_file(&mut self, path: &str) -> Result<()> {
match self {
Client::Smb(c) => c.remove_file(path).await,
Client::Ftp(c) => c.remove_file(path).await,
Client::Sftp(c) => c.remove_file(path).await,
}
}
async fn remove(&mut self, path: &str) -> Result<()> {
match self {
Client::Smb(c) => c.remove(path).await,
Client::Ftp(c) => c.remove(path).await,
Client::Sftp(c) => c.remove(path).await,
}
}
async fn rename(&mut self, from: &str, to: &str) -> Result<()> {
match self {
Client::Smb(c) => c.rename(from, to).await,
Client::Ftp(c) => c.rename(from, to).await,
Client::Sftp(c) => c.rename(from, to).await,
}
}
async fn read(&mut self, path: &str) -> Result<Vec<u8>> {
match self {
Client::Smb(c) => c.read(path).await,
Client::Ftp(c) => c.read(path).await,
Client::Sftp(c) => c.read(path).await,
}
}
async fn write(&mut self, path: &str, data: &[u8]) -> Result<()> {
match self {
Client::Smb(c) => c.write(path, data).await,
Client::Ftp(c) => c.write(path, data).await,
Client::Sftp(c) => c.write(path, data).await,
}
}
async fn download(&mut self, remote: &str, local: &Path) -> Result<()> {
match self {
Client::Smb(c) => c.download(remote, local).await,
Client::Ftp(c) => c.download(remote, local).await,
Client::Sftp(c) => c.download(remote, local).await,
}
}
async fn upload(&mut self, local: &Path, remote: &str) -> Result<()> {
match self {
Client::Smb(c) => c.upload(local, remote).await,
Client::Ftp(c) => c.upload(local, remote).await,
Client::Sftp(c) => c.upload(local, remote).await,
}
}
}
#[derive(Debug, Default)]
pub struct ClientBuilder {
config: Option<Config>,
}
impl ClientBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn smb(mut self, config: SmbConfig) -> Self {
self.config = Some(Config::Smb(config));
self
}
pub fn ftp(mut self, config: FtpConfig) -> Self {
self.config = Some(Config::Ftp(config));
self
}
pub fn sftp(mut self, config: SftpConfig) -> Self {
self.config = Some(Config::Sftp(config));
self
}
pub fn config(mut self, config: Config) -> Self {
self.config = Some(config);
self
}
pub async fn build(self) -> Result<Client> {
let config = self
.config
.ok_or_else(|| Error::Other("missing connection config".into()))?;
Client::connect_with(&config).await
}
}