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
235 lines
6.7 KiB
Rust
235 lines
6.7 KiB
Rust
//! 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()));
|
|
}
|
|
}
|
|
}
|