use std::cell::RefCell;
use std::collections::HashMap;
use sys::is_main_thread;
use crate::builtin::NodePath;
use crate::classes::{Engine, Node, SceneTree};
use crate::meta::error::ConvertError;
use crate::obj::{Gd, Inherits, Singleton};
use crate::sys;
pub fn get_autoload_by_name<T>(autoload_name: &str) -> Gd<T>
where
T: Inherits<Node>,
{
try_get_autoload_by_name::<T>(autoload_name)
.unwrap_or_else(|err| panic!("Failed to get autoload `{autoload_name}`: {err}"))
}
pub fn try_get_autoload_by_name<T>(autoload_name: &str) -> Result<Gd<T>, ConvertError>
where
T: Inherits<Node>,
{
ensure_main_thread()?;
let cached = AUTOLOAD_CACHE.with(|cache| cache.borrow().get(autoload_name).cloned());
if let Some(cached_node) = cached {
return cast_autoload(cached_node, autoload_name);
}
let main_loop = Engine::singleton()
.get_main_loop()
.ok_or_else(|| ConvertError::new("main loop not available"))?;
let scene_tree = main_loop
.try_cast::<SceneTree>()
.map_err(|_| ConvertError::new("main loop is not a SceneTree"))?;
let autoload_path = NodePath::from(&format!("/root/{autoload_name}"));
let root = scene_tree
.get_root()
.ok_or_else(|| ConvertError::new("scene tree root not available"))?;
let autoload_node = root
.try_get_node_as::<Node>(&autoload_path)
.ok_or_else(|| ConvertError::new(format!("autoload `{autoload_name}` not found")))?;
AUTOLOAD_CACHE.with(|cache| {
cache
.borrow_mut()
.insert(autoload_name.to_string(), autoload_node.clone());
});
cast_autoload(autoload_node, autoload_name)
}
thread_local! {
static AUTOLOAD_CACHE: RefCell<HashMap<String, Gd<Node>>> = RefCell::new(HashMap::new());
}
fn ensure_main_thread() -> Result<(), ConvertError> {
if is_main_thread() {
Ok(())
} else {
Err(ConvertError::new(
"Autoloads must be fetched from main thread, as Gd<T> is not thread-safe",
))
}
}
fn cast_autoload<T>(node: Gd<Node>, autoload_name: &str) -> Result<Gd<T>, ConvertError>
where
T: Inherits<Node>,
{
node.try_cast::<T>().map_err(|node| {
let expected = T::class_id();
let actual = node.get_class();
ConvertError::new(format!(
"autoload `{autoload_name}` has wrong type (expected {expected}, got {actual})",
))
})
}
pub(crate) fn clear_autoload_cache() {
ensure_main_thread().expect("clear_autoload_cache() must be called from the main thread");
AUTOLOAD_CACHE.with(|cache| {
cache.borrow_mut().clear();
});
}