diff --git a/core/src/services/alluxio/backend.rs b/core/src/services/alluxio/backend.rs index 8e8bdc148366..e1ce1fc01d40 100644 --- a/core/src/services/alluxio/backend.rs +++ b/core/src/services/alluxio/backend.rs @@ -108,12 +108,9 @@ impl Builder for AlluxioBuilder { let root = normalize_root(&self.config.root.clone().unwrap_or_default()); debug!("backend use root {}", &root); - let endpoint = match &self.config.endpoint { - Some(endpoint) => Ok(endpoint.clone()), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_operation("Builder::build") - .with_context("service", Scheme::Alluxio)), - }?; + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME) + .map_err(|err| err.with_operation("Builder::build"))?; debug!("backend use endpoint {}", &endpoint); let client = if let Some(client) = self.http_client { diff --git a/core/src/services/azdls/backend.rs b/core/src/services/azdls/backend.rs index 2c5bd72feab7..3020e1ce8ab9 100644 --- a/core/src/services/azdls/backend.rs +++ b/core/src/services/azdls/backend.rs @@ -164,12 +164,9 @@ impl Builder for AzdlsBuilder { }?; debug!("backend use filesystem {}", &filesystem); - let endpoint = match &self.config.endpoint { - Some(endpoint) => Ok(endpoint.clone()), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_operation("Builder::build") - .with_context("service", Scheme::Azdls)), - }?; + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME) + .map_err(|err| err.with_operation("Builder::build"))?; debug!("backend use endpoint {}", &endpoint); let client = if let Some(client) = self.http_client { diff --git a/core/src/services/azfile/backend.rs b/core/src/services/azfile/backend.rs index 61ab64717d03..51f3cf415787 100644 --- a/core/src/services/azfile/backend.rs +++ b/core/src/services/azfile/backend.rs @@ -148,12 +148,9 @@ impl Builder for AzfileBuilder { let root = normalize_root(&self.config.root.unwrap_or_default()); debug!("backend use root {}", root); - let endpoint = match &self.config.endpoint { - Some(endpoint) => Ok(endpoint.clone()), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_operation("Builder::build") - .with_context("service", Scheme::Azfile)), - }?; + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME) + .map_err(|err| err.with_operation("Builder::build"))?; debug!("backend use endpoint {}", &endpoint); let client = if let Some(client) = self.http_client { diff --git a/core/src/services/cos/backend.rs b/core/src/services/cos/backend.rs index a3768395c665..78d47c00b49c 100644 --- a/core/src/services/cos/backend.rs +++ b/core/src/services/cos/backend.rs @@ -157,22 +157,18 @@ impl Builder for CosBuilder { let bucket = match &self.config.bucket { Some(bucket) => Ok(bucket.to_string()), - None => Err( - Error::new(ErrorKind::ConfigInvalid, "The bucket is misconfigured") - .with_context("service", Scheme::Cos), - ), + None => Err(Error::error_kind_config_invalid( + Self::SCHEME, + "The bucket is misconfigured", + )), }?; debug!("backend use bucket {}", &bucket); let uri = match &self.config.endpoint { - Some(endpoint) => endpoint.parse::().map_err(|err| { - Error::new(ErrorKind::ConfigInvalid, "endpoint is invalid") - .with_context("service", Scheme::Cos) - .with_context("endpoint", endpoint) - .set_source(err) - }), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_context("service", Scheme::Cos)), + Some(endpoint) => endpoint + .parse::() + .map_err(|err| Error::empty_endpoint_err(Self::SCHEME).set_source(err)), + None => Err(Error::empty_endpoint_err(Self::SCHEME)), }?; let scheme = match uri.scheme_str() { diff --git a/core/src/services/dbfs/backend.rs b/core/src/services/dbfs/backend.rs index bf1152daea09..616ee0c9651e 100644 --- a/core/src/services/dbfs/backend.rs +++ b/core/src/services/dbfs/backend.rs @@ -105,12 +105,9 @@ impl Builder for DbfsBuilder { let root = normalize_root(&self.config.root.unwrap_or_default()); debug!("backend use root {}", root); - let endpoint = match &self.config.endpoint { - Some(endpoint) => Ok(endpoint.clone()), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_operation("Builder::build") - .with_context("service", Scheme::Dbfs)), - }?; + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME) + .map_err(|err| err.with_operation("Builder::build"))?; debug!("backend use endpoint: {}", &endpoint); let token = match self.config.token { diff --git a/core/src/services/ftp/backend.rs b/core/src/services/ftp/backend.rs index 144ba9aff7ad..7f5c6730d9d2 100644 --- a/core/src/services/ftp/backend.rs +++ b/core/src/services/ftp/backend.rs @@ -119,11 +119,8 @@ impl Builder for FtpBuilder { fn build(self) -> Result { debug!("ftp backend build started: {:?}", &self); - let endpoint = match &self.config.endpoint { - None => return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty")), - Some(v) => v, - }; - + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME)?; let endpoint_uri = match endpoint.parse::() { Err(e) => { return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is invalid") diff --git a/core/src/services/http/backend.rs b/core/src/services/http/backend.rs index 7908c2e58fd2..6c570748d013 100644 --- a/core/src/services/http/backend.rs +++ b/core/src/services/http/backend.rs @@ -131,15 +131,8 @@ impl Builder for HttpBuilder { fn build(self) -> Result { debug!("backend build started: {:?}", &self); - - let endpoint = match &self.config.endpoint { - Some(v) => v, - None => { - return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_context("service", Scheme::Http)) - } - }; - + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME)?; let root = normalize_root(&self.config.root.unwrap_or_default()); debug!("backend use root {}", root); diff --git a/core/src/services/ipfs/backend.rs b/core/src/services/ipfs/backend.rs index 2a72efc9a917..a7268dd9818c 100644 --- a/core/src/services/ipfs/backend.rs +++ b/core/src/services/ipfs/backend.rs @@ -117,12 +117,8 @@ impl Builder for IpfsBuilder { } debug!("backend use root {}", root); - let endpoint = match &self.config.endpoint { - Some(endpoint) => Ok(endpoint.clone()), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_context("service", Scheme::Ipfs) - .with_context("root", &root)), - }?; + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME)?; debug!("backend use endpoint {}", &endpoint); let client = if let Some(client) = self.http_client { diff --git a/core/src/services/koofr/backend.rs b/core/src/services/koofr/backend.rs index 0b638df32117..987e4d6d5c34 100644 --- a/core/src/services/koofr/backend.rs +++ b/core/src/services/koofr/backend.rs @@ -140,13 +140,9 @@ impl Builder for KoofrBuilder { let root = normalize_root(&self.config.root.clone().unwrap_or_default()); debug!("backend use root {}", &root); - if self.config.endpoint.is_empty() { - return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_operation("Builder::build") - .with_context("service", Scheme::Koofr)); - } - - debug!("backend use endpoint {}", &self.config.endpoint); + let endpoint = + Error::ensure_endpoint_not_empty(Some(self.config.endpoint.as_str()), Self::SCHEME)?; + debug!("backend use endpoint {}", endpoint); if self.config.email.is_empty() { return Err(Error::new(ErrorKind::ConfigInvalid, "email is empty") @@ -177,7 +173,7 @@ impl Builder for KoofrBuilder { Ok(KoofrBackend { core: Arc::new(KoofrCore { root, - endpoint: self.config.endpoint.clone(), + endpoint, email: self.config.email.clone(), password, mount_id: OnceCell::new(), diff --git a/core/src/services/memcached/backend.rs b/core/src/services/memcached/backend.rs index c89b81ba704a..821bcf2f6ab0 100644 --- a/core/src/services/memcached/backend.rs +++ b/core/src/services/memcached/backend.rs @@ -89,10 +89,8 @@ impl Builder for MemcachedBuilder { type Config = MemcachedConfig; fn build(self) -> Result { - let endpoint = self.config.endpoint.clone().ok_or_else(|| { - Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_context("service", Scheme::Memcached) - })?; + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME)?; let uri = http::Uri::try_from(&endpoint).map_err(|err| { Error::new(ErrorKind::ConfigInvalid, "endpoint is invalid") .with_context("service", Scheme::Memcached) diff --git a/core/src/services/obs/backend.rs b/core/src/services/obs/backend.rs index b84c62e77085..54e5d579cd3a 100644 --- a/core/src/services/obs/backend.rs +++ b/core/src/services/obs/backend.rs @@ -157,12 +157,10 @@ impl Builder for ObsBuilder { let uri = match &self.config.endpoint { Some(endpoint) => endpoint.parse::().map_err(|err| { - Error::new(ErrorKind::ConfigInvalid, "endpoint is invalid") - .with_context("service", Scheme::Obs) + Error::error_kind_config_invalid(Self::SCHEME, "endpoint is invalid") .set_source(err) }), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_context("service", Scheme::Obs)), + None => Err(Error::empty_endpoint_err(Self::SCHEME)), }?; let scheme = match uri.scheme_str() { diff --git a/core/src/services/oss/backend.rs b/core/src/services/oss/backend.rs index aa81ef2e55c3..9f9c8e0696c9 100644 --- a/core/src/services/oss/backend.rs +++ b/core/src/services/oss/backend.rs @@ -154,15 +154,11 @@ impl OssBuilder { fn parse_endpoint(&self, endpoint: &Option, bucket: &str) -> Result<(String, String)> { let (endpoint, host) = match endpoint.clone() { Some(ep) => { - let uri = ep.parse::().map_err(|err| { - Error::new(ErrorKind::ConfigInvalid, "endpoint is invalid") - .with_context("service", Scheme::Oss) - .with_context("endpoint", &ep) - .set_source(err) - })?; + let uri = ep + .parse::() + .map_err(|err| Error::empty_endpoint_err(Self::SCHEME).set_source(err))?; let host = uri.host().ok_or_else(|| { - Error::new(ErrorKind::ConfigInvalid, "endpoint host is empty") - .with_context("service", Scheme::Oss) + Error::error_kind_config_invalid(Self::SCHEME, "endpoint host is empty") .with_context("endpoint", &ep) })?; let full_host = if let Some(port) = uri.port_u16() { @@ -174,11 +170,10 @@ impl OssBuilder { Some(scheme_str) => match scheme_str { "http" | "https" => format!("{scheme_str}://{full_host}"), _ => { - return Err(Error::new( - ErrorKind::ConfigInvalid, + return Err(Error::error_kind_config_invalid( + Self::SCHEME, "endpoint protocol is invalid", - ) - .with_context("service", Scheme::Oss)); + )); } }, None => format!("https://{full_host}"), @@ -186,8 +181,7 @@ impl OssBuilder { (endpoint, full_host) } None => { - return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_context("service", Scheme::Oss)); + return Err(Error::empty_endpoint_err(Self::SCHEME)); } }; Ok((endpoint, host)) diff --git a/core/src/services/pcloud/backend.rs b/core/src/services/pcloud/backend.rs index 904b81d848a7..d496612b5528 100644 --- a/core/src/services/pcloud/backend.rs +++ b/core/src/services/pcloud/backend.rs @@ -137,13 +137,9 @@ impl Builder for PcloudBuilder { debug!("backend use root {}", &root); // Handle endpoint. - if self.config.endpoint.is_empty() { - return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_operation("Builder::build") - .with_context("service", Scheme::Pcloud)); - } - - debug!("backend use endpoint {}", &self.config.endpoint); + let endpoint = + Error::ensure_endpoint_not_empty(Some(self.config.endpoint.as_str()), Self::SCHEME)?; + debug!("backend use endpoint {}", endpoint); let username = match &self.config.username { Some(username) => Ok(username.clone()), @@ -171,7 +167,7 @@ impl Builder for PcloudBuilder { Ok(PcloudBackend { core: Arc::new(PcloudCore { root, - endpoint: self.config.endpoint.clone(), + endpoint, username, password, client, diff --git a/core/src/services/seafile/backend.rs b/core/src/services/seafile/backend.rs index 29029b6b23bd..05c9a483a820 100644 --- a/core/src/services/seafile/backend.rs +++ b/core/src/services/seafile/backend.rs @@ -158,12 +158,8 @@ impl Builder for SeafileBuilder { debug!("backend use repo_name {}", &self.config.repo_name); - let endpoint = match &self.config.endpoint { - Some(endpoint) => Ok(endpoint.clone()), - None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_operation("Builder::build") - .with_context("service", Scheme::Seafile)), - }?; + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME)?; let username = match &self.config.username { Some(username) => Ok(username.clone()), diff --git a/core/src/services/sftp/backend.rs b/core/src/services/sftp/backend.rs index 0d915572abfb..f47dd385e97f 100644 --- a/core/src/services/sftp/backend.rs +++ b/core/src/services/sftp/backend.rs @@ -150,10 +150,10 @@ impl Builder for SftpBuilder { fn build(self) -> Result { debug!("sftp backend build started: {:?}", &self); - let endpoint = match self.config.endpoint.clone() { - Some(v) => v, - None => return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty")), - }; + // Handle endpoint. + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME)?; + debug!("backend use endpoint {}", endpoint); let user = self.config.user.clone(); diff --git a/core/src/services/webdav/backend.rs b/core/src/services/webdav/backend.rs index e6f3b838b3f4..e4763c257b24 100644 --- a/core/src/services/webdav/backend.rs +++ b/core/src/services/webdav/backend.rs @@ -134,15 +134,13 @@ impl Builder for WebdavBuilder { fn build(self) -> Result { debug!("backend build started: {:?}", &self); - let endpoint = match &self.config.endpoint { - Some(v) => v, - None => { - return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty") - .with_context("service", Scheme::Webdav)); - } - }; + // Handle endpoint. + let endpoint = + Error::ensure_endpoint_not_empty(self.config.endpoint.as_deref(), Self::SCHEME)?; + debug!("backend use endpoint {}", endpoint); + // Some services might return the path with suffix `/remote.php/webdav/`, we need to trim them. - let server_path = http::Uri::from_str(endpoint) + let server_path = http::Uri::from_str(&endpoint) .map_err(|err| { Error::new(ErrorKind::ConfigInvalid, "endpoint is invalid") .with_context("service", Scheme::Webdav) @@ -176,7 +174,7 @@ impl Builder for WebdavBuilder { } let core = Arc::new(WebdavCore { - endpoint: endpoint.to_string(), + endpoint, server_path, authorization, disable_copy: self.config.disable_copy, diff --git a/core/src/types/error.rs b/core/src/types/error.rs index 4d55142732c6..4cf5c3d828a1 100644 --- a/core/src/types/error.rs +++ b/core/src/types/error.rs @@ -42,6 +42,8 @@ use std::fmt::Display; use std::fmt::Formatter; use std::io; +use crate::Scheme; + /// Result that is a wrapper of `Result` pub type Result = std::result::Result; @@ -400,6 +402,32 @@ impl Error { pub fn is_temporary(&self) -> bool { self.status == ErrorStatus::Temporary } + + /** Useful helper functions for Builder::build */ + + #[inline] + pub(crate) fn error_kind_config_invalid(scheme: Scheme, message: impl Into) -> Self { + // Since all ErrorKind::ConfigInvalid occur in Builder::build, + // we always call `with_operation` and `with_context`. + Self::new(ErrorKind::ConfigInvalid, message) + .with_operation("Builder::build") + .with_context("service", scheme) + } + + #[inline] + pub(crate) fn empty_endpoint_err(ctx_schema: Scheme) -> Self { + Self::error_kind_config_invalid(ctx_schema, "endpoint is empty") + } + + #[inline] + pub(crate) fn ensure_endpoint_not_empty( + endpoint: Option<&str>, + ctx_schema: Scheme, + ) -> Result { + let endpoint = endpoint.ok_or_else(|| Self::empty_endpoint_err(ctx_schema))?; + + Ok(endpoint.to_string()) + } } impl From for io::Error {