1pub mod tsify;
2
3use std::sync::Arc;
4
5use ankurah_proto::{CollectionId, EntityId, State};
6
7use crate::entity::Entity;
8use crate::error::StateError;
9
10use crate::property::PropertyError;
11
12use anyhow::Result;
13
14#[cfg(feature = "wasm")]
15use js_sys;
16#[cfg(feature = "wasm")]
17use wasm_bindgen;
18
19pub trait Model {
22 type View: View;
23 type Mutable: Mutable;
24 fn collection() -> CollectionId;
25 fn initialize_new_entity(&self, entity: &Entity);
28}
29
30pub trait View {
32 type Model: Model;
33 type Mutable: Mutable;
34 fn id(&self) -> EntityId { self.entity().id() }
35
36 fn collection() -> CollectionId { <Self::Model as Model>::collection() }
37 fn entity(&self) -> &Entity;
38 fn from_entity(inner: Entity) -> Self;
39 fn to_model(&self) -> Result<Self::Model, PropertyError>;
40}
41
42#[derive(Debug)]
44pub struct MutableBorrow<'rec, T: Mutable> {
45 mutable: T,
46 _entity_ref: &'rec Entity,
47}
48
49impl<'rec, T: Mutable> MutableBorrow<'rec, T> {
50 pub fn new(entity_ref: &'rec Entity) -> Self { Self { mutable: T::new(entity_ref.clone()), _entity_ref: entity_ref } }
51
52 pub fn into_core(self) -> T { self.mutable }
54}
55
56impl<'rec, T: Mutable> std::ops::Deref for MutableBorrow<'rec, T> {
57 type Target = T;
58 fn deref(&self) -> &Self::Target { &self.mutable }
59}
60
61impl<'rec, T: Mutable> std::ops::DerefMut for MutableBorrow<'rec, T> {
62 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.mutable }
63}
64
65pub trait Mutable {
68 type Model: Model;
69 type View: View;
70 fn id(&self) -> EntityId { self.entity().id() }
71 fn collection() -> CollectionId { <Self::Model as Model>::collection() }
72
73 fn entity(&self) -> &Entity;
74 fn new(entity: Entity) -> Self
75 where Self: Sized;
76
77 fn state(&self) -> Result<State, StateError> { self.entity().to_state() }
78
79 fn read(&self) -> Self::View {
80 let inner = self.entity();
81
82 let new_inner = match &inner.kind {
83 crate::entity::EntityKind::Transacted { upstream, .. } => upstream.clone(),
85 crate::entity::EntityKind::Primary => inner.clone(),
87 };
88
89 Self::View::from_entity(new_inner)
90 }
91}
92
93#[doc(hidden)]
96pub fn view_subscribe<V, F>(view: &V, listener: F) -> ankurah_signals::SubscriptionGuard
97where
98 V: ankurah_signals::Signal + View + Clone + Send + Sync + 'static,
99 F: ankurah_signals::subscribe::IntoSubscribeListener<V>,
100{
101 let listener = listener.into_subscribe_listener();
102 let view_clone = view.clone();
103 let subscription = view.listen(Arc::new(move |_| {
104 listener(view_clone.clone());
106 }));
107 ankurah_signals::SubscriptionGuard::new(subscription)
108}
109
110#[doc(hidden)]
111pub fn view_subscribe_no_clone<V, F>(view: &V, listener: F) -> ankurah_signals::SubscriptionGuard
112where
113 V: ankurah_signals::Signal + View + Send + Sync + 'static,
114 F: ankurah_signals::subscribe::IntoSubscribeListener<()>,
115{
116 let listener = listener.into_subscribe_listener();
117 let subscription = view.listen(Arc::new(move |_| {
118 listener(());
119 }));
120 ankurah_signals::SubscriptionGuard::new(subscription)
121}
122
123#[doc(hidden)]
126#[cfg(feature = "wasm")]
127pub fn js_resultset_map<V>(resultset: &crate::resultset::ResultSet<V>, callback: &js_sys::Function) -> js_sys::Array
128where V: View + Clone + 'static + Into<wasm_bindgen::JsValue> {
129 use ankurah_signals::Get;
130 let items = resultset.get();
131 let result_array = js_sys::Array::new();
132
133 for item in items {
134 let js_item = item.into();
135 if let Ok(mapped_value) = callback.call1(&wasm_bindgen::JsValue::NULL, &js_item) {
136 result_array.push(&mapped_value);
137 }
138 }
139
140 result_array
141}
142
143#[doc(hidden)]
145#[cfg(feature = "wasm")]
146pub fn js_livequery_subscribe<V, W, F>(
147 livequery: &crate::livequery::LiveQuery<V>,
148 callback: js_sys::Function,
149 immediate: bool,
150 wrap_changeset: F,
151) -> ankurah_signals::SubscriptionGuard
152where
153 V: View + Clone + Send + Sync + 'static,
154 W: Into<wasm_bindgen::JsValue>,
155 F: Fn(crate::changes::ChangeSet<V>) -> W + Send + Sync + 'static,
156{
157 use ankurah_signals::{Peek, Subscribe};
158
159 if immediate {
161 let current_items = livequery.peek();
162 let changes = current_items.into_iter().map(|item| crate::changes::ItemChange::Add { item, events: vec![] }).collect();
163 let initial_changeset = crate::changes::ChangeSet { resultset: livequery.resultset(), changes };
164 let wrapped = wrap_changeset(initial_changeset);
165 let _ = callback.call1(&wasm_bindgen::JsValue::NULL, &wrapped.into());
166 }
167
168 let callback = ::send_wrapper::SendWrapper::new(callback);
170 livequery.subscribe(move |changeset: crate::changes::ChangeSet<V>| {
171 let wrapped_changeset = wrap_changeset(changeset);
172 let _ = callback.call1(&wasm_bindgen::JsValue::NULL, &wrapped_changeset.into());
173 })
174}