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;