1use moire_types::{
2 CustomEventKind, EdgeKind, Entity, EntityBody, EntityBodySlot, EntityId, EventKind,
3 EventTarget, Json, Scope, ScopeBody, ScopeId,
4};
5use std::marker::PhantomData;
6use std::sync::{Arc, Weak};
7
8use super::db::runtime_db;
9
10#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct EntityRef {
12 id: EntityId,
13}
14
15impl EntityRef {
16 pub fn id(&self) -> &EntityId {
17 &self.id
18 }
19}
20
21pub fn entity_ref_from_wire(id: impl Into<String>) -> EntityRef {
22 EntityRef {
23 id: EntityId::new(id.into()),
24 }
25}
26
27pub fn current_causal_target() -> Option<EntityRef> {
28 current_causal_target_from_stack()
29}
30
31pub fn current_causal_target_from_stack() -> Option<EntityRef> {
32 super::FUTURE_CAUSAL_STACK
33 .try_with(|stack| {
34 stack.borrow().last().map(|id| EntityRef {
35 id: EntityId::new(id.as_str()),
36 })
37 })
38 .ok()
39 .flatten()
40}
41
42pub fn current_causal_target_with_task_fallback() -> Option<EntityRef> {
43 current_causal_target_from_stack()
44 .or_else(|| super::aether_entity_for_current_task().map(|id| EntityRef { id }))
45}
46
47#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
48pub struct ScopeRef {
49 id: ScopeId,
50}
51
52impl ScopeRef {
53 pub fn id(&self) -> &ScopeId {
54 &self.id
55 }
56}
57
58struct ScopeHandleInner {
59 id: ScopeId,
60}
61
62impl Drop for ScopeHandleInner {
63 fn drop(&mut self) {
64 if let Ok(mut db) = runtime_db().lock() {
65 db.remove_scope(&self.id);
66 }
67 }
68}
69
70#[derive(Clone)]
71pub struct ScopeHandle {
72 inner: Arc<ScopeHandleInner>,
73}
74
75impl ScopeHandle {
76 pub fn new(name: impl Into<String>, body: ScopeBody) -> Self {
77 let scope = Scope::new(super::capture_backtrace_id(), name, body);
78 let id = ScopeId::new(scope.id.as_str());
79
80 if let Ok(mut db) = runtime_db().lock() {
81 db.upsert_scope(scope);
82 }
83
84 Self {
85 inner: Arc::new(ScopeHandleInner { id }),
86 }
87 }
88
89 pub fn id(&self) -> &ScopeId {
90 &self.inner.id
91 }
92
93 pub fn scope_ref(&self) -> ScopeRef {
94 ScopeRef {
95 id: ScopeId::new(self.inner.id.as_str()),
96 }
97 }
98}
99
100struct HandleInner {
101 id: EntityId,
102 kind_name: &'static str,
103}
104
105impl Drop for HandleInner {
106 fn drop(&mut self) {
107 if let Ok(mut db) = runtime_db().lock() {
108 db.remove_entity(&self.id);
109 }
110 }
111}
112
113pub struct EntityHandle<S> {
114 inner: Arc<HandleInner>,
115 _slot: PhantomData<S>,
116}
117
118impl<S> Clone for EntityHandle<S> {
119 fn clone(&self) -> Self {
120 Self {
121 inner: Arc::clone(&self.inner),
122 _slot: PhantomData,
123 }
124 }
125}
126
127impl<S> EntityHandle<S> {
128 fn from_entity(entity: Entity) -> Self {
129 let kind_name = entity.body.kind_name();
130 let id = EntityId::new(entity.id.as_str());
131
132 if let Ok(mut db) = runtime_db().lock() {
133 db.upsert_entity(entity);
134 }
135
136 Self {
137 inner: Arc::new(HandleInner { id, kind_name }),
138 _slot: PhantomData,
139 }
140 }
141}
142
143impl<S> EntityHandle<S>
144where
145 S: EntityBodySlot<Value = S> + Into<EntityBody>,
146{
147 pub fn new(name: impl Into<String>, body: S) -> Self {
148 let entity = Entity::new(super::capture_backtrace_id(), name, body.into());
149 Self::from_entity(entity)
150 }
151}
152
153impl<S> EntityHandle<S> {
154 pub fn id(&self) -> &EntityId {
155 &self.inner.id
156 }
157
158 pub fn rename(&self, name: impl Into<String>) -> bool {
159 let mut db = runtime_db()
160 .lock()
161 .expect("runtime db lock poisoned during entity rename");
162 db.rename_entity_and_maybe_upsert(self.id(), name)
163 }
164
165 pub fn kind_name(&self) -> &'static str {
166 self.inner.kind_name
167 }
168
169 pub fn entity_ref(&self) -> EntityRef {
170 EntityRef {
171 id: EntityId::new(self.inner.id.as_str()),
172 }
173 }
174
175 pub fn link_to(&self, target: &EntityRef, kind: EdgeKind) {
176 if let Ok(mut db) = runtime_db().lock() {
177 db.upsert_edge(self.id(), target.id(), kind, super::capture_backtrace_id());
178 }
179 }
180
181 pub fn link_to_handle<T>(&self, target: &EntityHandle<T>, kind: EdgeKind) {
182 self.link_to(&target.entity_ref(), kind);
183 }
184}
185
186impl<S> EntityHandle<S>
187where
188 S: EntityBodySlot,
189{
190 pub fn mutate(&self, f: impl FnOnce(&mut S::Value)) -> bool {
191 if self.kind_name() != S::KIND_NAME {
192 panic!(
193 "entity kind mismatch for mutate: handle kind={} slot kind={} entity_id={}",
194 self.kind_name(),
195 S::KIND_NAME,
196 self.id().as_str(),
197 );
198 }
199
200 let mut db = runtime_db()
201 .lock()
202 .expect("runtime db lock poisoned during entity mutate");
203 db.mutate_entity_body_and_maybe_upsert(self.id(), |body| {
204 let slot = S::project_mut(body).unwrap_or_else(|| {
205 panic!(
206 "entity body projection failed after kind check: kind={} entity_id={}",
207 S::KIND_NAME,
208 self.id().as_str(),
209 )
210 });
211 f(slot);
212 })
213 }
214}
215
216impl<S> EntityHandle<S> {
217 pub fn emit_event(
219 &self,
220 kind: impl Into<String>,
221 display_name: impl Into<String>,
222 payload: Json,
223 ) {
224 let event = super::new_event(
225 EventTarget::Entity(EntityId::new(self.id().as_str())),
226 EventKind::Custom(CustomEventKind {
227 kind: kind.into(),
228 display_name: display_name.into(),
229 payload,
230 }),
231 );
232 super::record_event(event);
233 }
234}
235
236impl<S> EntityHandle<S> {
237 pub fn downgrade(&self) -> WeakEntityHandle<S> {
238 WeakEntityHandle {
239 inner: Arc::downgrade(&self.inner),
240 _slot: PhantomData,
241 }
242 }
243}
244
245pub struct WeakEntityHandle<S> {
250 inner: Weak<HandleInner>,
251 _slot: PhantomData<S>,
252}
253
254impl<S> Clone for WeakEntityHandle<S> {
255 fn clone(&self) -> Self {
256 Self {
257 inner: self.inner.clone(),
258 _slot: PhantomData,
259 }
260 }
261}
262
263impl<S> WeakEntityHandle<S>
264where
265 S: EntityBodySlot,
266{
267 pub fn rename(&self, name: impl Into<String>) -> bool {
268 let Some(inner) = self.inner.upgrade() else {
269 return false;
270 };
271 let mut db = runtime_db()
272 .lock()
273 .expect("runtime db lock poisoned during weak entity rename");
274 db.rename_entity_and_maybe_upsert(&inner.id, name)
275 }
276
277 pub fn mutate(&self, f: impl FnOnce(&mut S::Value)) -> bool {
278 let Some(inner) = self.inner.upgrade() else {
279 return false;
280 };
281 let mut db = runtime_db()
282 .lock()
283 .expect("runtime db lock poisoned during weak entity mutate");
284 db.mutate_entity_body_and_maybe_upsert(&inner.id, |body| {
285 let slot = S::project_mut(body).unwrap_or_else(|| {
286 panic!(
287 "entity body projection failed: kind={} entity_id={}",
288 S::KIND_NAME,
289 inner.id.as_str(),
290 )
291 });
292 f(slot);
293 })
294 }
295}
296
297pub struct EdgeHandle {
303 src: EntityId,
304 dst: EntityId,
305 kind: EdgeKind,
306}
307
308impl Drop for EdgeHandle {
309 fn drop(&mut self) {
310 if let Ok(mut db) = runtime_db().lock() {
311 db.remove_edge(&self.src, &self.dst, self.kind);
312 }
313 }
314}
315
316impl<S> EntityHandle<S> {
317 pub fn link_to_owned(&self, target: &impl AsEntityRef, kind: EdgeKind) -> EdgeHandle {
318 self.as_entity_ref().link_to_owned(target, kind)
319 }
320}
321
322impl EntityRef {
323 pub fn link_to_owned(&self, target: &impl AsEntityRef, kind: EdgeKind) -> EdgeHandle {
324 let src = self.id().clone();
325 let dst = target.as_entity_ref().id().clone();
326 if let Ok(mut db) = runtime_db().lock() {
327 db.upsert_edge(&src, &dst, kind, super::capture_backtrace_id());
328 }
329 EdgeHandle { src, dst, kind }
330 }
331}
332
333pub trait AsEntityRef {
335 fn as_entity_ref(&self) -> EntityRef;
336}
337
338impl<S> AsEntityRef for EntityHandle<S> {
339 fn as_entity_ref(&self) -> EntityRef {
340 self.entity_ref()
341 }
342}
343
344impl AsEntityRef for EntityRef {
345 fn as_entity_ref(&self) -> EntityRef {
346 self.clone()
347 }
348}