drying_paint/
lib.rs

1/* SPDX-License-Identifier: (Apache-2.0 OR MIT OR Zlib) */
2/* Copyright © 2021 Violet Leonard */
3
4//! The name 'drying_paint' comes from the expression "watching paint dry".
5//! This module provides a system to "watch" some values for changes and run
6//! code whenever they change.
7//!
8//! ```rust
9//! use std::{rc::Rc, cell::RefCell};
10//! use drying_paint::{Watcher, Watched, WatcherInit, WatchContext};
11//! // define a type to hold data
12//! struct Content {
13//!     dest: i32,
14//!     source: Watched<i32>,
15//! }
16//!
17//! // define Watcher trait for the type
18//! impl Watcher<'static> for Content {
19//!     fn init(mut init: impl WatcherInit<'static, Self>) {
20//!         // set up a callback that will be re-run when
21//!         // the Watched data changes
22//!         init.watch(|root| {
23//!             root.dest = *root.source;
24//!         });
25//!     }
26//! }
27//! // instantiate the content
28//! let content = Rc::new(RefCell::new(Content {
29//!     dest: 0,
30//!     source: Watched::new(37),
31//! }));
32//! let weak = Rc::downgrade(&content);
33//!
34//! // create the Context
35//! let mut ctx = WatchContext::new();
36//!
37//! // dest was 0 when instantiated
38//! assert_eq!(content.borrow().dest, 0);
39//!
40//! // after adding the watcher, the callback has run (once)
41//! ctx.add_watcher(&weak);
42//! assert_eq!(content.borrow().dest, 37);
43//!
44//! // we can change the "watched" value
45//! *content.borrow_mut().source = 43;
46//! assert_eq!(content.borrow().dest, 37);
47//!
48//! // and it will be updated when we call
49//! // update on the context
50//! ctx.update();
51//! assert_eq!(content.borrow().dest, 43);
52//! ```
53
54#![no_std]
55//#![warn(missing_docs)]
56#![deny(rust_2018_idioms)]
57
58#[cfg(any(test, feature = "std"))]
59extern crate std;
60
61extern crate alloc;
62
63mod context;
64#[cfg(do_cycle_debug)]
65mod cycle_debug;
66mod queue;
67mod sync;
68mod trigger;
69mod watched_core;
70mod watcher;
71
72pub use crate::{
73    context::{DefaultOwner, WatchContext},
74    queue::WatchedQueue,
75    sync::{
76        watched_channel, SendGuard, SyncTrigger, SyncWatchedMeta,
77        WatchedReceiver, WatchedSender,
78    },
79    trigger::{RawWatchArg, WatchArg, WatchName},
80    watched_core::{
81        WatchedCellCore, WatchedCore, WatchedMeta, WatchedValueCore,
82    },
83    watcher::{Watcher, WatcherHolder, WatcherInit},
84};
85
86#[cfg(feature = "std")]
87mod watched;
88#[cfg(feature = "std")]
89pub use crate::watched::{Watched, WatchedCell, WatchedValue};
90
91#[doc = include_str!("../README.md")]
92#[cfg(doctest)]
93pub struct ReadmeDoctests;
94
95#[cfg(all(test, feature = "std"))]
96mod tests {
97    use std::{cell::RefCell, rc::Rc};
98
99    use super::*;
100
101    #[test]
102    fn simple_propogate_core() {
103        struct Content {
104            dest: i32,
105            source: WatchedCore<'static, i32>,
106        }
107
108        impl Watcher<'static> for Content {
109            fn init(mut init: impl WatcherInit<'static, Self>) {
110                init.watch_explicit(|arg, root| {
111                    root.dest = *root.source.get(arg);
112                });
113            }
114        }
115        let content = Rc::new(RefCell::new(Content {
116            dest: 0,
117            source: WatchedCore::new(37),
118        }));
119        let weak = Rc::downgrade(&content);
120
121        let mut ctx = WatchContext::new();
122        assert_eq!(content.borrow().dest, 0);
123        ctx.add_watcher(&weak);
124        assert_eq!(content.borrow().dest, 37);
125        *content.borrow_mut().source.get_mut_external() = 43;
126        assert_eq!(content.borrow().dest, 37);
127        ctx.update();
128        assert_eq!(content.borrow().dest, 43);
129        ctx.update();
130        assert_eq!(content.borrow().dest, 43);
131    }
132
133    #[test]
134    fn simple_propogate() {
135        struct Content {
136            dest: i32,
137            source: Watched<i32>,
138        }
139
140        impl Watcher<'static> for Content {
141            fn init(mut init: impl WatcherInit<'static, Self>) {
142                init.watch(|root| {
143                    root.dest = *root.source;
144                });
145            }
146        }
147        let content = Rc::new(RefCell::new(Content {
148            dest: 0,
149            source: Watched::new(37),
150        }));
151        let weak = Rc::downgrade(&content);
152
153        let mut ctx = WatchContext::new();
154        assert_eq!(content.borrow().dest, 0);
155        ctx.add_watcher(&weak);
156        assert_eq!(content.borrow().dest, 37);
157        *content.borrow_mut().source = 43;
158        assert_eq!(content.borrow().dest, 37);
159        ctx.update();
160        assert_eq!(content.borrow().dest, 43);
161        ctx.update();
162        assert_eq!(content.borrow().dest, 43);
163    }
164
165    #[test]
166    fn double_mut_in_watch() {
167        #[derive(Default)]
168        struct MutsTwice {
169            value: Watched<i32>,
170        }
171
172        impl Watcher<'static> for MutsTwice {
173            fn init(mut init: impl WatcherInit<'static, Self>) {
174                init.watch(|root| {
175                    root.value += 1;
176                    root.value += 1;
177                });
178            }
179        }
180
181        let content = Rc::new(RefCell::new(MutsTwice {
182            value: Watched::new(0_i32),
183        }));
184        let weak = Rc::downgrade(&content);
185
186        let mut ctx = WatchContext::new();
187        ctx.set_frame_limit(Some(100));
188        ctx.add_watcher(&weak);
189        assert_eq!(*content.borrow().value, 2);
190        ctx.update();
191        assert_eq!(*content.borrow().value, 2);
192        *content.borrow_mut().value = 41;
193        ctx.update();
194        assert_eq!(*content.borrow().value, 43);
195    }
196
197    #[test]
198    fn send_received_by_watch() {
199        use std::sync::mpsc::{channel, Receiver};
200
201        struct Content {
202            dest: Option<i32>,
203            source: WatchedReceiver<Receiver<i32>>,
204        }
205
206        impl Watcher<'static> for Content {
207            fn init(mut init: impl WatcherInit<'static, Self>) {
208                init.watch_explicit(|arg, root| {
209                    root.dest = root.source.get(arg).try_recv().ok();
210                });
211            }
212        }
213
214        let (sender, receiver) = watched_channel(channel());
215
216        let content = Rc::new(RefCell::new(Content {
217            dest: None,
218            source: receiver,
219        }));
220        let weak = Rc::downgrade(&content);
221
222        let mut ctx = WatchContext::new();
223        ctx.add_watcher(&weak);
224        assert_eq!(content.borrow().dest, None);
225        let thread_handle = std::thread::spawn(move || {
226            sender.sender().send(4812).unwrap();
227        });
228        thread_handle.join().unwrap();
229        ctx.update();
230        assert_eq!(content.borrow().dest, Some(4812));
231    }
232}