1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
//! A simple, zero-dependency property-binding framework. It was originally designed to use
//! [imgui-rs](https://github.com/imgui-rs/imgui-rs) without drowning in mutable references to
//! everything and constantly fighting with the borrow checker.
//!
//! # Usage
//!
//! [bind()](Property::bind) can be called on an immutable `&Property` to get a mutable binding.
//! The [PropertyBinding] returned by `bind()` [Deref](std::ops::Deref)s to `&T` and
//! [DerefMut](std::ops::DerefMut)s to `&mut T`. The binding needs to be referenced mutably for
//! [DerefMut](std::ops::DerefMut), so rust's borrow checker enforces exclusive mutable access
//! XOR muliple immutable access to the binding itself. The binding unbinds itself when
//! [Drop](std::ops::Drop)ped, so it is automatically freed when it exits scope.
//!
//! ### Example
//!
//! ```rust
//! # mod imgui { pub struct Ui; impl Ui { pub fn slider(&self, _: &str, _: &mut f32) {} } }
//! pub struct PropHaver {
//! pub prop: binder::Property<f32>
//! }
//! fn use_prop(p: &PropHaver, ui: &imgui::Ui) {
//! ui.slider("wow what a cool slider", &mut p.prop.bind());
//! }
//! ```
//!
//! # Safety
//!
//! `Property` owns its value and maintains its own invariants over that value. Properties cannot
//! be bound more than once at the same time. The thread-safe [AtomicBool](std::sync::atomic::AtomicBool)
//! is used to synchronize access to the binding, so it should be fully thread-safe as well.
//!
//! Properties CANNOT be cloned to get more references to the same value. You can use
//! [Rc](std::rc::Rc)`<Property>` or [Arc](std::sync::Arc)`<Property>` for that.
//!
//! # Panic
//!
//! [bind()](Property::bind) will panic if called on a `Property` that's already been bound
//! elsewhere. Use [try_bind()](Property::try_bind) for a non-panicking version.
use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug)]
/// A binding to a [Property]. Allows mutable and immutable access to the value via dereferencing.
/// Exclusive access is enforced by the borrow checker like any other mutable object.
/// `PropertyBinding` automatically unbinds itself when [Drop](std::ops::Drop)ped, and allowing
/// the binding to leave scope and be [Drop](std::ops::Drop)ped is the preferred usage.
///
/// # Safety
///
/// Cannot be cloned, as it is assumed to have an exclusive lock on the property.
/// Thread-safe but not shareable. [Send](core::marker::Send) but not [Sync](core::marker::Sync).
pub struct PropertyBinding<T> {
value: NonNull<T>,
lock: Arc<AtomicBool>,
}
impl<T> Deref for PropertyBinding<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.value.as_ref() }
}
}
impl<T> DerefMut for PropertyBinding<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.value.as_mut() }
}
}
impl<T> Drop for PropertyBinding<T> {
fn drop(&mut self) {
if self.lock.swap(false, Ordering::SeqCst) == false {
panic!("PropertyBinding<{}>: Tried to drop a lock that was already unlocked!", std::any::type_name::<T>())
}
}
}
unsafe impl<T: Send> Send for PropertyBinding<T> {}
/// Used to define a bindable property. See crate-level documentation for more details.
#[derive(Debug)]
pub struct Property<T> {
property: UnsafeCell<T>,
mut_lock: Arc<AtomicBool>
}
impl<T> Property<T> {
/// Creates a new `Property` that owns the given value.
pub fn new(value: T) -> Self {
Property {
property: UnsafeCell::new(value),
mut_lock: Arc::new(AtomicBool::new(false))
}
}
/// Attempts to bind the property.
///
/// # Panics
///
/// This will panic if called while the property is already bound.
pub fn bind(&self) -> PropertyBinding<T> {
let was_locked = self.mut_lock.swap(true, Ordering::SeqCst);
if was_locked {
panic!("PropertyBinding<{}>: Tried to bind a property that was already bound!", std::any::type_name::<T>());
}
PropertyBinding {
value: NonNull::new(self.property.get()).unwrap(),
lock: self.mut_lock.clone()
}
}
/// Safer alternative to [bind](Property::bind).
/// Returns [Ok](core::result::Result::Ok)([PropertyBinding\<T\>](PropertyBinding))
/// upon successful binding and [Err](core::result::Result::Err)(())
/// if the `Property` was already bound.
pub fn try_bind(&self) -> Result<PropertyBinding<T>, ()> {
let was_locked = self.mut_lock.swap(true, Ordering::SeqCst);
if was_locked {
Err(())
}
else {
Ok(PropertyBinding {
value: NonNull::new(self.property.get()).unwrap(),
lock: self.mut_lock.clone()
})
}
}
}
unsafe impl<T: Send> Send for Property<T> {}
unsafe impl<T: Send + Sync> Sync for Property<T> {}
// hack to run compile_fail doctests
// #[cfg(doc)]
// #[doc(hidden)]
// #[path = "../tests/tests.rs"]
// mod tests;