// Context Provider - Component-level value sharing (Solid.js style)
//
// Context values are associated with Owners (component tree nodes).
// When useContext is called, it walks up the Owner chain to find the value.
///|
/// Context type with typed getter
/// The current_getter is managed per-context and tracks the current value chain
pub struct Context[T] {
id : Int
default_value : () -> T
/// Stack of (owner_id, getter) pairs for this context
/// Most recent provider is at the end
providers : Array[(Int, () -> T)]
}
///|
/// Global context ID counter
let context_id_counter : Ref[Int] = Ref::new(0)
///|
/// Create a new context with a default value
/// Similar to Solid.js createContext
pub fn[T] create_context(default_value : T) -> Context[T] {
let id = context_id_counter.val
context_id_counter.val = id + 1
let default_fn = fn() { default_value }
{ id, default_value: default_fn, providers: [] }
}
///|
/// Check if an owner is still valid (in the owner chain from current)
fn is_owner_valid(owner_id : Int, current : Owner?) -> Bool {
match current {
None => false
Some(o) =>
if o.id == owner_id {
not(o.disposed)
} else {
is_owner_valid(owner_id, o.parent)
}
}
}
///|
/// Find the most recent valid provider for this context
fn[T] find_valid_provider(
ctx : Context[T],
current_owner : Owner?,
) -> (() -> T)? {
// Walk through providers from most recent to oldest
for i = ctx.providers.length() - 1; i >= 0; i = i - 1 {
let (owner_id, getter) = ctx.providers[i]
if is_owner_valid(owner_id, current_owner) {
return Some(getter)
}
}
None
}
///|
/// Provide a context value for the current Owner and its descendants
/// The value is associated with the current Owner and will be available
/// to all effects and components created within this scope.
pub fn[T, R] provide(ctx : Context[T], value : T, f : () -> R) -> R {
match get_owner() {
None =>
// No owner, create a temporary root
create_root(fn(_dispose) { provide_inner(ctx, value, f) })
Some(_) => provide_inner(ctx, value, f)
}
}
///|
/// Internal: provide implementation when we have an owner
fn[T, R] provide_inner(ctx : Context[T], value : T, f : () -> R) -> R {
// Create a child owner for this provide scope
let parent = get_owner()
let owner = Owner::new(parent)
let owner_id = owner.id
// Add provider to the context's provider list
let getter : () -> T = fn() { value }
ctx.providers.push((owner_id, getter))
// Register cleanup to remove provider when owner is disposed
owner.cleanups.push(fn() {
ctx.providers.retain(fn(entry) {
let (oid, _) = entry
oid != owner_id
})
})
// Run the function with this owner
run_with_owner(owner, f)
}
///|
/// Use a context value - returns the current provided value or default
/// Walks up the Owner chain to find the nearest provided value.
/// Similar to Solid.js useContext
pub fn[T] use_context(ctx : Context[T]) -> T {
let current_owner = get_owner()
match find_valid_provider(ctx, current_owner) {
Some(getter) => getter()
None => (ctx.default_value)()
}
}