cranpose_core/
composer_context.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4use crate::{Composer, ComposerCore};
5
6// Thread-local stack of Composer handles (safe, no raw pointers).
7thread_local! {
8    static COMPOSER_STACK: RefCell<Vec<Rc<ComposerCore>>> = const { RefCell::new(Vec::new()) };
9}
10
11/// Guard that pops the composer stack on drop.
12#[must_use = "ComposerScopeGuard pops the composer stack on drop"]
13pub struct ComposerScopeGuard;
14
15impl Drop for ComposerScopeGuard {
16    fn drop(&mut self) {
17        COMPOSER_STACK.with(|stack| {
18            let mut stack = stack.borrow_mut();
19            stack.pop();
20        });
21    }
22}
23
24/// Pushes the composer onto the thread-local stack for the duration of the scope.
25/// Returns a guard that will pop it on drop.
26pub fn enter(composer: &Composer) -> ComposerScopeGuard {
27    COMPOSER_STACK.with(|stack| {
28        stack.borrow_mut().push(composer.clone_core());
29    });
30    ComposerScopeGuard
31}
32
33/// Access the current composer from the thread-local stack.
34///
35/// # Panics
36/// Panics if there is no active composer.
37pub fn with_composer<R>(f: impl FnOnce(&Composer) -> R) -> R {
38    COMPOSER_STACK.with(|stack| {
39        let core = stack
40            .borrow()
41            .last()
42            .expect("with_composer: no active composer")
43            .clone();
44        let composer = Composer::from_core(core);
45        f(&composer)
46    })
47}
48
49/// Try to access the current composer from the thread-local stack.
50/// Returns None if there is no active composer.
51pub fn try_with_composer<R>(f: impl FnOnce(&Composer) -> R) -> Option<R> {
52    COMPOSER_STACK.with(|stack| {
53        let core = stack.borrow().last()?.clone();
54        let composer = Composer::from_core(core);
55        Some(f(&composer))
56    })
57}