This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch xuanwo/azure-common-crate in repository https://gitbox.apache.org/repos/asf/opendal.git
commit f601c0bd73dd2697e9a91f55cb9ae6cf932a1666 Author: Xuanwo <[email protected]> AuthorDate: Mon Dec 8 02:23:11 2025 +0800 refactor: Split all azure storage services and ghac Signed-off-by: Xuanwo <[email protected]> --- core/Cargo.lock | 83 +++++++++++++++++++++- core/Cargo.toml | 12 ++-- core/core/Cargo.toml | 22 ------ core/core/src/raw/mod.rs | 13 ---- core/core/src/services/mod.rs | 20 ------ core/core/src/types/list.rs | 41 ----------- core/services/azblob/Cargo.toml | 45 ++++++++++++ .../azblob => services/azblob/src}/backend.rs | 16 +++-- .../azblob => services/azblob/src}/config.rs | 8 +-- .../azblob => services/azblob/src}/core.rs | 4 +- .../azblob => services/azblob/src}/deleter.rs | 6 +- .../azblob => services/azblob/src}/docs.md | 0 .../azblob => services/azblob/src}/error.rs | 26 ++++--- .../azblob/mod.rs => services/azblob/src/lib.rs} | 6 +- .../azblob => services/azblob/src}/lister.rs | 4 +- .../azblob => services/azblob/src}/writer.rs | 4 +- core/services/azdls/Cargo.toml | 39 ++++++++++ .../azdls => services/azdls/src}/backend.rs | 12 ++-- .../azdls => services/azdls/src}/config.rs | 30 ++++---- .../services/azdls => services/azdls/src}/core.rs | 4 +- .../azdls => services/azdls/src}/deleter.rs | 4 +- .../services/azdls => services/azdls/src}/docs.md | 0 .../services/azdls => services/azdls/src}/error.rs | 26 ++++--- .../azdls/mod.rs => services/azdls/src/lib.rs} | 2 +- .../azdls => services/azdls/src}/lister.rs | 4 +- .../azdls => services/azdls/src}/writer.rs | 4 +- core/services/azfile/Cargo.toml | 38 ++++++++++ .../azfile => services/azfile/src}/backend.rs | 12 ++-- .../azfile => services/azfile/src}/config.rs | 30 ++++---- .../azfile => services/azfile/src}/core.rs | 4 +- .../azfile => services/azfile/src}/deleter.rs | 4 +- .../azfile => services/azfile/src}/docs.md | 0 .../azfile => services/azfile/src}/error.rs | 26 ++++--- .../azfile/mod.rs => services/azfile/src/lib.rs} | 2 +- .../azfile => services/azfile/src}/lister.rs | 4 +- .../azfile => services/azfile/src}/writer.rs | 4 +- core/services/azure-common/Cargo.toml | 32 +++++++++ .../azure.rs => services/azure-common/src/lib.rs} | 23 +++--- core/services/ghac/Cargo.toml | 41 +++++++++++ .../services/ghac => services/ghac/src}/backend.rs | 4 +- .../services/ghac => services/ghac/src}/config.rs | 8 +-- .../services/ghac => services/ghac/src}/core.rs | 4 +- .../services/ghac => services/ghac/src}/docs.md | 0 .../services/ghac => services/ghac/src}/error.rs | 4 +- .../ghac/mod.rs => services/ghac/src/lib.rs} | 2 +- .../services/ghac => services/ghac/src}/writer.rs | 33 ++++++--- core/src/lib.rs | 8 +++ 47 files changed, 463 insertions(+), 255 deletions(-) diff --git a/core/Cargo.lock b/core/Cargo.lock index 6ed89eba4..ba0554e55 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -5407,6 +5407,10 @@ dependencies = [ "log", "opendal-core", "opendal-layer-async-backtrace", + "opendal-service-azblob", + "opendal-service-azdls", + "opendal-service-azfile", + "opendal-service-ghac", "opendal-service-moka", "opendal-service-s3", "opentelemetry", @@ -5475,7 +5479,6 @@ dependencies = [ "futures", "futures-rustls", "getrandom 0.2.16", - "ghac", "governor", "hdfs-native", "hdrs", @@ -5585,6 +5588,84 @@ dependencies = [ "opendal-core", ] +[[package]] +name = "opendal-service-azblob" +version = "0.55.0" +dependencies = [ + "base64 0.22.1", + "bytes", + "ctor", + "http 1.3.1", + "log", + "opendal-core", + "opendal-service-azure-common", + "pretty_assertions", + "quick-xml", + "reqsign", + "serde", + "serde_json", + "sha2", + "uuid", +] + +[[package]] +name = "opendal-service-azdls" +version = "0.55.0" +dependencies = [ + "bytes", + "ctor", + "http 1.3.1", + "log", + "opendal-core", + "opendal-service-azure-common", + "quick-xml", + "reqsign", + "serde", + "serde_json", +] + +[[package]] +name = "opendal-service-azfile" +version = "0.55.0" +dependencies = [ + "bytes", + "ctor", + "http 1.3.1", + "log", + "opendal-core", + "opendal-service-azure-common", + "quick-xml", + "reqsign", + "serde", +] + +[[package]] +name = "opendal-service-azure-common" +version = "0.55.0" +dependencies = [ + "http 1.3.1", + "opendal-core", + "reqsign", +] + +[[package]] +name = "opendal-service-ghac" +version = "0.55.0" +dependencies = [ + "bytes", + "ctor", + "ghac", + "http 1.3.1", + "log", + "opendal-core", + "opendal-service-azblob", + "prost 0.13.5", + "reqsign", + "serde", + "serde_json", + "sha2", +] + [[package]] name = "opendal-service-moka" version = "0.55.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index 329ef377d..b40233e4d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -69,9 +69,9 @@ layers-tracing = ["opendal-core/layers-tracing"] reqwest-rustls-tls = ["opendal-core/reqwest-rustls-tls"] services-aliyun-drive = ["opendal-core/services-aliyun-drive"] services-alluxio = ["opendal-core/services-alluxio"] -services-azblob = ["opendal-core/services-azblob"] -services-azdls = ["opendal-core/services-azdls"] -services-azfile = ["opendal-core/services-azfile"] +services-azblob = ["dep:opendal-service-azblob"] +services-azdls = ["dep:opendal-service-azdls"] +services-azfile = ["dep:opendal-service-azfile"] services-b2 = ["opendal-core/services-b2"] services-cacache = ["opendal-core/services-cacache"] services-cloudflare-kv = ["opendal-core/services-cloudflare-kv"] @@ -87,7 +87,7 @@ services-fs = ["opendal-core/services-fs"] services-ftp = ["opendal-core/services-ftp"] services-gcs = ["opendal-core/services-gcs"] services-gdrive = ["opendal-core/services-gdrive"] -services-ghac = ["opendal-core/services-ghac"] +services-ghac = ["dep:opendal-service-ghac"] services-github = ["opendal-core/services-github"] services-gridfs = ["opendal-core/services-gridfs"] services-hdfs = ["opendal-core/services-hdfs"] @@ -155,6 +155,10 @@ opendal-core = { path = "core", version = "0.55.0", default-features = false } opendal-layer-async-backtrace = { path = "layers/async-backtrace", version = "0.55.0", optional = true, default-features = false } opendal-service-s3 = { path = "services/s3", version = "0.55.0", optional = true, default-features = false } opendal-service-moka = { path = "services/moka", version = "0.55.0", optional = true, default-features = false } +opendal-service-azblob = { path = "services/azblob", version = "0.55.0", optional = true, default-features = false } +opendal-service-azdls = { path = "services/azdls", version = "0.55.0", optional = true, default-features = false } +opendal-service-azfile = { path = "services/azfile", version = "0.55.0", optional = true, default-features = false } +opendal-service-ghac = { path = "services/ghac", version = "0.55.0", optional = true, default-features = false } [dev-dependencies] anyhow = { version = "1.0.100", features = ["std"] } diff --git a/core/core/Cargo.toml b/core/core/Cargo.toml index 768d7f292..a590ca022 100644 --- a/core/core/Cargo.toml +++ b/core/core/Cargo.toml @@ -55,9 +55,6 @@ tests = [ "dep:rand", "dep:sha2", "dep:dotenvy", - "services-azblob", - "services-azdls", - "services-azfile", "services-fs", "services-http", "services-memory", @@ -102,22 +99,6 @@ layers-dtrace = ["dep:probe"] services-aliyun-drive = [] services-alluxio = [] -services-azblob = [ - "dep:sha2", - "dep:reqsign", - "reqsign?/services-azblob", - "reqsign?/reqwest_request", -] -services-azdls = [ - "dep:reqsign", - "reqsign?/services-azblob", - "reqsign?/reqwest_request", -] -services-azfile = [ - "dep:reqsign", - "reqsign?/services-azblob", - "reqsign?/reqwest_request", -] services-b2 = [] services-cacache = ["dep:cacache"] services-cloudflare-kv = [] @@ -146,7 +127,6 @@ services-gcs = [ "reqsign?/reqwest_request", ] services-gdrive = ["internal-path-cache"] -services-ghac = ["dep:ghac", "dep:prost", "services-azblob"] services-github = [] services-gridfs = ["dep:mongodb", "dep:mongodb-internal-macros"] services-hdfs = ["dep:hdrs"] @@ -269,8 +249,6 @@ foundationdb = { version = "0.9.0", features = [ "embedded-fdb-include", "fdb-7_3", ], optional = true } -# for services-ghac -ghac = { version = "0.2.0", optional = true } # for services-hdfs hdrs = { version = "0.3.2", optional = true, features = ["async_file"] } # for services-upyun diff --git a/core/core/src/raw/mod.rs b/core/core/src/raw/mod.rs index 446cdb8eb..f85f4bf86 100644 --- a/core/core/src/raw/mod.rs +++ b/core/core/src/raw/mod.rs @@ -29,19 +29,6 @@ mod accessor; pub use accessor::*; -#[cfg(any( - feature = "services-azblob", - feature = "services-azdls", - feature = "services-azfile" -))] -mod azure; -#[cfg(any( - feature = "services-azblob", - feature = "services-azdls", - feature = "services-azfile" -))] -pub(crate) use azure::*; - mod layer; pub use layer::*; diff --git a/core/core/src/services/mod.rs b/core/core/src/services/mod.rs index 23db1ae87..549ff6e79 100644 --- a/core/core/src/services/mod.rs +++ b/core/core/src/services/mod.rs @@ -29,21 +29,6 @@ mod alluxio; #[cfg(feature = "services-alluxio")] pub use alluxio::*; -#[cfg(feature = "services-azblob")] -mod azblob; -#[cfg(feature = "services-azblob")] -pub use azblob::*; - -#[cfg(feature = "services-azdls")] -mod azdls; -#[cfg(feature = "services-azdls")] -pub use azdls::*; - -#[cfg(feature = "services-azfile")] -mod azfile; -#[cfg(feature = "services-azfile")] -pub use azfile::*; - #[cfg(feature = "services-b2")] mod b2; #[cfg(feature = "services-b2")] @@ -119,11 +104,6 @@ mod gdrive; #[cfg(feature = "services-gdrive")] pub use gdrive::*; -#[cfg(feature = "services-ghac")] -mod ghac; -#[cfg(feature = "services-ghac")] -pub use ghac::*; - #[cfg(feature = "services-github")] mod github; #[cfg(feature = "services-github")] diff --git a/core/core/src/types/list.rs b/core/core/src/types/list.rs index f1c0dccb5..ab48a7d50 100644 --- a/core/core/src/types/list.rs +++ b/core/core/src/types/list.rs @@ -94,44 +94,3 @@ impl Stream for Lister { Poll::Ready(None) } } - -#[cfg(test)] -#[cfg(feature = "services-azblob")] -mod tests { - use futures::StreamExt; - use futures::future; - - use super::*; - use crate::services::Azblob; - - /// Inspired by <https://gist.github.com/kyle-mccarthy/1e6ae89cc34495d731b91ebf5eb5a3d9> - /// - /// Invalid lister should not panic nor endless loop. - #[tokio::test] - async fn test_invalid_lister() -> Result<()> { - let _ = tracing_subscriber::fmt().try_init(); - - let builder = Azblob::default() - .container("container") - .account_name("account_name") - .account_key("YWNjb3VudF9rZXk=") // Valid base64 encoding of "account_key" - .endpoint("https://account_name.blob.core.windows.net"); - - let operator = Operator::new(builder)?.finish(); - - let lister = operator.lister("/").await?; - - lister - .filter_map(|entry| { - dbg!(&entry); - future::ready(entry.ok()) - }) - .for_each(|entry| { - println!("{entry:?}"); - future::ready(()) - }) - .await; - - Ok(()) - } -} diff --git a/core/services/azblob/Cargo.toml b/core/services/azblob/Cargo.toml new file mode 100644 index 000000000..ddc122b50 --- /dev/null +++ b/core/services/azblob/Cargo.toml @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[package] +name = "opendal-service-azblob" +version = "0.55.0" +edition = "2024" +license = "Apache-2.0" +repository = "https://github.com/apache/opendal" +description = "Apache OpenDAL Azure Blob Storage service implementation" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +opendal-core = { path = "../../core", version = "0.55.0", default-features = false } +opendal-service-azure-common = { path = "../azure-common", version = "0.55.0" } +base64 = "0.22" +bytes = "1.6" +ctor = "0.6" +http = "1.1" +log = "0.4" +quick-xml = { version = "0.38", features = ["serialize", "overlapped-lists"] } +reqsign = { version = "0.16.5", default-features = false, features = ["reqwest_request", "services-azblob"] } +serde = { version = "1", features = ["derive"] } +sha2 = "0.10" +uuid = { version = "1", features = ["v4", "serde"] } + +[dev-dependencies] +pretty_assertions = "1" +serde_json = "1" diff --git a/core/core/src/services/azblob/backend.rs b/core/services/azblob/src/backend.rs similarity index 97% rename from core/core/src/services/azblob/backend.rs rename to core/services/azblob/src/backend.rs index 0aedc029c..b6e507ca3 100644 --- a/core/core/src/services/azblob/backend.rs +++ b/core/services/azblob/src/backend.rs @@ -39,8 +39,11 @@ use super::error::parse_error; use super::lister::AzblobLister; use super::writer::AzblobWriter; use super::writer::AzblobWriters; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; +use opendal_service_azure_common::{ + AzureStorageService, azure_account_name_from_endpoint, azure_config_from_connection_string, +}; const AZBLOB_BATCH_LIMIT: usize = 256; @@ -278,8 +281,7 @@ impl AzblobBuilder { /// Connection strings can only configure the endpoint, account name and /// authentication information. Users still need to configure container name. pub fn from_connection_string(conn: &str) -> Result<Self> { - let config = - raw::azure_config_from_connection_string(conn, raw::AzureStorageService::Blob)?; + let config = azure_config_from_connection_string(conn, AzureStorageService::Blob)?; Ok(AzblobConfig::from(config).into_builder()) } @@ -320,7 +322,7 @@ impl Builder for AzblobBuilder { .config .account_name .clone() - .or_else(|| raw::azure_account_name_from_endpoint(endpoint.as_str())) + .or_else(|| azure_account_name_from_endpoint(endpoint.as_str())) { config_loader.account_name = Some(v); } @@ -565,6 +567,10 @@ impl Access for AzblobBackend { ErrorKind::Unsupported, "operation is not supported", )), + _ => Err(Error::new( + ErrorKind::Unsupported, + "presign operation is not supported", + )), }; let mut req = req?; diff --git a/core/core/src/services/azblob/config.rs b/core/services/azblob/src/config.rs similarity index 97% rename from core/core/src/services/azblob/config.rs rename to core/services/azblob/src/config.rs index 36345b42e..4fcfbdf81 100644 --- a/core/core/src/services/azblob/config.rs +++ b/core/services/azblob/src/config.rs @@ -88,10 +88,10 @@ impl Debug for AzblobConfig { } } -impl crate::Configurator for AzblobConfig { +impl opendal_core::Configurator for AzblobConfig { type Builder = AzblobBuilder; - fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> { + fn from_uri(uri: &opendal_core::OperatorUri) -> opendal_core::Result<Self> { let mut map = uri.options().clone(); if let Some(container) = uri.name() { @@ -118,8 +118,8 @@ impl crate::Configurator for AzblobConfig { #[cfg(test)] mod tests { use super::*; - use crate::Configurator; - use crate::types::OperatorUri; + use opendal_core::Configurator; + use opendal_core::OperatorUri; #[test] fn test_container_name_aliases() { diff --git a/core/core/src/services/azblob/core.rs b/core/services/azblob/src/core.rs similarity index 99% rename from core/core/src/services/azblob/core.rs rename to core/services/azblob/src/core.rs index b2d615ff3..792e1ee02 100644 --- a/core/core/src/services/azblob/core.rs +++ b/core/services/azblob/src/core.rs @@ -39,8 +39,8 @@ use serde::Deserialize; use serde::Serialize; use uuid::Uuid; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; pub mod constants { // Indicates the Blob Storage version that was used to execute the request diff --git a/core/core/src/services/azblob/deleter.rs b/core/services/azblob/src/deleter.rs similarity index 97% rename from core/core/src/services/azblob/deleter.rs rename to core/services/azblob/src/deleter.rs index 4ecca5a4e..a2aba9fd9 100644 --- a/core/core/src/services/azblob/deleter.rs +++ b/core/services/azblob/src/deleter.rs @@ -21,9 +21,9 @@ use http::StatusCode; use super::core::*; use super::error::parse_error; -use crate::raw::oio::BatchDeleteResult; -use crate::raw::*; -use crate::*; +use opendal_core::raw::oio::BatchDeleteResult; +use opendal_core::raw::*; +use opendal_core::*; pub struct AzblobDeleter { core: Arc<AzblobCore>, diff --git a/core/core/src/services/azblob/docs.md b/core/services/azblob/src/docs.md similarity index 100% rename from core/core/src/services/azblob/docs.md rename to core/services/azblob/src/docs.md diff --git a/core/core/src/services/azblob/error.rs b/core/services/azblob/src/error.rs similarity index 92% rename from core/core/src/services/azblob/error.rs rename to core/services/azblob/src/error.rs index 0d26b8cd5..965dfb0cb 100644 --- a/core/core/src/services/azblob/error.rs +++ b/core/services/azblob/src/error.rs @@ -20,12 +20,11 @@ use std::fmt::Debug; use bytes::Buf; use http::Response; use http::StatusCode; +use opendal_core::*; +use opendal_service_azure_common::with_azure_error_response_context; use quick_xml::de; use serde::Deserialize; -use crate::raw::*; -use crate::*; - /// AzblobError is the error returned by azure blob service. #[derive(Default, Deserialize)] #[serde(default, rename_all = "PascalCase")] @@ -83,18 +82,17 @@ pub(super) fn parse_error(resp: Response<Buffer>) -> Error { }; // If there is no body here, fill with error code. - if message.is_empty() { - if let Some(v) = parts.headers.get("x-ms-error-code") { - if let Ok(code) = v.to_str() { - message = format!( - "{:?}", - AzblobError { - code: code.to_string(), - ..Default::default() - } - ) + if message.is_empty() + && let Some(v) = parts.headers.get("x-ms-error-code") + && let Ok(code) = v.to_str() + { + message = format!( + "{:?}", + AzblobError { + code: code.to_string(), + ..Default::default() } - } + ) } let mut err = Error::new(kind, &message); diff --git a/core/core/src/services/azblob/mod.rs b/core/services/azblob/src/lib.rs similarity index 92% rename from core/core/src/services/azblob/mod.rs rename to core/services/azblob/src/lib.rs index 75a1d7211..69fdf9d88 100644 --- a/core/core/src/services/azblob/mod.rs +++ b/core/services/azblob/src/lib.rs @@ -18,15 +18,15 @@ /// Default scheme for azblob service. pub const AZBLOB_SCHEME: &str = "azblob"; -use crate::types::DEFAULT_OPERATOR_REGISTRY; +use opendal_core::DEFAULT_OPERATOR_REGISTRY; mod backend; mod config; -pub(crate) mod core; +pub mod core; mod deleter; mod error; mod lister; -pub(crate) mod writer; +pub mod writer; pub use backend::AzblobBuilder as Azblob; pub use config::AzblobConfig; diff --git a/core/core/src/services/azblob/lister.rs b/core/services/azblob/src/lister.rs similarity index 98% rename from core/core/src/services/azblob/lister.rs rename to core/services/azblob/src/lister.rs index 2d2747125..ffd84c87f 100644 --- a/core/core/src/services/azblob/lister.rs +++ b/core/services/azblob/src/lister.rs @@ -23,8 +23,8 @@ use quick_xml::de; use super::core::AzblobCore; use super::core::ListBlobsOutput; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; pub struct AzblobLister { core: Arc<AzblobCore>, diff --git a/core/core/src/services/azblob/writer.rs b/core/services/azblob/src/writer.rs similarity index 99% rename from core/core/src/services/azblob/writer.rs rename to core/services/azblob/src/writer.rs index 1559383e1..6d9fc2145 100644 --- a/core/core/src/services/azblob/writer.rs +++ b/core/services/azblob/src/writer.rs @@ -23,8 +23,8 @@ use uuid::Uuid; use super::core::AzblobCore; use super::core::constants::X_MS_VERSION_ID; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; const X_MS_BLOB_TYPE: &str = "x-ms-blob-type"; diff --git a/core/services/azdls/Cargo.toml b/core/services/azdls/Cargo.toml new file mode 100644 index 000000000..c9d8f445a --- /dev/null +++ b/core/services/azdls/Cargo.toml @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[package] +name = "opendal-service-azdls" +version = "0.55.0" +edition = "2024" +license = "Apache-2.0" +repository = "https://github.com/apache/opendal" +description = "Apache OpenDAL Azure Data Lake Storage Gen2 service implementation" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +opendal-core = { path = "../../core", version = "0.55.0", default-features = false } +opendal-service-azure-common = { path = "../azure-common", version = "0.55.0" } +bytes = "1.6" +ctor = "0.6" +http = "1.1" +log = "0.4" +quick-xml = { version = "0.38", features = ["serialize", "overlapped-lists"] } +reqsign = { version = "0.16.5", default-features = false, features = ["reqwest_request", "services-azblob"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" diff --git a/core/core/src/services/azdls/backend.rs b/core/services/azdls/src/backend.rs similarity index 97% rename from core/core/src/services/azdls/backend.rs rename to core/services/azdls/src/backend.rs index 292345b23..c6e52ce43 100644 --- a/core/core/src/services/azdls/backend.rs +++ b/core/services/azdls/src/backend.rs @@ -34,8 +34,11 @@ use super::error::parse_error; use super::lister::AzdlsLister; use super::writer::AzdlsWriter; use super::writer::AzdlsWriters; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; +use opendal_service_azure_common::{ + AzureStorageService, azure_account_name_from_endpoint, azure_config_from_connection_string, +}; impl From<AzureStorageConfig> for AzdlsConfig { fn from(config: AzureStorageConfig) -> Self { @@ -227,8 +230,7 @@ impl AzdlsBuilder { /// .unwrap(); /// ``` pub fn from_connection_string(conn_str: &str) -> Result<Self> { - let config = - raw::azure_config_from_connection_string(conn_str, raw::AzureStorageService::Adls)?; + let config = azure_config_from_connection_string(conn_str, AzureStorageService::Adls)?; Ok(AzdlsConfig::from(config).into_builder()) } @@ -265,7 +267,7 @@ impl Builder for AzdlsBuilder { .config .account_name .clone() - .or_else(|| raw::azure_account_name_from_endpoint(endpoint.as_str())), + .or_else(|| azure_account_name_from_endpoint(endpoint.as_str())), account_key: self.config.account_key.clone(), sas_token: self.config.sas_token, client_id: self.config.client_id.clone(), diff --git a/core/core/src/services/azdls/config.rs b/core/services/azdls/src/config.rs similarity index 87% rename from core/core/src/services/azdls/config.rs rename to core/services/azdls/src/config.rs index b4db8c059..28efd83b6 100644 --- a/core/core/src/services/azdls/config.rs +++ b/core/services/azdls/src/config.rs @@ -70,29 +70,29 @@ impl Debug for AzdlsConfig { } } -impl crate::Configurator for AzdlsConfig { +impl opendal_core::Configurator for AzdlsConfig { type Builder = AzdlsBuilder; - fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> { + fn from_uri(uri: &opendal_core::OperatorUri) -> opendal_core::Result<Self> { let mut map = uri.options().clone(); if let Some(authority) = uri.authority() { map.insert("endpoint".to_string(), format!("https://{authority}")); } - if let Some(host) = uri.name() { - if let Some(account) = host.split('.').next() { - if !account.is_empty() { - map.entry("account_name".to_string()) - .or_insert_with(|| account.to_string()); - } - } + if let Some(account) = uri + .name() + .and_then(|host| host.split('.').next()) + .filter(|account| !account.is_empty()) + { + map.entry("account_name".to_string()) + .or_insert_with(|| account.to_string()); } if let Some(root) = uri.root() { if let Some((filesystem, rest)) = root.split_once('/') { if filesystem.is_empty() { - return Err(crate::Error::new( - crate::ErrorKind::ConfigInvalid, + return Err(opendal_core::Error::new( + opendal_core::ErrorKind::ConfigInvalid, "filesystem is required in uri path", ) .with_context("service", AZDLS_SCHEME)); @@ -107,8 +107,8 @@ impl crate::Configurator for AzdlsConfig { } if !map.contains_key("filesystem") { - return Err(crate::Error::new( - crate::ErrorKind::ConfigInvalid, + return Err(opendal_core::Error::new( + opendal_core::ErrorKind::ConfigInvalid, "filesystem is required", ) .with_context("service", AZDLS_SCHEME)); @@ -129,8 +129,8 @@ impl crate::Configurator for AzdlsConfig { #[cfg(test)] mod tests { use super::*; - use crate::Configurator; - use crate::types::OperatorUri; + use opendal_core::Configurator; + use opendal_core::OperatorUri; #[test] fn from_uri_sets_endpoint_filesystem_root_and_account() { diff --git a/core/core/src/services/azdls/core.rs b/core/services/azdls/src/core.rs similarity index 99% rename from core/core/src/services/azdls/core.rs rename to core/services/azdls/src/core.rs index e88b757cb..f40cfc636 100644 --- a/core/core/src/services/azdls/core.rs +++ b/core/services/azdls/src/core.rs @@ -32,8 +32,8 @@ use reqsign::AzureStorageLoader; use reqsign::AzureStorageSigner; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; const X_MS_RENAME_SOURCE: &str = "x-ms-rename-source"; const X_MS_VERSION: &str = "x-ms-version"; diff --git a/core/core/src/services/azdls/deleter.rs b/core/services/azdls/src/deleter.rs similarity index 96% rename from core/core/src/services/azdls/deleter.rs rename to core/services/azdls/src/deleter.rs index 583b61d64..f90c87c8a 100644 --- a/core/core/src/services/azdls/deleter.rs +++ b/core/services/azdls/src/deleter.rs @@ -21,8 +21,8 @@ use http::StatusCode; use super::core::*; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; pub struct AzdlsDeleter { core: Arc<AzdlsCore>, diff --git a/core/core/src/services/azdls/docs.md b/core/services/azdls/src/docs.md similarity index 100% rename from core/core/src/services/azdls/docs.md rename to core/services/azdls/src/docs.md diff --git a/core/core/src/services/azdls/error.rs b/core/services/azdls/src/error.rs similarity index 88% rename from core/core/src/services/azdls/error.rs rename to core/services/azdls/src/error.rs index 83770195e..11ea17b50 100644 --- a/core/core/src/services/azdls/error.rs +++ b/core/services/azdls/src/error.rs @@ -20,12 +20,11 @@ use std::fmt::Debug; use bytes::Buf; use http::Response; use http::StatusCode; +use opendal_core::*; +use opendal_service_azure_common::with_azure_error_response_context; use quick_xml::de; use serde::Deserialize; -use crate::raw::*; -use crate::*; - /// AzdlsError is the error returned by azure dfs service. #[derive(Default, Deserialize)] #[serde(default, rename_all = "PascalCase")] @@ -81,18 +80,17 @@ pub(super) fn parse_error(resp: Response<Buffer>) -> Error { Err(_) => String::from_utf8_lossy(&bs).into_owned(), }; // If there is no body here, fill with error code. - if message.is_empty() { - if let Some(v) = parts.headers.get("x-ms-error-code") { - if let Ok(code) = v.to_str() { - message = format!( - "{:?}", - AzdlsError { - code: code.to_string(), - ..Default::default() - } - ) + if message.is_empty() + && let Some(v) = parts.headers.get("x-ms-error-code") + && let Ok(code) = v.to_str() + { + message = format!( + "{:?}", + AzdlsError { + code: code.to_string(), + ..Default::default() } - } + ) } let mut err = Error::new(kind, &message); diff --git a/core/core/src/services/azdls/mod.rs b/core/services/azdls/src/lib.rs similarity index 96% rename from core/core/src/services/azdls/mod.rs rename to core/services/azdls/src/lib.rs index 7a75dae09..9c139e977 100644 --- a/core/core/src/services/azdls/mod.rs +++ b/core/services/azdls/src/lib.rs @@ -18,7 +18,7 @@ /// Default scheme for azdls service. pub const AZDLS_SCHEME: &str = "azdls"; -use crate::types::DEFAULT_OPERATOR_REGISTRY; +use opendal_core::DEFAULT_OPERATOR_REGISTRY; mod backend; mod config; diff --git a/core/core/src/services/azdls/lister.rs b/core/services/azdls/src/lister.rs similarity index 99% rename from core/core/src/services/azdls/lister.rs rename to core/services/azdls/src/lister.rs index 2e5744a81..80d5f38e6 100644 --- a/core/core/src/services/azdls/lister.rs +++ b/core/services/azdls/src/lister.rs @@ -23,8 +23,8 @@ use serde_json::de; use super::core::AzdlsCore; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; pub struct AzdlsLister { core: Arc<AzdlsCore>, diff --git a/core/core/src/services/azdls/writer.rs b/core/services/azdls/src/writer.rs similarity index 99% rename from core/core/src/services/azdls/writer.rs rename to core/services/azdls/src/writer.rs index d46e8e792..e8e4db6b9 100644 --- a/core/core/src/services/azdls/writer.rs +++ b/core/services/azdls/src/writer.rs @@ -23,8 +23,8 @@ use super::core::AzdlsCore; use super::core::FILE; use super::core::X_MS_VERSION_ID; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; /// Writer type for azdls: non-append uses PositionWriter, append uses AppendWriter. pub type AzdlsWriters = TwoWays<oio::PositionWriter<AzdlsWriter>, oio::AppendWriter<AzdlsWriter>>; diff --git a/core/services/azfile/Cargo.toml b/core/services/azfile/Cargo.toml new file mode 100644 index 000000000..20ed5177f --- /dev/null +++ b/core/services/azfile/Cargo.toml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[package] +name = "opendal-service-azfile" +version = "0.55.0" +edition = "2024" +license = "Apache-2.0" +repository = "https://github.com/apache/opendal" +description = "Apache OpenDAL Azure File Storage service implementation" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +opendal-core = { path = "../../core", version = "0.55.0", default-features = false } +opendal-service-azure-common = { path = "../azure-common", version = "0.55.0" } +bytes = "1.6" +ctor = "0.6" +http = "1.1" +log = "0.4" +quick-xml = { version = "0.38", features = ["serialize", "overlapped-lists"] } +reqsign = { version = "0.16.5", default-features = false, features = ["reqwest_request", "services-azblob"] } +serde = { version = "1", features = ["derive"] } diff --git a/core/core/src/services/azfile/backend.rs b/core/services/azfile/src/backend.rs similarity index 97% rename from core/core/src/services/azfile/backend.rs rename to core/services/azfile/src/backend.rs index ebdf6739d..e995e66e7 100644 --- a/core/core/src/services/azfile/backend.rs +++ b/core/services/azfile/src/backend.rs @@ -34,8 +34,11 @@ use super::error::parse_error; use super::lister::AzfileLister; use super::writer::AzfileWriter; use super::writer::AzfileWriters; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; +use opendal_service_azure_common::{ + AzureStorageService, azure_account_name_from_endpoint, azure_config_from_connection_string, +}; impl From<AzureStorageConfig> for AzfileConfig { fn from(config: AzureStorageConfig) -> Self { @@ -160,8 +163,7 @@ impl AzfileBuilder { /// .unwrap(); /// ``` pub fn from_connection_string(conn_str: &str) -> Result<Self> { - let config = - raw::azure_config_from_connection_string(conn_str, raw::AzureStorageService::File)?; + let config = azure_config_from_connection_string(conn_str, AzureStorageService::File)?; Ok(AzfileConfig::from(config).into_builder()) } @@ -188,7 +190,7 @@ impl Builder for AzfileBuilder { .config .account_name .clone() - .or_else(|| raw::azure_account_name_from_endpoint(endpoint.as_str())); + .or_else(|| azure_account_name_from_endpoint(endpoint.as_str())); let account_name = match account_name_option { Some(account_name) => Ok(account_name), diff --git a/core/core/src/services/azfile/config.rs b/core/services/azfile/src/config.rs similarity index 85% rename from core/core/src/services/azfile/config.rs rename to core/services/azfile/src/config.rs index e6624c717..572c375c9 100644 --- a/core/core/src/services/azfile/config.rs +++ b/core/services/azfile/src/config.rs @@ -50,29 +50,29 @@ impl Debug for AzfileConfig { } } -impl crate::Configurator for AzfileConfig { +impl opendal_core::Configurator for AzfileConfig { type Builder = AzfileBuilder; - fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> { + fn from_uri(uri: &opendal_core::OperatorUri) -> opendal_core::Result<Self> { let mut map = uri.options().clone(); if let Some(authority) = uri.authority() { map.insert("endpoint".to_string(), format!("https://{authority}")); } - if let Some(host) = uri.name() { - if let Some(account) = host.split('.').next() { - if !account.is_empty() { - map.entry("account_name".to_string()) - .or_insert_with(|| account.to_string()); - } - } + if let Some(account) = uri + .name() + .and_then(|host| host.split('.').next()) + .filter(|account| !account.is_empty()) + { + map.entry("account_name".to_string()) + .or_insert_with(|| account.to_string()); } if let Some(root) = uri.root() { if let Some((share, rest)) = root.split_once('/') { if share.is_empty() { - return Err(crate::Error::new( - crate::ErrorKind::ConfigInvalid, + return Err(opendal_core::Error::new( + opendal_core::ErrorKind::ConfigInvalid, "share name is required in uri path", ) .with_context("service", AZFILE_SCHEME)); @@ -87,8 +87,8 @@ impl crate::Configurator for AzfileConfig { } if !map.contains_key("share_name") { - return Err(crate::Error::new( - crate::ErrorKind::ConfigInvalid, + return Err(opendal_core::Error::new( + opendal_core::ErrorKind::ConfigInvalid, "share name is required", ) .with_context("service", AZFILE_SCHEME)); @@ -109,8 +109,8 @@ impl crate::Configurator for AzfileConfig { #[cfg(test)] mod tests { use super::*; - use crate::Configurator; - use crate::types::OperatorUri; + use opendal_core::Configurator; + use opendal_core::OperatorUri; #[test] fn from_uri_sets_endpoint_share_root_and_account() { diff --git a/core/core/src/services/azfile/core.rs b/core/services/azfile/src/core.rs similarity index 99% rename from core/core/src/services/azfile/core.rs rename to core/services/azfile/src/core.rs index 5cd8205fc..da4257089 100644 --- a/core/core/src/services/azfile/core.rs +++ b/core/services/azfile/src/core.rs @@ -33,8 +33,8 @@ use reqsign::AzureStorageLoader; use reqsign::AzureStorageSigner; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; const X_MS_VERSION: &str = "x-ms-version"; const X_MS_WRITE: &str = "x-ms-write"; diff --git a/core/core/src/services/azfile/deleter.rs b/core/services/azfile/src/deleter.rs similarity index 97% rename from core/core/src/services/azfile/deleter.rs rename to core/services/azfile/src/deleter.rs index eb881e9dd..6400cebf8 100644 --- a/core/core/src/services/azfile/deleter.rs +++ b/core/services/azfile/src/deleter.rs @@ -21,8 +21,8 @@ use http::StatusCode; use super::core::*; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; pub struct AzfileDeleter { core: Arc<AzfileCore>, diff --git a/core/core/src/services/azfile/docs.md b/core/services/azfile/src/docs.md similarity index 100% rename from core/core/src/services/azfile/docs.md rename to core/services/azfile/src/docs.md diff --git a/core/core/src/services/azfile/error.rs b/core/services/azfile/src/error.rs similarity index 88% rename from core/core/src/services/azfile/error.rs rename to core/services/azfile/src/error.rs index ec0bd6970..9c62c281a 100644 --- a/core/core/src/services/azfile/error.rs +++ b/core/services/azfile/src/error.rs @@ -20,12 +20,11 @@ use std::fmt::Debug; use bytes::Buf; use http::Response; use http::StatusCode; +use opendal_core::*; +use opendal_service_azure_common::with_azure_error_response_context; use quick_xml::de; use serde::Deserialize; -use crate::raw::*; -use crate::*; - /// AzfileError is the error returned by azure file service. #[derive(Default, Deserialize)] #[serde(default, rename_all = "PascalCase")] @@ -82,18 +81,17 @@ pub(super) fn parse_error(resp: Response<Buffer>) -> Error { }; // If there is no body here, fill with error code. - if message.is_empty() { - if let Some(v) = parts.headers.get("x-ms-error-code") { - if let Ok(code) = v.to_str() { - message = format!( - "{:?}", - AzfileError { - code: code.to_string(), - ..Default::default() - } - ) + if message.is_empty() + && let Some(v) = parts.headers.get("x-ms-error-code") + && let Ok(code) = v.to_str() + { + message = format!( + "{:?}", + AzfileError { + code: code.to_string(), + ..Default::default() } - } + ) } let mut err = Error::new(kind, &message); diff --git a/core/core/src/services/azfile/mod.rs b/core/services/azfile/src/lib.rs similarity index 96% rename from core/core/src/services/azfile/mod.rs rename to core/services/azfile/src/lib.rs index c659719f8..ea3f41abf 100644 --- a/core/core/src/services/azfile/mod.rs +++ b/core/services/azfile/src/lib.rs @@ -18,7 +18,7 @@ /// Default scheme for azfile service. pub const AZFILE_SCHEME: &str = "azfile"; -use crate::types::DEFAULT_OPERATOR_REGISTRY; +use opendal_core::DEFAULT_OPERATOR_REGISTRY; mod backend; mod config; diff --git a/core/core/src/services/azfile/lister.rs b/core/services/azfile/src/lister.rs similarity index 99% rename from core/core/src/services/azfile/lister.rs rename to core/services/azfile/src/lister.rs index e52cbd0d6..289db82b9 100644 --- a/core/core/src/services/azfile/lister.rs +++ b/core/services/azfile/src/lister.rs @@ -24,8 +24,8 @@ use serde::Deserialize; use super::core::AzfileCore; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; pub struct AzfileLister { core: Arc<AzfileCore>, diff --git a/core/core/src/services/azfile/writer.rs b/core/services/azfile/src/writer.rs similarity index 98% rename from core/core/src/services/azfile/writer.rs rename to core/services/azfile/src/writer.rs index b8686c625..cce523218 100644 --- a/core/core/src/services/azfile/writer.rs +++ b/core/services/azfile/src/writer.rs @@ -21,8 +21,8 @@ use http::StatusCode; use super::core::AzfileCore; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; pub type AzfileWriters = TwoWays<oio::OneShotWriter<AzfileWriter>, oio::AppendWriter<AzfileWriter>>; diff --git a/core/services/azure-common/Cargo.toml b/core/services/azure-common/Cargo.toml new file mode 100644 index 000000000..7dba50f67 --- /dev/null +++ b/core/services/azure-common/Cargo.toml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[package] +name = "opendal-service-azure-common" +version = "0.55.0" +edition = "2024" +license = "Apache-2.0" +repository = "https://github.com/apache/opendal" +description = "Shared Azure helpers for Apache OpenDAL services" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +opendal-core = { path = "../../core", version = "0.55.0", default-features = false } +http = "1.1" +reqsign = { version = "0.16.5", default-features = false } diff --git a/core/core/src/raw/azure.rs b/core/services/azure-common/src/lib.rs similarity index 97% rename from core/core/src/raw/azure.rs rename to core/services/azure-common/src/lib.rs index c9ef55bbc..e380539df 100644 --- a/core/core/src/raw/azure.rs +++ b/core/services/azure-common/src/lib.rs @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny(missing_docs)] //! Azure Storage helpers. //! //! This module provides utilities and shared abstractions for services built @@ -25,10 +27,9 @@ use std::collections::HashMap; use http::Uri; use http::response::Parts; +use opendal_core::{Error, ErrorKind, Result}; use reqsign::{AzureStorageConfig, AzureStorageCredential}; -use crate::{Error, ErrorKind, Result}; - /// Parses an [Azure connection string][1] into a configuration object. /// /// The connection string doesn't have to specify all required parameters @@ -38,7 +39,7 @@ use crate::{Error, ErrorKind, Result}; /// the fields used to parse the endpoint. /// /// [1]: https://learn.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string -pub(crate) fn azure_config_from_connection_string( +pub fn azure_config_from_connection_string( conn_str: &str, storage: AzureStorageService, ) -> Result<AzureStorageConfig> { @@ -72,21 +73,22 @@ pub(crate) fn azure_config_from_connection_string( /// The service that a connection string refers to. The type influences /// interpretation of endpoint-related fields during parsing. #[derive(PartialEq)] -pub(crate) enum AzureStorageService { +pub enum AzureStorageService { /// Azure Blob Storage. Blob, /// Azure File Storage. - #[cfg(feature = "services-azfile")] File, /// Azure Data Lake Storage Gen2. /// Backed by Blob Storage but exposed through a different endpoint (`dfs`). - #[cfg(feature = "services-azdls")] Adls, } -pub(crate) fn azure_account_name_from_endpoint(endpoint: &str) -> Option<String> { +/// Try to extract the storage account name from an Azure endpoint. +/// +/// Returns `None` if the endpoint doesn't match known Azure Storage suffixes. +pub fn azure_account_name_from_endpoint(endpoint: &str) -> Option<String> { /// Known Azure Storage endpoint suffixes. const KNOWN_ENDPOINT_SUFFIXES: &[&str] = &[ "core.windows.net", // Azure public cloud @@ -184,9 +186,7 @@ fn collect_endpoint( ) -> Result<Option<String>> { match storage { AzureStorageService::Blob => collect_or_build_endpoint(key_values, "BlobEndpoint", "blob"), - #[cfg(feature = "services-azfile")] AzureStorageService::File => collect_or_build_endpoint(key_values, "FileEndpoint", "file"), - #[cfg(feature = "services-azdls")] AzureStorageService::Adls => { // ADLS doesn't have a dedicated endpoint field and we can only // build it from parts. @@ -329,7 +329,7 @@ mod tests { use http::Uri; use reqsign::AzureStorageConfig; - use crate::raw::azure::censor_sas_uri; + use super::censor_sas_uri; use super::{ AzureStorageService, azure_account_name_from_endpoint, azure_config_from_connection_string, @@ -451,7 +451,6 @@ mod tests { ), ]; - #[cfg(feature = "services-azdls")] test_cases.push( ("adls endpoint from parts", (AzureStorageService::Adls, "AccountName=testaccount;EndpointSuffix=core.windows.net;DefaultEndpointsProtocol=https"), @@ -463,7 +462,6 @@ mod tests { ) ); - #[cfg(feature = "services-azfile")] test_cases.extend(vec![ ( "file endpoint from field", @@ -490,7 +488,6 @@ mod tests { ), ]); - #[cfg(feature = "services-azdls")] test_cases.push(( "azdls development storage", (AzureStorageService::Adls, "UseDevelopmentStorage=true"), diff --git a/core/services/ghac/Cargo.toml b/core/services/ghac/Cargo.toml new file mode 100644 index 000000000..d6f6384c0 --- /dev/null +++ b/core/services/ghac/Cargo.toml @@ -0,0 +1,41 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[package] +name = "opendal-service-ghac" +version = "0.55.0" +edition = "2024" +license = "Apache-2.0" +repository = "https://github.com/apache/opendal" +description = "Apache OpenDAL GitHub Actions Cache service implementation" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +opendal-core = { path = "../../core", version = "0.55.0", default-features = false } +opendal-service-azblob = { path = "../azblob", version = "0.55.0", default-features = false } +bytes = "1.6" +ghac = { version = "0.2.0", default-features = false } +http = "1.1" +log = "0.4" +prost = { version = "0.13", default-features = false } +reqsign = { version = "0.16.5", default-features = false } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +sha2 = "0.10" +ctor = "0.6" diff --git a/core/core/src/services/ghac/backend.rs b/core/services/ghac/src/backend.rs similarity index 99% rename from core/core/src/services/ghac/backend.rs rename to core/services/ghac/src/backend.rs index b2c896e15..7063dc601 100644 --- a/core/core/src/services/ghac/backend.rs +++ b/core/services/ghac/src/backend.rs @@ -30,8 +30,8 @@ use super::core::GhacCore; use super::core::*; use super::error::parse_error; use super::writer::GhacWriter; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; fn value_or_env( explicit_value: Option<String>, diff --git a/core/core/src/services/ghac/config.rs b/core/services/ghac/src/config.rs similarity index 94% rename from core/core/src/services/ghac/config.rs rename to core/services/ghac/src/config.rs index 07469326e..4acda69ac 100644 --- a/core/core/src/services/ghac/config.rs +++ b/core/services/ghac/src/config.rs @@ -47,10 +47,10 @@ impl Debug for GhacConfig { } } -impl crate::Configurator for GhacConfig { +impl opendal_core::Configurator for GhacConfig { type Builder = GhacBuilder; - fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> { + fn from_uri(uri: &opendal_core::OperatorUri) -> opendal_core::Result<Self> { let mut map = uri.options().clone(); if let Some(authority) = uri.authority() { @@ -89,8 +89,8 @@ impl crate::Configurator for GhacConfig { #[cfg(test)] mod tests { use super::*; - use crate::Configurator; - use crate::types::OperatorUri; + use opendal_core::Configurator; + use opendal_core::OperatorUri; #[test] fn from_uri_sets_endpoint_version_and_root() { diff --git a/core/core/src/services/ghac/core.rs b/core/services/ghac/src/core.rs similarity index 99% rename from core/core/src/services/ghac/core.rs rename to core/services/ghac/src/core.rs index d4518bb7f..5effb1bea 100644 --- a/core/core/src/services/ghac/core.rs +++ b/core/services/ghac/src/core.rs @@ -38,8 +38,8 @@ use serde::Deserialize; use serde::Serialize; use super::error::parse_error; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; /// The base url for cache url. pub const CACHE_URL_BASE: &str = "_apis/artifactcache"; diff --git a/core/core/src/services/ghac/docs.md b/core/services/ghac/src/docs.md similarity index 100% rename from core/core/src/services/ghac/docs.md rename to core/services/ghac/src/docs.md diff --git a/core/core/src/services/ghac/error.rs b/core/services/ghac/src/error.rs similarity index 97% rename from core/core/src/services/ghac/error.rs rename to core/services/ghac/src/error.rs index 7efafe1f6..903f1d829 100644 --- a/core/core/src/services/ghac/error.rs +++ b/core/services/ghac/src/error.rs @@ -18,8 +18,8 @@ use http::Response; use http::StatusCode; -use crate::raw::*; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; /// Parse error response into Error. pub(super) fn parse_error(resp: Response<Buffer>) -> Error { diff --git a/core/core/src/services/ghac/mod.rs b/core/services/ghac/src/lib.rs similarity index 96% rename from core/core/src/services/ghac/mod.rs rename to core/services/ghac/src/lib.rs index c35782850..c1981b075 100644 --- a/core/core/src/services/ghac/mod.rs +++ b/core/services/ghac/src/lib.rs @@ -18,7 +18,7 @@ /// Default scheme for ghac service. pub const GHAC_SCHEME: &str = "ghac"; -use crate::types::DEFAULT_OPERATOR_REGISTRY; +use opendal_core::DEFAULT_OPERATOR_REGISTRY; mod backend; mod config; diff --git a/core/core/src/services/ghac/writer.rs b/core/services/ghac/src/writer.rs similarity index 89% rename from core/core/src/services/ghac/writer.rs rename to core/services/ghac/src/writer.rs index e0d550753..d6b4f4692 100644 --- a/core/core/src/services/ghac/writer.rs +++ b/core/services/ghac/src/writer.rs @@ -15,28 +15,29 @@ // specific language governing permissions and limitations // under the License. +use std::future::Future; use std::str::FromStr; use std::sync::Arc; use super::core::*; use super::error::parse_error; -use crate::raw::*; -use crate::services::core::AzblobCore; -use crate::services::writer::AzblobWriter; -use crate::*; +use opendal_core::raw::*; +use opendal_core::*; +use opendal_service_azblob::core::AzblobCore; +use opendal_service_azblob::writer::AzblobWriter; -pub type GhacWriter = TwoWays<GhacWriterV1, GhacWriterV2>; +pub struct GhacWriter(pub TwoWays<GhacWriterV1, GhacWriterV2>); impl GhacWriter { /// TODO: maybe we can move the signed url logic to azblob service instead. pub fn new(core: Arc<GhacCore>, write_path: String, url: String) -> Result<Self> { match core.service_version { - GhacVersion::V1 => Ok(TwoWays::One(GhacWriterV1 { + GhacVersion::V1 => Ok(GhacWriter(TwoWays::One(GhacWriterV1 { core, path: write_path, url, size: 0, - })), + }))), GhacVersion::V2 => { let uri = http::Uri::from_str(&url) .map_err(new_http_uri_invalid_error)? @@ -123,18 +124,32 @@ impl GhacWriter { }); let w = AzblobWriter::new(azure_core, OpWrite::default(), path.to_string()); let writer = oio::BlockWriter::new(core.info.clone(), w, 4); - Ok(TwoWays::Two(GhacWriterV2 { + Ok(GhacWriter(TwoWays::Two(GhacWriterV2 { core, writer, path: write_path, url, size: 0, - })) + }))) } } } } +impl oio::Write for GhacWriter { + fn write(&mut self, bs: Buffer) -> impl Future<Output = Result<()>> + MaybeSend { + self.0.write(bs) + } + + fn abort(&mut self) -> impl Future<Output = Result<()>> + MaybeSend { + self.0.abort() + } + + fn close(&mut self) -> impl Future<Output = Result<Metadata>> + MaybeSend { + self.0.close() + } +} + pub struct GhacWriterV1 { core: Arc<GhacCore>, diff --git a/core/src/lib.rs b/core/src/lib.rs index 8349138de..e6746cef7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -27,6 +27,14 @@ pub use opendal_core::*; /// Re-export of service implementations. pub mod services { pub use opendal_core::services::*; + #[cfg(feature = "services-azblob")] + pub use opendal_service_azblob::*; + #[cfg(feature = "services-azdls")] + pub use opendal_service_azdls::*; + #[cfg(feature = "services-azfile")] + pub use opendal_service_azfile::*; + #[cfg(feature = "services-ghac")] + pub use opendal_service_ghac::*; #[cfg(feature = "services-moka")] pub use opendal_service_moka::*; #[cfg(feature = "services-s3")]
