1use crate::context::{Context, Isolation, RootInner};
4use crate::effect::{AsyncDisposer, EffectHandle};
5use crate::fiber::{Fiber, FiberInner, FiberState};
6use crate::registry::Inject;
7use crate::utils::lock;
8use crate::{CordisError, ErrorCode, Result, Value};
9use std::collections::HashMap;
10use std::fmt::{self, Debug, Formatter};
11use std::sync::atomic::{AtomicU64, Ordering};
12use std::sync::{Arc, Mutex, Weak};
13
14pub type AccessorGet = Arc<dyn Fn(&Context) -> Result<Option<Value>> + Send + Sync + 'static>;
16pub type AccessorSet = Arc<dyn Fn(&Context, Value) -> Result<()> + Send + Sync + 'static>;
18
19#[derive(Clone)]
21pub struct Accessor {
22 pub get: AccessorGet,
24 pub set: Option<AccessorSet>,
26}
27
28impl Accessor {
29 pub fn read_only<F>(get: F) -> Self
31 where
32 F: Fn(&Context) -> Result<Option<Value>> + Send + Sync + 'static,
33 {
34 Self {
35 get: Arc::new(get),
36 set: None,
37 }
38 }
39
40 pub fn read_write<G, S>(get: G, set: S) -> Self
42 where
43 G: Fn(&Context) -> Result<Option<Value>> + Send + Sync + 'static,
44 S: Fn(&Context, Value) -> Result<()> + Send + Sync + 'static,
45 {
46 Self {
47 get: Arc::new(get),
48 set: Some(Arc::new(set)),
49 }
50 }
51}
52
53impl Debug for Accessor {
54 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
55 f.debug_struct("Accessor")
56 .field("writable", &self.set.is_some())
57 .finish_non_exhaustive()
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum Property {
64 Service,
66 Accessor,
68}
69
70struct AccessorRecord {
71 owner_uid: u64,
72 accessor: Accessor,
73}
74
75type AvailabilityCheck = Arc<dyn Fn(&Value) -> bool + Send + Sync + 'static>;
76
77pub(crate) struct ServiceImpl {
78 name: String,
79 value: Value,
80 fiber: Weak<FiberInner>,
81 provider_uid: u64,
82 generation: u64,
83 check: Option<AvailabilityCheck>,
84}
85
86#[derive(Default)]
87struct ReflectState {
88 default_scopes: HashMap<String, Isolation>,
89 implementations: HashMap<Isolation, ServiceImpl>,
90 properties: HashMap<String, Property>,
91 accessors: HashMap<String, AccessorRecord>,
92}
93
94pub(crate) struct ReflectRoot {
95 state: Mutex<ReflectState>,
96 next_generation: AtomicU64,
97}
98
99impl ReflectRoot {
100 pub(crate) fn new() -> Self {
101 Self {
102 state: Mutex::new(ReflectState::default()),
103 next_generation: AtomicU64::new(0),
104 }
105 }
106
107 fn next_generation(&self) -> u64 {
108 self.next_generation.fetch_add(1, Ordering::Relaxed) + 1
109 }
110
111 fn scope_for(&self, ctx: &Context, name: &str) -> Option<Isolation> {
112 ctx.scope_override(name)
113 .or_else(|| lock(&self.state).default_scopes.get(name).copied())
114 }
115
116 fn ensure_scope(&self, ctx: &Context, name: &str) -> Isolation {
117 if let Some(scope) = ctx.scope_override(name) {
118 return scope;
119 }
120 let mut state = lock(&self.state);
121 if let Some(scope) = state.default_scopes.get(name) {
122 return *scope;
123 }
124 let scope = ctx.root.scope();
125 state.default_scopes.insert(name.to_owned(), scope);
126 scope
127 }
128
129 fn value(&self, ctx: &Context, name: &str, strict: bool) -> Result<Option<Value>> {
130 let scope_override = ctx.scope_override(name);
133 let state = lock(&self.state);
134 if state.properties.get(name) == Some(&Property::Accessor) {
135 let accessor = state
136 .accessors
137 .get(name)
138 .map(|record| record.accessor.clone());
139 drop(state);
140 return match accessor {
141 Some(accessor) => (accessor.get)(ctx),
142 None => Ok(None),
143 };
144 }
145
146 let Some(scope) = scope_override.or_else(|| state.default_scopes.get(name).copied()) else {
147 return Ok(None);
148 };
149 let Some(implementation) = state.implementations.get(&scope) else {
150 return Ok(None);
151 };
152 if implementation.name != name {
153 return Ok(None);
154 }
155 let value = implementation.value.clone();
156 let provider = implementation.fiber.upgrade();
157 let provider_uid = implementation.provider_uid;
158 drop(state);
159
160 let Some(provider) = provider else {
161 return Ok(None);
162 };
163 if strict {
164 let caller_uid = ctx.fiber().ok().and_then(|fiber| fiber.uid());
165 if caller_uid != Some(provider_uid)
166 && Fiber::from_inner(provider).state() != FiberState::Active
167 {
168 return Ok(None);
169 }
170 }
171 Ok(Some(value))
172 }
173
174 fn implementation_epoch(&self, ctx: &Context, name: &str) -> Option<u64> {
175 let scope_override = ctx.scope_override(name);
176 let (generation, provider, check, value) = {
177 let state = lock(&self.state);
178 let scope = scope_override.or_else(|| state.default_scopes.get(name).copied())?;
179 let implementation = state.implementations.get(&scope)?;
180 if implementation.name != name {
181 return None;
182 }
183 (
184 implementation.generation,
185 implementation.fiber.upgrade(),
186 implementation.check.clone(),
187 implementation
191 .check
192 .as_ref()
193 .map(|_| implementation.value.clone()),
194 )
195 };
196 let provider = provider?;
197 if Fiber::from_inner(provider).state() != FiberState::Active {
198 return None;
199 }
200 if let (Some(check), Some(value)) = (check, value) {
201 if !check(&value) {
202 return None;
203 }
204 }
205 Some(generation)
206 }
207
208 fn services_owned_by(&self, uid: u64) -> Vec<(String, Isolation)> {
209 lock(&self.state)
210 .implementations
211 .iter()
212 .filter(|(_, implementation)| implementation.provider_uid == uid)
213 .map(|(scope, implementation)| (implementation.name.clone(), *scope))
214 .collect()
215 }
216}
217
218#[derive(Debug, Clone)]
220pub struct ServiceInfo {
221 pub name: String,
223 pub isolation: Isolation,
225 pub provider_uid: u64,
227 pub provider_name: String,
229 pub type_name: &'static str,
231 pub generation: u64,
233}
234
235#[derive(Clone, Debug)]
237pub struct ReflectService {
238 ctx: Context,
239}
240
241impl ReflectService {
242 pub(crate) fn new(ctx: Context) -> Self {
243 Self { ctx }
244 }
245
246 pub fn get<T>(&self, name: &str, strict: bool) -> Result<Option<Arc<T>>>
248 where
249 T: Send + Sync + 'static,
250 {
251 self.get_value(name, strict)?
252 .map(|value| value.downcast())
253 .transpose()
254 }
255
256 pub fn get_value(&self, name: &str, strict: bool) -> Result<Option<Value>> {
258 self.ctx.root.reflect.value(&self.ctx, name, strict)
259 }
260
261 pub fn require<T>(&self, name: &str) -> Result<Arc<T>>
263 where
264 T: Send + Sync + 'static,
265 {
266 let value = self.get_value(name, true)?.ok_or_else(|| {
267 CordisError::with_message(
268 ErrorCode::MissingService,
269 format!("required service \"{name}\" is unavailable"),
270 )
271 })?;
272 value.downcast()
273 }
274
275 pub fn provide_value(
277 &self,
278 name: String,
279 value: Value,
280 check: Option<AvailabilityCheck>,
281 ) -> Result<EffectHandle> {
282 let fiber = self.ctx.fiber()?;
283 fiber.assert_active()?;
284 let uid = fiber
285 .uid()
286 .ok_or_else(|| CordisError::new(ErrorCode::InactiveEffect))?;
287 let scope = self.ctx.root.reflect.ensure_scope(&self.ctx, &name);
288 let generation = self.ctx.root.reflect.next_generation();
289
290 {
291 let mut state = lock(&self.ctx.root.reflect.state);
292 if let Some(property) = state.properties.get(&name) {
293 if *property != Property::Service {
294 return Err(CordisError::with_message(
295 ErrorCode::PropertyConflict,
296 format!("property \"{name}\" is already declared as {property:?}"),
297 ));
298 }
299 }
300 if let Some(existing) = state.implementations.get(&scope) {
301 let provider_name = existing
302 .fiber
303 .upgrade()
304 .map(Fiber::from_inner)
305 .map(|fiber| fiber.name())
306 .unwrap_or_else(|| "disposed".to_owned());
307 return Err(CordisError::with_message(
308 ErrorCode::DuplicateService,
309 format!("service \"{name}\" has been registered at <{provider_name}>"),
310 ));
311 }
312 state.properties.insert(name.clone(), Property::Service);
313 state.implementations.insert(
314 scope,
315 ServiceImpl {
316 name: name.clone(),
317 value,
318 fiber: Arc::downgrade(&fiber.inner),
319 provider_uid: uid,
320 generation,
321 check,
322 },
323 );
324 }
325
326 let root = Arc::downgrade(&self.ctx.root);
327 let effect_name = name.clone();
328 let effect = fiber.register_effect(
329 format!("ctx.provide({name:?})"),
330 AsyncDisposer::from_sync(move || {
331 let Some(root) = root.upgrade() else {
332 return Ok(());
333 };
334 let removed = {
335 let mut state = lock(&root.reflect.state);
336 match state.implementations.get(&scope) {
337 Some(current) if current.generation == generation => {
338 state.implementations.remove(&scope);
339 true
340 }
341 _ => false,
342 }
343 };
344 if removed {
345 root.notify_service(&effect_name, scope);
346 }
347 Ok(())
348 }),
349 );
350
351 match effect {
352 Ok(effect) => {
353 self.ctx.root.notify_service(&name, scope);
354 Ok(effect)
355 }
356 Err(error) => {
357 lock(&self.ctx.root.reflect.state)
358 .implementations
359 .remove(&scope);
360 Err(error)
361 }
362 }
363 }
364
365 pub fn provide_checked<T, F>(
367 &self,
368 name: impl Into<String>,
369 value: T,
370 check: F,
371 ) -> Result<EffectHandle>
372 where
373 T: Send + Sync + 'static,
374 F: Fn(&T) -> bool + Send + Sync + 'static,
375 {
376 let value = Value::new(value);
377 let check = Arc::new(move |value: &Value| {
378 value
379 .as_any()
380 .downcast_ref::<T>()
381 .map(&check)
382 .unwrap_or(false)
383 });
384 self.provide_value(name.into(), value, Some(check))
385 }
386
387 pub fn set_value(&self, name: &str, value: Value) -> Result<()> {
389 let scope_override = self.ctx.scope_override(name);
390 let mut state = lock(&self.ctx.root.reflect.state);
391 if state.properties.get(name) == Some(&Property::Accessor) {
392 let setter = state
393 .accessors
394 .get(name)
395 .and_then(|record| record.accessor.set.clone());
396 drop(state);
397 let setter = setter.ok_or_else(|| {
398 CordisError::with_message(
399 ErrorCode::AccessDenied,
400 format!("property \"{name}\" is read-only"),
401 )
402 })?;
403 return setter(&self.ctx, value);
404 }
405
406 let scope = scope_override
407 .or_else(|| state.default_scopes.get(name).copied())
408 .ok_or_else(|| {
409 CordisError::with_message(
410 ErrorCode::MissingService,
411 format!("cannot set property \"{name}\" without provide"),
412 )
413 })?;
414 let uid = self.ctx.fiber()?.uid();
415 let implementation = state.implementations.get_mut(&scope).ok_or_else(|| {
416 CordisError::with_message(
417 ErrorCode::MissingService,
418 format!("cannot set property \"{name}\" without provide"),
419 )
420 })?;
421 if uid != Some(implementation.provider_uid) {
422 return Err(CordisError::with_message(
423 ErrorCode::AccessDenied,
424 format!("cannot set property \"{name}\" in multiple fibers"),
425 ));
426 }
427 implementation.value = value;
428 Ok(())
429 }
430
431 pub fn accessor(&self, name: String, accessor: Accessor) -> Result<EffectHandle> {
433 let fiber = self.ctx.fiber()?;
434 let uid = fiber
435 .uid()
436 .ok_or_else(|| CordisError::new(ErrorCode::InactiveEffect))?;
437 {
438 let mut state = lock(&self.ctx.root.reflect.state);
439 if let Some(property) = state.properties.get(&name) {
440 return Err(CordisError::with_message(
441 ErrorCode::PropertyConflict,
442 format!("property \"{name}\" is already declared as {property:?}"),
443 ));
444 }
445 state.properties.insert(name.clone(), Property::Accessor);
446 state.accessors.insert(
447 name.clone(),
448 AccessorRecord {
449 owner_uid: uid,
450 accessor,
451 },
452 );
453 }
454 let root = Arc::downgrade(&self.ctx.root);
455 let effect_name = name.clone();
456 let effect = fiber.register_effect(
457 format!("ctx.accessor({name:?})"),
458 AsyncDisposer::from_sync(move || {
459 if let Some(root) = root.upgrade() {
460 let mut state = lock(&root.reflect.state);
461 let owned = state
462 .accessors
463 .get(&effect_name)
464 .map(|record| record.owner_uid == uid)
465 .unwrap_or(false);
466 if owned {
467 state.accessors.remove(&effect_name);
468 state.properties.remove(&effect_name);
469 }
470 }
471 Ok(())
472 }),
473 );
474 if effect.is_err() {
475 let mut state = lock(&self.ctx.root.reflect.state);
476 state.accessors.remove(&name);
477 state.properties.remove(&name);
478 }
479 effect
480 }
481
482 pub fn alias(
485 &self,
486 alias: impl Into<String>,
487 target: impl Into<String>,
488 ) -> Result<EffectHandle> {
489 let alias = alias.into();
490 let target = target.into();
491 let getter_target = target.clone();
492 let setter_target = target;
493 self.accessor(
494 alias,
495 Accessor::read_write(
496 move |ctx| ctx.reflect().get_value(&getter_target, true),
497 move |ctx, value| ctx.reflect().set_value(&setter_target, value),
498 ),
499 )
500 }
501
502 pub fn notify<I, S>(&self, names: I) -> Vec<Fiber>
509 where
510 I: IntoIterator<Item = S>,
511 S: AsRef<str>,
512 {
513 let scoped_names = names
514 .into_iter()
515 .filter_map(|name| {
516 let name = name.as_ref().to_owned();
517 self.ctx
518 .root
519 .reflect
520 .scope_for(&self.ctx, &name)
521 .map(|scope| (name, scope))
522 })
523 .collect::<Vec<_>>();
524 let mut affected: Vec<Fiber> = Vec::new();
525 for (name, scope) in scoped_names {
526 for fiber in self.ctx.root.notify_service(&name, scope) {
527 if !affected
528 .iter()
529 .any(|seen| Arc::ptr_eq(&seen.inner, &fiber.inner))
530 {
531 affected.push(fiber);
532 }
533 }
534 }
535 affected
536 }
537
538 pub fn properties(&self) -> HashMap<String, Property> {
540 lock(&self.ctx.root.reflect.state).properties.clone()
541 }
542
543 pub fn services(&self) -> Vec<ServiceInfo> {
545 lock(&self.ctx.root.reflect.state)
546 .implementations
547 .iter()
548 .map(|(scope, implementation)| ServiceInfo {
549 name: implementation.name.clone(),
550 isolation: *scope,
551 provider_uid: implementation.provider_uid,
552 provider_name: implementation
553 .fiber
554 .upgrade()
555 .map(Fiber::from_inner)
556 .map(|fiber| fiber.name())
557 .unwrap_or_else(|| "disposed".to_owned()),
558 type_name: implementation.value.type_name(),
559 generation: implementation.generation,
560 })
561 .collect()
562 }
563}
564
565impl RootInner {
566 pub(crate) fn dependency_epoch(&self, ctx: &Context, inject: &Inject) -> Option<Vec<u64>> {
567 let mut epoch = Vec::with_capacity(inject.len());
570 for dependency in inject.iter() {
571 epoch.push(self.reflect.implementation_epoch(ctx, &dependency.name)?);
572 }
573 Some(epoch)
574 }
575
576 pub(crate) fn notify_service(&self, name: &str, scope: Isolation) -> Vec<Fiber> {
577 let mut refreshed = Vec::new();
578 for fiber in self.registry.fibers_injecting(name) {
579 let same_scope = fiber
580 .scope_override(name)
581 .or_else(|| lock(&self.reflect.state).default_scopes.get(name).copied())
582 == Some(scope);
583 if same_scope {
584 fiber.refresh();
585 refreshed.push(fiber);
586 }
587 }
588
589 if let Some(root) = self.root_fiber.get().and_then(Fiber::context) {
590 let mut args = vec![Value::new(name.to_owned())];
591 if let Ok(Some(value)) = self.reflect.value(&root, name, false) {
592 args.push(value);
593 }
594 if let Err(error) = root.events().emit("internal/service", args) {
595 root.log_error(error);
596 }
597 }
598 refreshed
599 }
600
601 pub(crate) fn notify_fiber_services(&self, fiber: &Fiber) {
602 let Some(uid) = fiber.uid() else {
603 return;
604 };
605 for (name, scope) in self.reflect.services_owned_by(uid) {
606 self.notify_service(&name, scope);
607 }
608 }
609}