Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ all-features = true
[features]
blocking = ["opendal-core/blocking"]
default = [
"register-services",
"reqwest-rustls-tls",
"executors-tokio",
"layers-concurrent-limit",
Expand Down Expand Up @@ -117,6 +118,8 @@ layers-tail-cut = ["dep:opendal-layer-tail-cut"]
layers-throttle = ["dep:opendal-layer-throttle"]
layers-timeout = ["dep:opendal-layer-timeout"]
layers-tracing = ["dep:opendal-layer-tracing"]
# Register services enabled to the OperatorRegistry so that they can be used by `Operator::from_uri`.
register-services = ["dep:ctor"]
reqwest-rustls-tls = ["opendal-core/reqwest-rustls-tls"]
services-aliyun-drive = ["dep:opendal-service-aliyun-drive"]
services-alluxio = ["dep:opendal-service-alluxio"]
Expand Down Expand Up @@ -207,7 +210,7 @@ path = "tests/behavior/main.rs"
required-features = ["tests"]

[dependencies]
ctor = { workspace = true }
ctor = { workspace = true, optional = true }
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-layer-await-tree = { path = "layers/await-tree", version = "0.55.0", optional = true, default-features = false }
Expand Down
1 change: 0 additions & 1 deletion core/core/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ mod execute;
pub use execute::*;

mod operator;
pub use operator::DEFAULT_OPERATOR_REGISTRY;
pub use operator::IntoOperatorUri;
pub use operator::Operator;
pub use operator::OperatorBuilder;
Expand Down
2 changes: 1 addition & 1 deletion core/core/src/types/operator/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl Operator {
/// # }
/// ```
pub fn from_uri(uri: impl IntoOperatorUri) -> Result<Operator> {
crate::DEFAULT_OPERATOR_REGISTRY.load(uri)
OperatorRegistry::get().load(uri)
}

/// Create a new operator via given scheme and iterator of config value in dynamic dispatch.
Expand Down
2 changes: 1 addition & 1 deletion core/core/src/types/operator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub use info::OperatorInfo;
pub mod operator_futures;

mod registry;
pub use registry::{DEFAULT_OPERATOR_REGISTRY, OperatorFactory, OperatorRegistry};
pub use registry::{OperatorFactory, OperatorRegistry};

mod uri;
pub use uri::{IntoOperatorUri, OperatorUri};
42 changes: 19 additions & 23 deletions core/core/src/types/operator/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,38 @@
// specific language governing permissions and limitations
// under the License.

use std::collections::HashMap;
use std::sync::{LazyLock, Mutex};

use crate::types::builder::{Builder, Configurator};
use crate::types::{IntoOperatorUri, OperatorUri};
use crate::{Error, ErrorKind, Operator, Result};
use std::collections::HashMap;
use std::sync::{LazyLock, Mutex};

/// Factory signature used to construct [`Operator`] from a URI and extra options.
pub type OperatorFactory = fn(&OperatorUri) -> Result<Operator>;

/// Default registry used by [`Operator::from_uri`].
///
/// `memory` is always registered here since it's used pervasively in unit tests
/// and as a zero-dependency backend.
///
/// Other optional service registrations are handled by the facade crate `opendal`.
pub static DEFAULT_OPERATOR_REGISTRY: LazyLock<OperatorRegistry> = LazyLock::new(|| {
let registry = OperatorRegistry::new();

crate::services::register_memory_service(&registry);

registry
});

/// Global registry that maps schemes to [`OperatorFactory`] functions.
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct OperatorRegistry {
factories: Mutex<HashMap<String, OperatorFactory>>,
}

impl OperatorRegistry {
/// Create a new, empty registry.
pub fn new() -> Self {
Self {
factories: Mutex::new(HashMap::new()),
}
/// Get the global registry.
pub fn get() -> &'static Self {
Comment on lines -43 to +35
Copy link
Member Author

@tisonkun tisonkun Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd better have one registry and avoid any outside construction.

/// The global registry used by [`Operator::from_uri`].
///
/// `memory` is always registered here since it's used pervasively in unit tests
/// and as a zero-dependency backend.
///
/// Other optional service registrations are handled by the facade crate `opendal`.
static OPERATOR_REGISTRY: LazyLock<OperatorRegistry> = LazyLock::new(|| {
let factories = Mutex::new(HashMap::default());
let registry = OperatorRegistry { factories };
crate::services::register_memory_service(&registry);
registry
});

&OPERATOR_REGISTRY
}

/// Register a builder for the given scheme.
Expand Down
13 changes: 5 additions & 8 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ pub use opendal_core::*;
#[cfg(feature = "tests")]
pub extern crate opendal_testkit as tests;

static DEFAULT_REGISTRY_INIT: std::sync::Once = std::sync::Once::new();

/// Initialize [`DEFAULT_OPERATOR_REGISTRY`] with enabled services.
/// Initialize the global [`OperatorRegistry`] with enabled services.
///
/// This function is safe to call multiple times and will only perform
/// initialization once.
Expand All @@ -41,13 +39,11 @@ static DEFAULT_REGISTRY_INIT: std::sync::Once = std::sync::Once::new();
/// should call this function explicitly before using `Operator::from_uri` or
/// `Operator::via_iter`.
pub fn init_default_registry() {
DEFAULT_REGISTRY_INIT.call_once(|| {
let registry = &opendal_core::DEFAULT_OPERATOR_REGISTRY;
init_default_registry_inner(registry);
});
static DEFAULT_REGISTRY_INIT: std::sync::Once = std::sync::Once::new();
DEFAULT_REGISTRY_INIT.call_once(|| init_default_registry_inner(OperatorRegistry::get()));
}

fn init_default_registry_inner(registry: &opendal_core::OperatorRegistry) {
fn init_default_registry_inner(registry: &OperatorRegistry) {
opendal_core::services::register_memory_service(registry);

#[cfg(feature = "services-aliyun-drive")]
Expand Down Expand Up @@ -234,6 +230,7 @@ fn init_default_registry_inner(registry: &opendal_core::OperatorRegistry) {
opendal_service_redis::register_redis_service(registry);
}

#[cfg(feature = "register-services")]
#[ctor::ctor]
fn register_default_operator_registry() {
init_default_registry();
Expand Down
Loading