set_compare.rs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. use crate::write::Writable;
  2. use std::hash::Hash;
  3. use crate::read::Readable;
  4. use dioxus_core::prelude::*;
  5. use futures_util::StreamExt;
  6. use generational_box::{Storage, UnsyncStorage};
  7. use crate::{CopyValue, ReadOnlySignal, Signal, SignalData};
  8. use rustc_hash::FxHashMap;
  9. /// An object that can efficiently compare a value to a set of values.
  10. #[derive(Debug)]
  11. pub struct SetCompare<R: 'static, S: Storage<SignalData<bool>> = UnsyncStorage> {
  12. subscribers: CopyValue<FxHashMap<R, Signal<bool, S>>>,
  13. }
  14. impl<R: Eq + Hash> SetCompare<R> {
  15. /// Creates a new [`SetCompare`] which efficiently tracks when a value changes to check if it is equal to a set of values.
  16. ///
  17. /// Generally, you shouldn't need to use this hook. Instead you can use [`crate::Memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
  18. #[track_caller]
  19. pub fn new(f: impl FnMut() -> R + 'static) -> SetCompare<R> {
  20. Self::new_maybe_sync(f)
  21. }
  22. }
  23. impl<R: Eq + Hash, S: Storage<SignalData<bool>>> SetCompare<R, S> {
  24. /// Creates a new [`SetCompare`] that may be `Sync + Send` which efficiently tracks when a value changes to check if it is equal to a set of values.
  25. ///
  26. /// Generally, you shouldn't need to use this hook. Instead you can use [`crate::Memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
  27. #[track_caller]
  28. pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> SetCompare<R> {
  29. let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
  30. CopyValue::new(FxHashMap::default());
  31. let mut previous = CopyValue::new(None);
  32. let mut recompute = move || {
  33. let subscribers = subscribers.read();
  34. let mut previous = previous.write();
  35. if let Some(previous) = previous.take() {
  36. if let Some(mut value) = subscribers.get(&previous).cloned() {
  37. *value.write() = false;
  38. }
  39. }
  40. let current = f();
  41. if let Some(mut value) = subscribers.get(&current).cloned() {
  42. *value.write() = true;
  43. }
  44. *previous = Some(current);
  45. };
  46. let (rc, mut changed) = ReactiveContext::new();
  47. spawn(async move {
  48. loop {
  49. // Recompute the value
  50. rc.reset_and_run_in(&mut recompute);
  51. // Wait for context to change
  52. let _ = changed.next().await;
  53. }
  54. });
  55. SetCompare { subscribers }
  56. }
  57. /// Returns a signal which is true when the value is equal to the value passed to this function.
  58. pub fn equal(&mut self, value: R) -> ReadOnlySignal<bool, S> {
  59. let subscribers = self.subscribers.write();
  60. match subscribers.get(&value) {
  61. Some(&signal) => signal.into(),
  62. None => {
  63. drop(subscribers);
  64. let mut subscribers = self.subscribers.write();
  65. let signal = Signal::new_maybe_sync(false);
  66. subscribers.insert(value, signal);
  67. signal.into()
  68. }
  69. }
  70. }
  71. }
  72. impl<R: 'static, S: Storage<SignalData<bool>>> PartialEq for SetCompare<R, S> {
  73. fn eq(&self, other: &Self) -> bool {
  74. self.subscribers == other.subscribers
  75. }
  76. }
  77. impl<R, S: Storage<SignalData<bool>>> Clone for SetCompare<R, S> {
  78. fn clone(&self) -> Self {
  79. *self
  80. }
  81. }
  82. impl<R, S: Storage<SignalData<bool>>> Copy for SetCompare<R, S> {}