Complete first release candidate audit (#107)
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled
This commit is contained in:
164
crates/libremetaverse/src/service_collection.rs
Normal file
164
crates/libremetaverse/src/service_collection.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
//! Runtime-neutral singleton registration for native `GridClient` composition.
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::{Error, GridClient, Settings};
|
||||
|
||||
/// Factory stored by service collections for lazy `GridClient` construction.
|
||||
pub type GridClientFactory = Arc<dyn Fn() -> Result<GridClient, Error> + Send + Sync>;
|
||||
|
||||
/// Object-safe service collection contract used by the mapped DI extension.
|
||||
pub trait IServiceCollection: Send {
|
||||
/// Registers the concrete `GridClient` singleton factory.
|
||||
fn add_grid_client_singleton(&mut self, factory: GridClientFactory) -> Result<(), Error>;
|
||||
|
||||
/// Registers `IGridClient` as an alias of the concrete singleton.
|
||||
fn add_i_grid_client_alias(&mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Small native service collection suitable for applications without a DI crate.
|
||||
#[derive(Default)]
|
||||
pub struct ServiceCollection {
|
||||
client_factory: Option<GridClientFactory>,
|
||||
client: Mutex<Option<GridClient>>,
|
||||
interface_alias: bool,
|
||||
}
|
||||
|
||||
impl ServiceCollection {
|
||||
/// Resolves the lazily-created concrete client singleton.
|
||||
pub fn resolve_grid_client(&self) -> Result<GridClient, Error> {
|
||||
let mut singleton = self
|
||||
.client
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
if let Some(client) = singleton.as_ref() {
|
||||
return Ok(client.clone());
|
||||
}
|
||||
let factory = self
|
||||
.client_factory
|
||||
.as_ref()
|
||||
.ok_or(Error::InvalidOperation)?;
|
||||
let client = factory()?;
|
||||
*singleton = Some(client.clone());
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
/// Resolves the interface registration to the same native singleton.
|
||||
pub fn resolve_i_grid_client(&self) -> Result<GridClient, Error> {
|
||||
if !self.interface_alias {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
self.resolve_grid_client()
|
||||
}
|
||||
}
|
||||
|
||||
impl IServiceCollection for ServiceCollection {
|
||||
fn add_grid_client_singleton(&mut self, factory: GridClientFactory) -> Result<(), Error> {
|
||||
if self.client_factory.is_some() {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
self.client_factory = Some(factory);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_i_grid_client_alias(&mut self) -> Result<(), Error> {
|
||||
if self.client_factory.is_none() || self.interface_alias {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
self.interface_alias = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_grid_client(
|
||||
mut services: Box<dyn IServiceCollection>,
|
||||
configure: Option<Box<dyn Fn(&mut Settings) + Send + Sync>>,
|
||||
) -> Result<Box<dyn IServiceCollection>, Error> {
|
||||
let configure: Option<Arc<dyn Fn(&mut Settings) + Send + Sync>> = configure.map(Arc::from);
|
||||
let factory: GridClientFactory = Arc::new(move || {
|
||||
let mut settings = Settings::default();
|
||||
if let Some(configure) = &configure {
|
||||
configure(&mut settings);
|
||||
}
|
||||
GridClient::builder()
|
||||
.with_settings(settings)
|
||||
.build()
|
||||
.map_err(Into::into)
|
||||
});
|
||||
services.add_grid_client_singleton(factory)?;
|
||||
services.add_i_grid_client_alias()?;
|
||||
Ok(services)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
|
||||
#[derive(Default)]
|
||||
struct RecordingCollection {
|
||||
factory: Arc<Mutex<Option<GridClientFactory>>>,
|
||||
alias_registered: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl IServiceCollection for RecordingCollection {
|
||||
fn add_grid_client_singleton(&mut self, factory: GridClientFactory) -> Result<(), Error> {
|
||||
*self
|
||||
.factory
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(factory);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_i_grid_client_alias(&mut self) -> Result<(), Error> {
|
||||
self.alias_registered.store(true, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registers_one_lazy_client_and_interface_alias() {
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let observed = Arc::clone(&calls);
|
||||
let collection = RecordingCollection::default();
|
||||
let recorded_factory = Arc::clone(&collection.factory);
|
||||
let alias_registered = Arc::clone(&collection.alias_registered);
|
||||
let _services = add_grid_client(
|
||||
Box::new(collection),
|
||||
Some(Box::new(move |settings| {
|
||||
observed.fetch_add(1, Ordering::Relaxed);
|
||||
settings.default_effect_color.r = 0.25;
|
||||
})),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(alias_registered.load(Ordering::Relaxed));
|
||||
assert_eq!(calls.load(Ordering::Relaxed), 0);
|
||||
|
||||
let factory = recorded_factory
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.clone()
|
||||
.expect("GridClient factory must be registered");
|
||||
let client = factory().unwrap();
|
||||
assert_eq!(calls.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(client.settings_ref().default_effect_color.r, 0.25);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concrete_collection_resolves_same_runtime() {
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let observed = Arc::clone(&calls);
|
||||
let mut services = ServiceCollection::default();
|
||||
services
|
||||
.add_grid_client_singleton(Arc::new(move || {
|
||||
observed.fetch_add(1, Ordering::Relaxed);
|
||||
GridClient::builder().build().map_err(Into::into)
|
||||
}))
|
||||
.unwrap();
|
||||
services.add_i_grid_client_alias().unwrap();
|
||||
let concrete = services.resolve_grid_client().unwrap();
|
||||
let interface = services.resolve_i_grid_client().unwrap();
|
||||
assert_eq!(concrete.settings_ref(), interface.settings_ref());
|
||||
assert_eq!(calls.load(Ordering::Relaxed), 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user