fn nodecontext_to_robj(
    ctx: &{{ context_type_path }},
) -> extendr_api::Robj {
    use extendr_api::prelude::*;
    let mut pairs: Vec<(&str, extendr_api::Robj)> = Vec::new();
{{ context_field_lines }}
    List::from_pairs(pairs).into()
}

pub struct {{ struct_name }} {
    r_obj: extendr_api::Robj,
}

impl std::fmt::Debug for {{ struct_name }} {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{{ struct_name }}")
    }
}

impl {{ struct_name }} {
    pub fn new(r_obj: extendr_api::Robj) -> Self {
        Self { r_obj }
    }
}

// SAFETY: `extendr_api::Robj` wraps a raw `SEXP` (a GC-managed R object pointer).
// R is single-threaded; the bridge is only invoked synchronously from the R thread
// that constructed it. The `Arc<Mutex>` outer wrapper ensures exclusive access.
// Sending the bridge across threads would cause undefined behaviour in the R
// runtime — callers must not do so.
unsafe impl Send for {{ struct_name }} {}
// SAFETY: see Send impl above.
unsafe impl Sync for {{ struct_name }} {}

impl {{ trait_path }} for {{ struct_name }} {
{{ method_impls }}
}
