vertigo/computed/
computed_box.rs1use std::hash::Hash;
2use std::rc::Rc;
3
4use crate::{
5 computed::{graph_id::GraphId, GraphValue},
6 render::{render_list, render_value, render_value_option},
7 struct_mut::ValueMut,
8 DomNode, DropResource, Value,
9};
10
11use super::context::Context;
12
13pub struct Computed<T: Clone> {
56 inner: Rc<GraphValue<T>>,
57}
58
59impl<T: Clone + 'static> Clone for Computed<T> {
60 fn clone(&self) -> Self {
61 Computed {
62 inner: self.inner.clone(),
63 }
64 }
65}
66
67impl<T: Clone + 'static> PartialEq for Computed<T> {
68 fn eq(&self, other: &Self) -> bool {
69 self.id() == other.id()
70 }
71}
72
73impl<T: Clone + 'static> Computed<T> {
74 pub fn from<F: Fn(&Context) -> T + 'static>(get_value: F) -> Computed<T> {
76 Computed {
77 inner: GraphValue::new(true, move |context| get_value(context)),
78 }
79 }
80
81 pub fn get(&self, context: &Context) -> T {
83 self.inner.get_value(context)
84 }
85
86 pub fn map<K: Clone + 'static, F: 'static + Fn(T) -> K>(&self, fun: F) -> Computed<K> {
88 Computed::from({
89 let myself = self.clone();
90 move |context| fun(myself.get(context))
91 })
92 }
93
94 pub fn id(&self) -> GraphId {
95 self.inner.id()
96 }
97
98 pub fn subscribe_all<R: 'static, F: Fn(T) -> R + 'static>(self, callback: F) -> DropResource {
102 let resource_box = ValueMut::new(None);
103
104 let graph_value = GraphValue::new(false, move |context| {
105 let value = self.get(context);
106
107 let resource = callback(value);
108 resource_box.change(move |inner| {
109 *inner = Some(resource);
110 });
111 });
112
113 let context = Context::new();
114 graph_value.get_value(&context);
115 let _ = context;
116
117 DropResource::from_struct(graph_value)
118 }
119}
120
121impl<T: Clone + PartialEq + 'static> Computed<T> {
122 pub fn subscribe<R: 'static, F: Fn(T) -> R + 'static>(self, callback: F) -> DropResource {
127 let prev_value = ValueMut::new(None);
128
129 let resource_box = ValueMut::new(None);
130
131 let graph_value = GraphValue::new(false, move |context| {
132 let value = self.get(context);
133
134 let should_update = prev_value.set_if_changed(Some(value.clone()));
135
136 if should_update {
137 let resource = callback(value);
138 resource_box.change(move |inner| {
139 *inner = Some(resource);
140 });
141 }
142 });
143
144 let context = Context::new();
145 graph_value.get_value(&context);
146 let _ = context;
147
148 DropResource::from_struct(graph_value)
149 }
150
151 pub fn render_value(&self, render: impl Fn(T) -> DomNode + 'static) -> DomNode {
153 render_value(self.clone(), render)
154 }
155
156 pub fn render_value_option(&self, render: impl Fn(T) -> Option<DomNode> + 'static) -> DomNode {
158 render_value_option(self.clone(), render)
159 }
160}
161
162impl<T: Clone + PartialEq + 'static, L: IntoIterator<Item = T> + Clone + 'static> Computed<L> {
163 pub fn render_list<K: Eq + Hash>(
165 &self,
166 get_key: impl Fn(&T) -> K + 'static,
167 render: impl Fn(&T) -> DomNode + 'static,
168 ) -> DomNode {
169 let list = self.map(|inner| inner.into_iter().collect::<Vec<_>>());
170 render_list(list, get_key, render)
171 }
172}
173
174impl<T: Clone + 'static> From<Value<T>> for Computed<T> {
175 fn from(val: Value<T>) -> Self {
176 val.to_computed()
177 }
178}
179
180impl<T: Clone + 'static> From<T> for Computed<T> {
181 fn from(value: T) -> Self {
182 Value::new(value).to_computed()
183 }
184}
185
186impl<T: Clone + 'static> From<&T> for Computed<T> {
187 fn from(value: &T) -> Self {
188 Value::new(value.clone()).to_computed()
189 }
190}
191
192impl From<&str> for Computed<String> {
193 fn from(value: &str) -> Self {
194 Value::new(value.to_string()).to_computed()
195 }
196}