use crate::classes::node::DuplicateFlags;
#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
use crate::classes::resource::DeepDuplicateMode;
use crate::classes::{Node, Resource};
use crate::obj::{Gd, Inherits};
impl<T> Gd<T>
where
T: Inherits<Node>,
{
pub fn duplicate_node(&self) -> Gd<T> {
self.duplicate_node_ex().done()
}
pub fn duplicate_node_ex(&self) -> ExDuplicateNode<'_, T> {
ExDuplicateNode::new(self)
}
}
#[must_use]
pub struct ExDuplicateNode<'a, T>
where
T: Inherits<Node>,
{
node: &'a Gd<T>,
flags: DuplicateFlags,
}
impl<'a, T> ExDuplicateNode<'a, T>
where
T: Inherits<Node>,
{
fn new(node: &'a Gd<T>) -> Self {
Self {
node,
flags: crate::obj::EngineBitfield::from_ord(15),
}
}
pub fn flags(mut self, flags: DuplicateFlags) -> Self {
self.flags = flags;
self
}
pub fn done(self) -> Gd<T> {
self.try_duplicate()
.and_then(|dup| dup.try_cast::<T>().ok())
.unwrap_or_else(|| {
panic!(
"Failed to duplicate class {t}; is it default-constructible?",
t = self.node.dynamic_class_string()
)
})
}
pub fn done_or_null(self) -> Option<Gd<T>> {
self.try_duplicate()?.try_cast::<T>().ok()
}
fn try_duplicate(&self) -> Option<Gd<Node>> {
#[expect(deprecated)]
self.node.upcast_ref::<Node>().duplicate_full(self.flags)
}
}
impl<T> Gd<T>
where
T: Inherits<Resource>,
{
pub fn duplicate_resource(&self) -> Gd<T> {
self.duplicate_resource_ex().done()
}
pub fn duplicate_resource_ex(&self) -> ExDuplicateResource<'_, T> {
ExDuplicateResource::new(self)
}
}
#[must_use]
pub struct ExDuplicateResource<'a, T>
where
T: Inherits<Resource>,
{
resource: &'a Gd<T>,
godot_api: GodotDuplicateApi,
}
enum GodotDuplicateApi {
Duplicate {
deep: bool,
},
#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
DuplicateDeep {
mode: crate::classes::resource::DeepDuplicateMode,
},
}
impl<'a, T> ExDuplicateResource<'a, T>
where
T: Inherits<Resource>,
{
fn new(resource: &'a Gd<T>) -> Self {
Self {
resource,
godot_api: GodotDuplicateApi::Duplicate { deep: false },
}
}
pub fn deep_internal(mut self) -> Self {
self.godot_api = GodotDuplicateApi::Duplicate { deep: true };
self
}
#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
pub fn deep(mut self, subresources: DeepDuplicateMode) -> Self {
self.godot_api = GodotDuplicateApi::DuplicateDeep { mode: subresources };
self
}
pub fn done(self) -> Gd<T> {
self.try_duplicate()
.and_then(|dup| dup.try_cast::<T>().ok())
.unwrap_or_else(|| {
panic!(
"Failed to duplicate class {t}; is it default-constructible?",
t = self.resource.dynamic_class_string()
)
})
}
pub fn done_or_null(self) -> Option<Gd<T>> {
self.try_duplicate()?.try_cast::<T>().ok()
}
#[expect(deprecated)]
fn try_duplicate(&self) -> Option<Gd<Resource>> {
let resource_ref = self.resource.upcast_ref::<Resource>();
match self.godot_api {
#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
GodotDuplicateApi::Duplicate { deep } => resource_ref.duplicate_ex().deep(deep).done(),
#[cfg(before_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.5")))]
GodotDuplicateApi::Duplicate { deep } => {
resource_ref.duplicate_ex().subresources(deep).done()
}
#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
GodotDuplicateApi::DuplicateDeep { mode } => resource_ref
.duplicate_deep_ex()
.deep_subresources_mode(mode)
.done(),
}
}
}