register<T extends Object> method
Resolvable<T>
register<T extends Object>(
- FutureOr<
T> value, { - Option<
TOnRegisterCallback< onRegister = const None(),T> > - Option<
TOnUnregisterCallback< onUnregister = const None(),T> > - Entity groupEntity = const DefaultEntity(),
- bool enableUntilExactlyK = false,
Registers a dependency with the container.
Implementation
Resolvable<T> register<T extends Object>(
FutureOr<T> value, {
Option<TOnRegisterCallback<T>> onRegister = const None(),
Option<TOnUnregisterCallback<T>> onUnregister = const None(),
Entity groupEntity = const DefaultEntity(),
bool enableUntilExactlyK = false,
}) {
assert(T != Object, 'T must be specified and cannot be Object.');
assert(
value is! Future || T != FutureOr,
'register<$T>: registering a Future where T is FutureOr is ambiguous. '
'Use Resolvable<T> or unwrap the Future before registering.',
);
final g = groupEntity.preferOverDefault(focusGroup);
// Check for existing slot BEFORE building the Resolvable — wrapping it
// runs the onRegister side effects (init()), which must not fire for a
// registration we'll reject.
switch (getDependency<T>(groupEntity: g, traverse: false)) {
case Some():
return Sync<T>.err(Err('Dependency already registered.'));
case None():
break;
}
final metadata = DependencyMetadata(
index: Some(_indexIncrementer++),
groupEntity: g,
onUnregister: onUnregister.map((cb) => (e) => cb(e.transf())),
);
// Wrap onRegister so a sync throw lands as Err on the returned
// Resolvable instead of escaping out of `register()`.
final a = Resolvable(
() => consec(value, (e) {
return consec(_safeOnRegister<T>(onRegister, e), (_) => e);
}),
);
final b = registerDependency<T>(
dependency: Dependency(a, metadata: Some(metadata)),
// Slot was already free-checked above; skip the redundant probe.
checkExisting: false,
);
switch (b) {
case Err<Dependency<T>> err:
return Sync.err(err.transfErr<T>());
case Ok():
break;
}
if (value is! ReservedSafeCompleter<T>) {
_maybeFinish<Object>(value: value, g: g);
if (enableUntilExactlyK) {
(this as SupportsMixinK).maybeFinishK<T>(g: g);
}
}
// We just wrote the slot — `get<T>` should find it unless a concurrent
// unregister raced. Return Err in that case rather than throw.
return switch (get<T>(groupEntity: groupEntity)) {
Some(value: final r) => r,
None() => Sync<T>.err(
Err('register<$T>: post-register lookup returned None '
'(slot was concurrently removed).'),
),
};
}