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:
234
crates/libremetaverse/src/observable_dictionary.rs
Normal file
234
crates/libremetaverse/src/observable_dictionary.rs
Normal file
@@ -0,0 +1,234 @@
|
||||
//! Thread-safe observable key/value storage.
|
||||
|
||||
use crate::{DictionaryEventAction, Error};
|
||||
use libremetaverse_types::compat::{DictionaryEntry, IEnumerator, Object};
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
type Callback = Arc<dyn Fn(DictionaryEventAction, DictionaryEntry) + Send + Sync>;
|
||||
|
||||
pub struct ObservableDictionary<TKey, TValue> {
|
||||
values: Mutex<HashMap<TKey, Option<TValue>>>,
|
||||
delegates: Mutex<HashMap<DictionaryEventAction, Vec<Callback>>>,
|
||||
}
|
||||
|
||||
impl<TKey, TValue> ObservableDictionary<TKey, TValue>
|
||||
where
|
||||
TKey: Clone + Eq + Hash + Into<Object>,
|
||||
TValue: Clone + PartialEq + Into<Object>,
|
||||
{
|
||||
pub fn new_with_constructor() -> Result<Self, Error> {
|
||||
Self::new_with_capacity(0)
|
||||
}
|
||||
|
||||
pub fn new_with_int32(capacity: i32) -> Result<Self, Error> {
|
||||
let capacity = usize::try_from(capacity).map_err(|_| Error::Argument)?;
|
||||
Self::new_with_capacity(capacity)
|
||||
}
|
||||
|
||||
fn new_with_capacity(capacity: usize) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
values: Mutex::new(HashMap::with_capacity(capacity)),
|
||||
delegates: Mutex::new(HashMap::new()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add(&self, key: TKey, value: Option<TValue>) -> Result<(), Error> {
|
||||
self.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.insert(key.clone(), value.clone());
|
||||
self.fire(DictionaryEventAction::Add, key, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_delegate(
|
||||
&self,
|
||||
action: DictionaryEventAction,
|
||||
callback: Callback,
|
||||
) -> Result<(), Error> {
|
||||
self.delegates
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.entry(action)
|
||||
.or_default()
|
||||
.push(callback);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_delegate(
|
||||
&self,
|
||||
action: DictionaryEventAction,
|
||||
callback: Callback,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(callbacks) = self
|
||||
.delegates
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.get_mut(&action)
|
||||
{
|
||||
callbacks.retain(|registered| !Arc::ptr_eq(registered, &callback));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear(&self) -> Result<(), Error> {
|
||||
let removed: Vec<_> = self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.drain()
|
||||
.collect();
|
||||
for (key, value) in removed {
|
||||
self.fire(DictionaryEventAction::Remove, key, value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, key: TKey) -> Result<bool, Error> {
|
||||
Ok(self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.contains_key(&key))
|
||||
}
|
||||
|
||||
pub fn contains_value(&self, value: Option<TValue>) -> Result<bool, Error> {
|
||||
Ok(self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.values()
|
||||
.any(|candidate| candidate == &value))
|
||||
}
|
||||
|
||||
pub fn find(
|
||||
&self,
|
||||
predicate: Box<dyn Fn(&Option<TValue>) -> bool + Send + Sync>,
|
||||
) -> Result<Option<TValue>, Error> {
|
||||
Ok(self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.values()
|
||||
.find(|value| predicate(value))
|
||||
.cloned()
|
||||
.flatten())
|
||||
}
|
||||
|
||||
pub fn find_all_with_predicate(
|
||||
&self,
|
||||
predicate: Box<dyn Fn(&TKey) -> bool + Send + Sync>,
|
||||
) -> Result<Vec<TKey>, Error> {
|
||||
Ok(self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.keys()
|
||||
.filter(|key| predicate(key))
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn find_all_with_predicate_6d98ccd2(
|
||||
&self,
|
||||
predicate: Box<dyn Fn(&Option<TValue>) -> bool + Send + Sync>,
|
||||
) -> Result<Vec<Option<TValue>>, Error> {
|
||||
Ok(self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.values()
|
||||
.filter(|value| predicate(value))
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn get_enumerator(&self) -> Result<IEnumerator, Error> {
|
||||
Ok(IEnumerator)
|
||||
}
|
||||
|
||||
pub fn remove(&self, key: TKey) -> Result<bool, Error> {
|
||||
let removed = self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.remove(&key);
|
||||
if let Some(value) = removed {
|
||||
self.fire(DictionaryEventAction::Remove, key, value);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn try_get_value(&self, key: TKey, value: &mut Option<TValue>) -> bool {
|
||||
let found = self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.get(&key)
|
||||
.cloned();
|
||||
match found {
|
||||
Some(found) => {
|
||||
*value = found;
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn count(&self) -> i32 {
|
||||
i32::try_from(
|
||||
self.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.len(),
|
||||
)
|
||||
.unwrap_or(i32::MAX)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn item(&self, key: Option<TKey>) -> Option<TValue> {
|
||||
let key = key?;
|
||||
self.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.get(&key)
|
||||
.cloned()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn set_item(&mut self, key: Option<TKey>, value: Option<TValue>) {
|
||||
let Some(key) = key else { return };
|
||||
let action = if self
|
||||
.values
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.insert(key.clone(), value.clone())
|
||||
.is_some()
|
||||
{
|
||||
DictionaryEventAction::Change
|
||||
} else {
|
||||
DictionaryEventAction::Add
|
||||
};
|
||||
self.fire(action, key, value);
|
||||
}
|
||||
|
||||
fn fire(&self, action: DictionaryEventAction, key: TKey, value: Option<TValue>) {
|
||||
let callbacks = self
|
||||
.delegates
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.get(&action)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let entry = DictionaryEntry(key.into(), value.map_or(Object::Undefined, Into::into));
|
||||
for callback in callbacks {
|
||||
callback(action, DictionaryEntry(entry.0.clone(), entry.1.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user