lib.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. use std::{
  2. cell::{Ref, RefCell},
  3. collections::HashMap,
  4. hash::Hash,
  5. marker::PhantomData,
  6. rc::Rc,
  7. };
  8. pub use atomfamily::*;
  9. pub use atoms::*;
  10. pub use ecs::*;
  11. use error::*;
  12. pub use hooks::*;
  13. pub use hooks::*;
  14. pub use root::*;
  15. pub use selector::*;
  16. pub use selectorfamily::*;
  17. pub use traits::*;
  18. pub use utils::*;
  19. mod tracingimmap;
  20. mod traits {
  21. use dioxus_core::prelude::Context;
  22. use super::*;
  23. pub trait MapKey: PartialEq + Hash + 'static {}
  24. impl<T: PartialEq + Hash + 'static> MapKey for T {}
  25. pub trait AtomValue: PartialEq + 'static {}
  26. impl<T: PartialEq + 'static> AtomValue for T {}
  27. // Atoms, selectors, and their family variants are readable
  28. pub trait Readable<T: AtomValue>: Sized + Copy {
  29. fn use_read<'a, P: 'static>(self, ctx: Context<'a, P>) -> &'a T {
  30. hooks::use_read(&ctx, self)
  31. }
  32. // This returns a future of the value
  33. // If the atom is currently pending, that future will resolve to pending
  34. // If the atom is currently ready, the future will immediately resolve
  35. // if the atom switches from ready to pending, the component will re-run, returning a pending future
  36. fn use_read_async<'a, P>(self, ctx: Context<'a, P>) -> &'a T {
  37. todo!()
  38. }
  39. fn initialize(self, api: &RecoilRoot) -> T;
  40. // We use the Raw Ptr to the atom
  41. // TODO: Make sure atoms with the same definitions don't get merged together. I don't think they do, but double check
  42. fn static_id(self) -> u32;
  43. }
  44. // Only atoms and atom families are writable
  45. // Selectors and selector families are not
  46. pub trait Writable<T: AtomValue>: Readable<T> + Sized {
  47. fn use_read_write<'a, P>(self, ctx: Context<'a, P>) -> (&'a T, &'a Rc<dyn Fn(T)>) {
  48. todo!()
  49. }
  50. fn use_write<'a, P>(self, ctx: Context<'a, P>) -> &'a Rc<dyn Fn(T)> {
  51. todo!()
  52. }
  53. }
  54. }
  55. mod atoms {
  56. use super::*;
  57. // Currently doesn't do anything, but will eventually add effects, id, serialize/deserialize keys, etc
  58. // Doesn't currently allow async values, but the goal is to eventually enable them
  59. pub struct AtomBuilder {}
  60. pub type Atom<T> = fn(&mut AtomBuilder) -> T;
  61. // impl<T: AtomValue> Readable<T> for Atom<T> {}
  62. impl<T: AtomValue> Readable<T> for &'static Atom<T> {
  63. fn static_id(self) -> u32 {
  64. self as *const _ as u32
  65. }
  66. fn initialize(self, api: &RecoilRoot) -> T {
  67. let mut builder = AtomBuilder {};
  68. let p = self(&mut builder);
  69. p
  70. }
  71. }
  72. impl<T: AtomValue> Writable<T> for &'static Atom<T> {}
  73. mod compilests {
  74. use super::*;
  75. use dioxus_core::prelude::Context;
  76. fn _test(ctx: Context<()>) {
  77. const EXAMPLE_ATOM: Atom<i32> = |_| 10;
  78. // ensure that atoms are both read and write
  79. let _ = use_read(&ctx, &EXAMPLE_ATOM);
  80. let _ = use_read_write(&ctx, &EXAMPLE_ATOM);
  81. let _ = use_write(&ctx, &EXAMPLE_ATOM);
  82. }
  83. }
  84. }
  85. mod atomfamily {
  86. use super::*;
  87. pub trait FamilyCollection<K, V> {}
  88. impl<K, V> FamilyCollection<K, V> for HashMap<K, V> {}
  89. use im_rc::HashMap as ImHashMap;
  90. /// AtomHashMaps provide an efficient way of maintaing collections of atoms.
  91. ///
  92. /// Under the hood, AtomHashMaps uses [IM](https://www.rust-lang.org)'s immutable HashMap implementation to lazily
  93. /// clone data as it is modified.
  94. ///
  95. ///
  96. ///
  97. ///
  98. ///
  99. ///
  100. pub type AtomHashMap<K, V> = fn(&mut ImHashMap<K, V>);
  101. pub trait AtomFamilySelector<K: MapKey, V: AtomValue + Clone> {
  102. fn select(&'static self, k: &K) -> AtomMapSelection<K, V> {
  103. todo!()
  104. }
  105. }
  106. impl<K: MapKey, V: AtomValue + Clone> AtomFamilySelector<K, V> for AtomHashMap<K, V> {
  107. fn select(&'static self, k: &K) -> AtomMapSelection<K, V> {
  108. todo!()
  109. }
  110. }
  111. pub struct AtomMapSelection<'a, K: MapKey, V: AtomValue> {
  112. root: &'static AtomHashMap<K, V>,
  113. key: &'a K,
  114. }
  115. impl<'a, K: MapKey, V: AtomValue> Readable<V> for &AtomMapSelection<'a, K, V> {
  116. fn static_id(self) -> u32 {
  117. todo!()
  118. }
  119. fn initialize(self, api: &RecoilRoot) -> V {
  120. todo!()
  121. // let mut builder = AtomBuilder {};
  122. // let p = self(&mut builder);
  123. // p
  124. }
  125. }
  126. impl<'a, K: MapKey, T: AtomValue> Writable<T> for &AtomMapSelection<'a, K, T> {}
  127. mod compiletests {
  128. use dioxus_core::prelude::Context;
  129. use super::*;
  130. const Titles: AtomHashMap<u32, &str> = |map| {};
  131. fn test(ctx: Context<()>) {
  132. let title = Titles.select(&10).use_read(ctx);
  133. let t2 = use_read(&ctx, &Titles.select(&10));
  134. }
  135. }
  136. }
  137. mod selector {
  138. use super::*;
  139. pub struct SelectorBuilder {}
  140. impl SelectorBuilder {
  141. pub fn get<T: AtomValue>(&self, t: impl Readable<T>) -> &T {
  142. todo!()
  143. }
  144. }
  145. pub type Selector<T> = fn(&mut SelectorBuilder) -> T;
  146. impl<T: AtomValue> Readable<T> for &'static Selector<T> {
  147. fn static_id(self) -> u32 {
  148. todo!()
  149. }
  150. fn initialize(self, api: &RecoilRoot) -> T {
  151. todo!()
  152. }
  153. }
  154. pub struct SelectorFamilyBuilder {}
  155. impl SelectorFamilyBuilder {
  156. pub fn get<T: AtomValue>(&self, t: impl Readable<T>) -> &T {
  157. todo!()
  158. }
  159. }
  160. }
  161. mod selectorfamily {
  162. use super::*;
  163. // pub trait SelectionSelector<K, V> {
  164. // fn select(&self, k: &K) -> CollectionSelection<K, V> {
  165. // todo!()
  166. // }
  167. // }
  168. // impl<K, V, F> SelectionSelector<K, V> for AtomFamily<K, V, F> {}
  169. /// Create a new value as a result of a combination of previous values
  170. /// If you need to return borrowed data, check out [`SelectorFamilyBorrowed`]
  171. pub type SelectorFamily<Key, Value> = fn(&mut SelectorFamilyBuilder, Key) -> Value;
  172. impl<K, V: AtomValue> Readable<V> for &'static SelectorFamily<K, V> {
  173. fn static_id(self) -> u32 {
  174. todo!()
  175. }
  176. fn initialize(self, api: &RecoilRoot) -> V {
  177. todo!()
  178. }
  179. }
  180. /// Borrowed selector families are – surprisingly - discouraged.
  181. /// This is because it's not possible safely memoize these values without keeping old versions around.
  182. ///
  183. /// However, it does come in handy to borrow the contents of an item without re-rendering child components.
  184. pub type SelectorFamilyBorrowed<Key, Value> =
  185. for<'a> fn(&'a mut SelectorFamilyBuilder, Key) -> &'a Value;
  186. // impl<'a, K, V: 'a> SelectionSelector<K, V> for fn(&'a mut SelectorFamilyBuilder, K) -> V {}
  187. }
  188. mod root {
  189. use std::{
  190. any::{Any, TypeId},
  191. collections::{HashSet, VecDeque},
  192. iter::FromIterator,
  193. sync::atomic::{AtomicU32, AtomicUsize},
  194. };
  195. use super::*;
  196. // use generational_arena::Index as ConsumerId;
  197. type AtomId = u32;
  198. type ConsumerId = u32;
  199. pub type RecoilContext = RefCell<RecoilRoot>;
  200. // Sometimes memoization means we don't need to re-render components that holds "correct values"
  201. // IE we consider re-render more expensive than keeping the old value around.
  202. // We *could* unsafely overwrite this slot, but that's just **asking** for UB (holding a &mut while & is held in components)
  203. //
  204. // Instead, we choose to let the hook itself hold onto the Rc<T> by not forcing a render when T is the same.
  205. // Whenever the component needs to be re-rendered for other reasons, the "get" method will automatically update the Rc<T> to the most recent one.
  206. pub struct RecoilRoot {
  207. nodes: RefCell<HashMap<AtomId, Slot>>,
  208. consumer_map: HashMap<ConsumerId, AtomId>,
  209. }
  210. struct Slot {
  211. type_id: TypeId,
  212. source: AtomId,
  213. value: Rc<dyn Any>,
  214. consumers: HashMap<ConsumerId, Rc<dyn Fn()>>,
  215. dependents: HashSet<AtomId>,
  216. }
  217. static NEXT_ID: AtomicU32 = AtomicU32::new(0);
  218. fn next_consumer_id() -> u32 {
  219. NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
  220. }
  221. impl RecoilRoot {
  222. pub(crate) fn new() -> Self {
  223. Self {
  224. nodes: Default::default(),
  225. consumer_map: Default::default(),
  226. }
  227. }
  228. pub fn subscribe<T: AtomValue>(
  229. &mut self,
  230. readable: impl Readable<T>,
  231. receiver_fn: Rc<dyn Fn()>,
  232. ) -> ConsumerId {
  233. let consumer_id = next_consumer_id();
  234. let atom_id = readable.static_id();
  235. log::debug!("Subscribing consumer to atom {} {}", consumer_id, atom_id);
  236. let mut nodes = self.nodes.borrow_mut();
  237. let slot = nodes.get_mut(&atom_id).unwrap();
  238. slot.consumers.insert(consumer_id, receiver_fn);
  239. self.consumer_map.insert(consumer_id, atom_id);
  240. consumer_id
  241. }
  242. pub fn unsubscribe(&mut self, consumer_id: ConsumerId) {
  243. let atom_id = self.consumer_map.get(&consumer_id).unwrap();
  244. let mut nodes = self.nodes.borrow_mut();
  245. let slot = nodes.get_mut(&atom_id).unwrap();
  246. slot.consumers.remove(&consumer_id);
  247. }
  248. /// Directly get the *slot*
  249. /// All Atoms are held in slots (an Rc)
  250. ///
  251. ///
  252. pub fn try_get_raw<T: AtomValue>(&self, readable: impl Readable<T>) -> Result<Rc<T>> {
  253. let atom_id = readable.static_id();
  254. let mut nodes = self.nodes.borrow_mut();
  255. if !nodes.contains_key(&atom_id) {
  256. let value = Slot {
  257. type_id: TypeId::of::<T>(),
  258. source: atom_id,
  259. value: Rc::new(readable.initialize(self)),
  260. consumers: Default::default(),
  261. dependents: Default::default(),
  262. };
  263. nodes.insert(atom_id, value);
  264. }
  265. let out = nodes
  266. .get(&atom_id)
  267. .unwrap()
  268. .value
  269. .clone()
  270. .downcast::<T>()
  271. .unwrap();
  272. Ok(out)
  273. }
  274. pub fn try_set<T: AtomValue>(
  275. &mut self,
  276. writable: impl Writable<T>,
  277. new_val: T,
  278. ) -> crate::error::Result<()> {
  279. let atom_id = writable.static_id();
  280. self.set_by_id(atom_id, new_val);
  281. Ok(())
  282. }
  283. // A slightly dangerous method to manually overwrite any slot given an AtomId
  284. pub(crate) fn set_by_id<T: AtomValue>(&mut self, atom_id: AtomId, new_val: T) {
  285. let mut nodes = self.nodes.borrow_mut();
  286. let consumers = match nodes.get_mut(&atom_id) {
  287. Some(slot) => {
  288. slot.value = Rc::new(new_val);
  289. &slot.consumers
  290. }
  291. None => {
  292. let value = Slot {
  293. type_id: TypeId::of::<T>(),
  294. source: atom_id,
  295. value: Rc::new(new_val),
  296. // value: Rc::new(writable.initialize(self)),
  297. consumers: Default::default(),
  298. dependents: Default::default(),
  299. };
  300. nodes.insert(atom_id, value);
  301. &nodes.get(&atom_id).unwrap().consumers
  302. }
  303. };
  304. for (id, consumer_fn) in consumers {
  305. log::debug!("triggering selector {}", id);
  306. consumer_fn();
  307. }
  308. }
  309. }
  310. }
  311. mod hooks {
  312. use super::*;
  313. use dioxus_core::{hooks::use_ref, prelude::Context, virtual_dom::Scoped};
  314. pub fn use_init_recoil_root<P>(ctx: Context<P>, cfg: impl Fn(())) {
  315. ctx.use_create_context(move || RefCell::new(RecoilRoot::new()))
  316. }
  317. /// Gain access to the recoil API directly - set, get, modify, everything
  318. /// This is the foundational hook in which read/write/modify are built on
  319. ///
  320. /// This does not subscribe the component to *any* updates
  321. ///
  322. /// You can use this method to create controllers that perform much more complex actions than set/get
  323. /// However, be aware that "getting" values through this hook will not subscribe the component to any updates.
  324. pub fn use_recoil_api<'a>(ctx: &impl Scoped<'a>) -> &'a Rc<RecoilContext> {
  325. ctx.use_context::<RecoilContext>()
  326. }
  327. pub fn use_write<'a, T: AtomValue>(
  328. ctx: &impl Scoped<'a>,
  329. // todo: this shouldn't need to be static
  330. writable: impl Writable<T>,
  331. ) -> &'a Rc<dyn Fn(T)> {
  332. let api = use_recoil_api(ctx);
  333. ctx.use_hook(
  334. move || {
  335. let api = api.clone();
  336. let raw_id = writable.static_id();
  337. Rc::new(move |new_val| {
  338. //
  339. log::debug!("setting new value ");
  340. let mut api = api.as_ref().borrow_mut();
  341. // api.try_set(writable, new_val).expect("failed to set");
  342. api.set_by_id(raw_id, new_val);
  343. }) as Rc<dyn Fn(T)>
  344. },
  345. move |hook| &*hook,
  346. |hook| {},
  347. )
  348. }
  349. /// Read the atom and get the Rc directly to the Atom's slot
  350. /// This is useful if you need the memoized Atom value. However, Rc<T> is not as easy to
  351. /// work with as
  352. pub fn use_read_raw<'a, T: AtomValue>(
  353. ctx: &impl Scoped<'a>,
  354. readable: impl Readable<T>,
  355. ) -> &'a Rc<T> {
  356. struct ReadHook<T> {
  357. value: Rc<T>,
  358. consumer_id: u32,
  359. }
  360. let api = use_recoil_api(ctx);
  361. ctx.use_hook(
  362. move || {
  363. let mut api = api.as_ref().borrow_mut();
  364. let update = ctx.schedule_update();
  365. let val = api.try_get_raw(readable).unwrap();
  366. let id = api.subscribe(readable, update);
  367. ReadHook {
  368. value: val,
  369. consumer_id: id,
  370. }
  371. },
  372. move |hook| {
  373. let api = api.as_ref().borrow();
  374. let val = api.try_get_raw(readable).unwrap();
  375. hook.value = val;
  376. &hook.value
  377. },
  378. move |hook| {
  379. let mut api = api.as_ref().borrow_mut();
  380. api.unsubscribe(hook.consumer_id);
  381. },
  382. )
  383. }
  384. ///
  385. pub fn use_read<'a, T: AtomValue>(ctx: &impl Scoped<'a>, readable: impl Readable<T>) -> &'a T {
  386. use_read_raw(ctx, readable).as_ref()
  387. }
  388. /// # Use an atom in both read and write
  389. ///
  390. /// This method is only available for atoms and family selections (not selectors).
  391. ///
  392. /// This is equivalent to calling both `use_read` and `use_write`, but saves you the hassle and repitition
  393. ///
  394. /// ## Example
  395. ///
  396. /// ```
  397. /// const Title: Atom<&str> = |_| "hello";
  398. /// let (title, set_title) = use_read_write(ctx, &Title);
  399. ///
  400. /// // equivalent to:
  401. /// let (title, set_title) = (use_read(ctx, &Title), use_write(ctx, &Title));
  402. /// ```
  403. pub fn use_read_write<'a, T: AtomValue + 'static>(
  404. ctx: &impl Scoped<'a>,
  405. writable: impl Writable<T>,
  406. ) -> (&'a T, &'a Rc<dyn Fn(T)>) {
  407. (use_read(ctx, writable), use_write(ctx, writable))
  408. }
  409. /// # Modify an atom without using `use_read`.
  410. ///
  411. /// Occasionally, a component might want to write to an atom without subscribing to its changes. `use_write` does not
  412. /// provide this functionality, so `use_modify` exists to gain access to the current atom value while setting it.
  413. ///
  414. /// ## Notes
  415. ///
  416. /// Do note that this hook can only be used with Atoms where T: Clone since we actually clone the current atom to make
  417. /// it mutable.
  418. ///
  419. /// Also note that you need to stack-borrow the closure since the modify closure expects an &dyn Fn. If we made it
  420. /// a static type, it wouldn't be possible to use the `modify` closure more than once (two closures always are different)
  421. ///
  422. /// ## Example
  423. ///
  424. /// ```ignore
  425. /// let modify_atom = use_modify(ctx, Atom);
  426. ///
  427. /// modify_atom(&|a| *a += 1)
  428. /// ```
  429. pub fn use_modify<'a, T: AtomValue + 'static + Clone, P>(
  430. ctx: Context<'a, P>,
  431. writable: impl Writable<T>,
  432. ) -> impl Fn(&dyn Fn()) {
  433. |_| {}
  434. }
  435. /// Use a family collection directly
  436. /// !! Any changes to the family will cause this subscriber to update
  437. /// Try not to put this at the very top-level of your app.
  438. pub fn use_read_family<'a, K, V, P>(
  439. ctx: Context<'a, P>,
  440. t: &AtomHashMap<K, V>,
  441. ) -> &'a im_rc::HashMap<K, V> {
  442. todo!()
  443. }
  444. }
  445. mod ecs {
  446. use super::*;
  447. pub struct Blah<K, V> {
  448. _p: PhantomData<(K, V)>,
  449. }
  450. pub type EcsModel<K, Ty> = fn(Blah<K, Ty>);
  451. }
  452. mod utils {
  453. use super::*;
  454. use dioxus_core::prelude::*;
  455. pub struct RecoilApi {}
  456. /// This tiny util wraps your main component with the initializer for the recoil root.
  457. /// This is useful for small programs and the examples in this crate
  458. pub fn RecoilApp<T: 'static>(
  459. root: impl for<'a> Fn(Context<'a, T>) -> VNode,
  460. ) -> impl for<'a> Fn(Context<'a, T>) -> VNode {
  461. move |ctx| {
  462. use_init_recoil_root(ctx, |_| {});
  463. root(ctx)
  464. }
  465. }
  466. }
  467. mod compiletests {}
  468. pub mod error {
  469. use thiserror::Error as ThisError;
  470. pub type Result<T, E = Error> = std::result::Result<T, E>;
  471. #[derive(ThisError, Debug)]
  472. pub enum Error {
  473. #[error("Fatal Internal Error: {0}")]
  474. FatalInternal(&'static str),
  475. // #[error("Context is missing")]
  476. // MissingSharedContext,
  477. // #[error("No event to progress")]
  478. // NoEvent,
  479. // #[error("Wrong Properties Type")]
  480. // WrongProps,
  481. // #[error("Base scope has not been mounted yet")]
  482. // NotMounted,
  483. // #[error("I/O Error: {0}")]
  484. // BorrowMut(#[from] std::),
  485. // #[error("eee Error: {0}")]
  486. // IO(#[from] core::result::),
  487. #[error("I/O Error: {0}")]
  488. IO(#[from] std::io::Error),
  489. #[error(transparent)]
  490. Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
  491. }
  492. }