get<T extends Object> method
Retrieves a dependency from the container.
Implementation
Option<Resolvable<T>> get<T extends Object>({
Entity groupEntity = const DefaultEntity(),
bool traverse = true,
}) {
assert(T != Object, 'T must be specified and cannot be Object.');
final g = groupEntity.preferOverDefault(focusGroup);
final option = getDependency<T>(groupEntity: g, traverse: traverse);
// Outer pattern: collapse the Option<Result<Dependency<T>>>. Each branch
// is structural — no .unwrap() on a None or Err sits between us and the
// dependency.
return switch (option) {
None() => const None(),
Some(value: Err(:final error, :final stackTrace)) => Some(
Sync<T>.err(Err<T>(error, stackTrace: stackTrace)),
),
Some(value: Ok(value: final dep)) => switch (dep.value) {
Sync<T>() => Some(dep.value),
Async<T>(value: final fut) => Some(
Async<T>(
() => fut.then((e) {
// If the async slot resolved to Err, propagate by throwing —
// the surrounding `Async()` constructor absorbs the throw
// into an Err Result on its own value.
final value = switch (e) {
Ok(value: final v) => v,
Err(:final error, :final stackTrace) =>
throw Err<T>(error, stackTrace: stackTrace),
};
// Replace the Async slot with a Sync slot holding the
// resolved value (memoisation). The remove may already
// be a no-op if a concurrent unregister won the race; the
// re-register skips its dup-check by design.
registry.removeDependency<T>(groupEntity: g).end();
switch (registerDependency<T>(
dependency: Dependency<T>(
Sync<T>.okValue(value),
metadata: dep.metadata,
),
checkExisting: false,
)) {
case Ok():
break;
case Err(:final error):
// Should be unreachable with checkExisting: false; if it
// still fires we surface the cause rather than silently
// returning the resolved value with stale registry state.
throw error;
}
return value;
}),
),
),
},
};
}