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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#![feature(arc_new_cyclic)]
#![feature(unsize)]
#![feature(coerce_unsized)]
#![feature(test)]
#![feature(min_specialization)]

//! Provides MobX style observables. Example:
//! ```rust
//! use observatory as o;
//! o::init();
//! let first_name = o::observable("William");
//! let last_name = o::observable("Riker");
//! let nickname = o::observable::<Option<&'static str>>(None);
//! // A derivation is run the first time it is created, and the guts of the Derivation type will
//! // detect that the function borrows nickname, first_name, and last_name during that time.
//! # let output_data = std::rc::Rc::new(std::cell::RefCell::new(Vec::new()));
//! # let output_data2 = Clone::clone(&output_data);
//! let display_name = o::derivation_with_ptrs!(
//!     first_name, last_name, nickname;
//!     if let Some(name) = *nickname.borrow() {
//!         format!("{}", name)
//!     } else {
//!         format!("{} {}", *first_name.borrow(), *last_name.borrow())
//!     }
//! );
//! // Prints "William Riker"
//! let logger = o::derivation_with_ptrs!(
//!     display_name;
//! #   { output_data.borrow_mut().push(display_name.borrow().clone());
//!     println!("{}", *display_name.borrow())
//! #   }
//! );
//! // Prints "Will of Yam Riker"
//! first_name.set("Will of Yam");
//! // Prints "Number One"
//! // After executing this function the library will detect that `display_name` didn't need to
//! // borrow first_name or last_name to update its value.
//! nickname.set(Some("Number One"));
//! // Causes no updates, display_name has automatically unsubscribed from updates to last_name.
//! last_name.set("Something else");
//! # assert_eq!(*output_data2.borrow(), vec![format!("William Riker"), format!("Will of Yam Riker"), format!("Number One")]);
//! ```
//! ## Observables
//! The function `observable` returns a value of type `ObservablePtr<T>`.
//! These represent singular pieces of data which can be modified through `ObservablePtr::set` or
//! `ObservablePtr::borrow_mut`. The value itself works like the standard library's `Rc` shared
//! pointer, in that it can be cloned and shared around, as long as it is not sent between threads:
//! ```rust
//! use observatory as o;
//! o::init();
//! let data = o::observable(123);
//! let data2 = Clone::clone(&data);
//! data.set(42);
//! assert_eq!(*data2.borrow_untracked(), 42);
//! ```
//! Notice the use of `borrow_untracked` in the previous example. The normal `ObservablePtr::borrow`
//! is intended to be called from within the body of a derivation and as such will panic outside of
//! that environment.
//! ## Derivations
//! Derivations themselves are a kind of observable, as their result can be
//! observed. However, they do not have `set` or `borrow_mut` functions. Instead, a single function
//! is specified which computes the value the derivation should have. This function is then
//! automatically re-run whenever any of the observables it had borrowed have changed. Here is an
//! example of a derivation:
//! ```rust
//! use observatory as o;
//! o::init();
//! let number /* ObservablePtr<i32> */ = o::observable(3);
//! let number2 = Clone::clone(&number);
//! let squared /* DerivationPtr<i32, [closure type]> */ = o::derivation(move || {
//!     let value = *number2.borrow();
//!     value * value
//! });
//! assert_eq!(*squared.borrow_untracked(), 9);
//! number.set(4);
//! assert_eq!(*squared.borrow_untracked(), 16);
//! ```
//! Cloning all the pointers you need to access from within the closure can be rather tedious, which
//! is where the `derivation_with_ptrs` macro comes in:
//! ```rust
//! use observatory as o;
//! o::init();
//! let number = o::observable(3);
//! let squared = o::derivation_with_ptrs!(
//!     number, extra_example: number; {
//!     let value = *number.borrow();
//!     assert_eq!(*number.borrow(), *extra_example.borrow());
//!     value * value
//! });
//! assert_eq!(*squared.borrow_untracked(), 9);
//! number.set(4);
//! assert_eq!(*squared.borrow_untracked(), 16);
//! ```
//! The `DerivationPtr` struct has two template parameters, one for the return type and another for
//! the type of the function. Because of this, it is not directly possible to store these pointers
//! in a struct or in a vector. To solve this, the function type can be made to be
//! `Box<dyn FnMut() -> T>`, but this would introduce a lot of boilerplate. This is handled for you
//! if you use `derivation_dyn` or `o::derivation_with_pointers_dyn` and the type
//! alias `DerivationDynPtr<T>`:
//! ```rust
//! use observatory as o;
//! o::init();
//! let number = o::observable(3);
//! let squared /* DerivationDynPtr<i32> */ = o::derivation_with_ptrs_dyn!(
//!     number, extra_example: number; {
//!     let value = *number.borrow();
//!     assert_eq!(*number.borrow(), *extra_example.borrow());
//!     value * value
//! });
//! assert_eq!(*squared.borrow_untracked(), 9);
//! number.set(4);
//! assert_eq!(*squared.borrow_untracked(), 16);
//! ```

mod bench;
mod observable;
mod observer;
#[doc(hidden)]
pub mod ptr_util;
mod static_state;
mod tests;

pub use observable::ObservablePtr;
pub use observer::DerivationPtr;
pub use observer::IsUnchanged;
pub use static_state::{init, is_initialized};

pub type DerivationDynPtr<T> = DerivationPtr<T, Box<dyn FnMut() -> T + 'static>>;

pub fn observable<T: 'static>(value: T) -> ObservablePtr<T> {
    ObservablePtr::new(value)
}

pub fn derivation<T: IsUnchanged + 'static, F: FnMut() -> T + 'static>(
    compute_value: F,
) -> DerivationPtr<T, F> {
    DerivationPtr::new(compute_value)
}

pub fn derivation_dyn<T: IsUnchanged + 'static, F: FnMut() -> T + 'static>(
    compute_value: F,
) -> DerivationDynPtr<T> {
    DerivationPtr::new_dyn(compute_value)
}

#[macro_export]
#[doc(hidden)]
macro_rules! __derivation_with_ptrs_parse {
    ($constructor:ident ($($args:tt)*); $($remaining:tt)*) => {
        {
            $crate::ptr_clone!($($args)*);
            $crate::$constructor(move || $($remaining)*)
        }
    };
    ($constructor:ident ($($args:tt)*) $next:tt $($remaining:tt)*) => {
        $crate::__derivation_with_ptrs_parse!($constructor ($($args)*$next) $($remaining)*)
    };
}

#[macro_export]
macro_rules! derivation_with_ptrs {
    ($($args:tt)*) => {
        $crate::__derivation_with_ptrs_parse!(derivation () $($args)*)
    };
}

#[macro_export]
macro_rules! derivation_with_ptrs_dyn {
    ($($args:tt)*) => {
        $crate::__derivation_with_ptrs_parse!(derivation_dyn () $($args)*)
    };
}