Skip to main content

godot_core/obj/
gd_duplicate.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8//! Type-safe duplicate methods for Node and Resource.
9
10use crate::classes::node::DuplicateFlags;
11#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
12use crate::classes::resource::DeepDuplicateMode;
13use crate::classes::{Node, Resource};
14use crate::obj::{Gd, Inherits};
15
16// ----------------------------------------------------------------------------------------------------------------------------------------------
17// Node duplication
18
19impl<T> Gd<T>
20where
21    T: Inherits<Node>,
22{
23    /// ⚠️ Returns a new node with all of its properties, signals, groups, and children copied from the original.
24    ///
25    /// See [`duplicate_node_ex()`][Self::duplicate_node_ex] for details and panics.
26    ///
27    /// # Example
28    /// ```no_run
29    /// use godot::prelude::*;
30    ///
31    /// let mut node = Node2D::new_alloc();
32    /// node.set_position(Vector2::new(1.0, 2.0));
33    ///
34    /// let copy = node.duplicate_node(); // type Gd<Node2D>
35    /// assert_eq!(copy.get_position(), Vector2::new(1.0, 2.0));
36    ///
37    /// node.free();
38    /// copy.free();
39    /// ```
40    pub fn duplicate_node(&self) -> Gd<T> {
41        self.duplicate_node_ex().done()
42    }
43
44    /// Duplicates this node with a fluent builder API for fine-grained control.
45    ///
46    /// By default, all flags are enabled, just like [`duplicate_node()`][Self::duplicate_node]:
47    /// - Properties, signals, groups, and children are copied.
48    /// - Internal nodes are not duplicated.
49    ///
50    /// You can change this behavior with [`flags()`][ExDuplicateNode::flags]. For nodes with attached scripts: if the script's `_init()` has
51    /// required parameters, the duplicated node will **not** have a script.
52    ///
53    /// This function can be used polymorphically: duplicating `Gd<Node>` pointing to dynamic type `Node2D` duplicates the concrete `Node2D`.
54    ///
55    /// # Panics
56    /// Panics if duplication fails. Likely causes:
57    /// - The **dynamic** (runtime) type of the node is not default-constructible. For example, if a `Gd<Node>` actually points to an instance
58    ///   of `MyClass` (inheriting `Node`), and `MyClass` has no `init` or uses `#[class(no_init)]`, then duplication will fail.
59    /// - Called from a thread other than the main thread (Godot's scene tree is single-threaded).
60    /// - You use [`DuplicateFlags::USE_INSTANTIATION`] and the scene file cannot be loaded.
61    /// - Any child node's duplication fails.
62    ///
63    /// To avoid panics, use [`ExDuplicateNode::done_or_null()`].
64    ///
65    /// # Example
66    /// ```no_run
67    /// use godot::prelude::*;
68    /// use godot::classes::node::DuplicateFlags;
69    ///
70    /// let node: Gd<Node> = Node::new_alloc();
71    /// // Configure node...
72    /// let copy = node.duplicate_node_ex()
73    ///     .flags(DuplicateFlags::SIGNALS | DuplicateFlags::GROUPS)
74    ///     .done();
75    /// ```
76    pub fn duplicate_node_ex(&self) -> ExDuplicateNode<'_, T> {
77        ExDuplicateNode::new(self)
78    }
79}
80
81/// Builder for duplicating a node with fine-grained flag control.
82///
83/// Created by [`Gd::duplicate_node_ex()`].
84/// See [`duplicate_node_ex()`](Gd::duplicate_node_ex) for complete documentation including
85/// default behavior, panic conditions, and script handling.
86#[must_use]
87pub struct ExDuplicateNode<'a, T>
88where
89    T: Inherits<Node>,
90{
91    node: &'a Gd<T>,
92    flags: DuplicateFlags,
93}
94
95impl<'a, T> ExDuplicateNode<'a, T>
96where
97    T: Inherits<Node>,
98{
99    fn new(node: &'a Gd<T>) -> Self {
100        Self {
101            node,
102            // DEFAULT constant is only available from Godot 4.5. Any bits unrecognized by earlier versions should be ignored by Godot.
103            flags: crate::obj::EngineBitfield::from_ord(15),
104        }
105    }
106
107    /// **Replaces** flags (use `|` to combine them).
108    pub fn flags(mut self, flags: DuplicateFlags) -> Self {
109        self.flags = flags;
110        self
111    }
112
113    /// Complete the duplication and return the duplicated node.
114    ///
115    /// # Panics
116    /// On errors, see [`Gd::duplicate_node_ex()`]. To check for results, use [`done_or_null()`][Self::done_or_null].
117    pub fn done(self) -> Gd<T> {
118        self.try_duplicate()
119            .and_then(|dup| dup.try_cast::<T>().ok())
120            .unwrap_or_else(|| {
121                panic!(
122                    "Failed to duplicate class {t}; is it default-constructible?",
123                    t = self.node.dynamic_class_string()
124                )
125            })
126    }
127
128    /// Complete the duplication and return the duplicated node, or `None` if it fails.
129    ///
130    /// See [`Gd::duplicate_node_ex()`] for details.
131    pub fn done_or_null(self) -> Option<Gd<T>> {
132        self.try_duplicate()?.try_cast::<T>().ok()
133    }
134
135    fn try_duplicate(&self) -> Option<Gd<Node>> {
136        #[expect(deprecated)]
137        self.node.upcast_ref::<Node>().duplicate_full(self.flags)
138    }
139}
140
141// ----------------------------------------------------------------------------------------------------------------------------------------------
142// Resource duplication
143
144impl<T> Gd<T>
145where
146    T: Inherits<Resource>,
147{
148    /// ⚠️ Returns a shallow duplicate of this resource.
149    ///
150    /// See [`duplicate_resource_ex()`][Self::duplicate_resource_ex] for details, panics, and version-specific behavior.
151    ///
152    /// # Example
153    /// ```no_run
154    /// use godot::prelude::*;
155    ///
156    /// let resource = Resource::new_gd();
157    /// let copy = resource.duplicate_resource(); // Gd<Resource>.
158    /// assert_ne!(copy, resource); // Different Gd pointer.
159    /// ```
160    pub fn duplicate_resource(&self) -> Gd<T> {
161        self.duplicate_resource_ex().done()
162    }
163
164    /// Duplicates this resource with a fluent builder API for fine-grained control.
165    ///
166    /// By default, performs a shallow copy, same as [`duplicate_resource()`][Self::duplicate_resource].  \
167    /// Use [`deep_internal()`][ExDuplicateResource::deep_internal] or [`deep()`][ExDuplicateResource::deep] to control
168    /// subresource duplication.
169    ///
170    /// Works polymorphically: duplicating `Gd<Resource>` pointing to a dynamic type duplicates the concrete type.
171    ///
172    /// # Panics
173    /// If the dynamic type is not default-constructible (e.g. `#[class(no_init)]`).
174    /// Use [`ExDuplicateResource::done_or_null()`] to handle errors.
175    ///
176    /// # Behavior table
177    /// The behavior has changed in [PR #100673](https://github.com/godotengine/godot/pull/100673) for Godot 4.5, and with both `duplicate()`
178    /// and `duplicate_deep()`, there is now partial semantic overlap in Godot. The following table summarizes the behavior and elaborates
179    /// how it maps to godot-rust.
180    ///
181    /// See also [Godot docs](https://docs.godotengine.org/en/stable/classes/class_resource.html#class-resource-method-duplicate)
182    /// for `Resource.duplicate()` and `Resource.duplicate_deep()`.
183    ///
184    /// | godot-rust | Godot | 4.2–4.4 | 4.5+ |
185    /// |------------|-------|---------|------|
186    /// | `duplicate_resource()`<br>`duplicate_resource_ex()` | `duplicate(false)` | A/D[^ad] shallow-copied, subresources shared | A/D only referenced |
187    /// | `_ex().deep_internal()` | `duplicate(true)` | A/D shallow-copied, subresources in A/D<br>**ignored** (bug) | A/D deep-copied, **internal** subresources duplicated[^internal] |
188    /// | `_ex().deep(NONE)` | `duplicate_deep(NONE)` | | A/D deep-copied, subresources **shared** |
189    /// | `_ex().deep(INTERNAL)` | `duplicate_deep(INTERNAL)` | | A/D deep-copied, **internal** subresources **duplicated** |
190    /// | `_ex().deep(ALL)` | `duplicate_deep(ALL)` | | A/D deep-copied, **all** subresources **duplicated** |
191    ///
192    /// [^ad]: "A/D" stands for "`Array`/`Dictionary`".
193    /// [^internal]: `_ex().deep_internal()` is equivalent to `_ex().deep(INTERNAL)`. This method only exists for <4.5 compatibility.
194    pub fn duplicate_resource_ex(&self) -> ExDuplicateResource<'_, T> {
195        ExDuplicateResource::new(self)
196    }
197}
198
199/// Builder for duplicating a resource with deep duplication control.
200///
201/// Created by [`Gd::duplicate_resource_ex()`]. See that method for details and version-specific behavior.
202///
203/// Configuration methods:
204/// - [`deep_internal()`][Self::deep_internal]: Duplicates internal subresources (all Godot versions).
205/// - [`deep(subresources)`][Self::deep]: Fine-grained control via [`DeepDuplicateMode`] (**Godot 4.5+**).
206///
207/// Terminal methods:
208/// - [`done()`][Self::done]: Finalize duplication (panics on failure).
209/// - [`done_or_null()`][Self::done_or_null]: Finalize duplication (returns `Result`).
210#[must_use]
211pub struct ExDuplicateResource<'a, T>
212where
213    T: Inherits<Resource>,
214{
215    resource: &'a Gd<T>,
216    godot_api: GodotDuplicateApi,
217}
218
219enum GodotDuplicateApi {
220    Duplicate {
221        deep: bool,
222    },
223    #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
224    DuplicateDeep {
225        mode: crate::classes::resource::DeepDuplicateMode,
226    },
227}
228
229impl<'a, T> ExDuplicateResource<'a, T>
230where
231    T: Inherits<Resource>,
232{
233    fn new(resource: &'a Gd<T>) -> Self {
234        Self {
235            resource,
236            godot_api: GodotDuplicateApi::Duplicate { deep: false },
237        }
238    }
239
240    /// Deep duplication of internal subresources (Godot 4.3+).
241    ///
242    /// Duplicates subresources that have no external path (embedded/internal resources).
243    ///
244    /// # Compatibility
245    /// In Godot 4.2-4.4, subresources inside `Array`/`Dictionary` properties are **not** duplicated (known Godot bug).
246    /// This was fixed in Godot 4.5, from which version onward this is equivalent to [`deep(DeepDuplicateMode::INTERNAL)`][Self::deep].
247    // Needs to exist as a separate method for <4.5 compatibility.
248    pub fn deep_internal(mut self) -> Self {
249        self.godot_api = GodotDuplicateApi::Duplicate { deep: true };
250        self
251    }
252
253    /// Deep duplication with control over subresources (**Godot 4.5+**).
254    ///
255    /// The `subresources` parameter controls which `Resource` objects are duplicated:
256    /// - [`NONE`][DeepDuplicateMode::NONE]: No subresources duplicated (but `Array`/`Dictionary` containers are deep-copied).
257    /// - [`INTERNAL`][DeepDuplicateMode::INTERNAL]: Duplicates internal subresources only (no external path). Same as [`deep_internal()`][Self::deep_internal].
258    /// - [`ALL`][DeepDuplicateMode::ALL]: Duplicates all subresources.
259    ///
260    /// Note: Unlike [`done()`][Self::done] without `deep()`, this method **always** deep-copies `Array`/`Dictionary` containers.
261    /// The `subresources` mode only controls whether `Resource` objects inside them are duplicated or shared.
262    ///
263    /// # Compatibility
264    /// Requires Godot 4.5, as it uses Godot's new `Resource::duplicate_deep()` API.
265    #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
266    pub fn deep(mut self, subresources: DeepDuplicateMode) -> Self {
267        self.godot_api = GodotDuplicateApi::DuplicateDeep { mode: subresources };
268        self
269    }
270
271    /// Complete the duplication and return the duplicated resource.
272    ///
273    /// # Panics
274    /// On errors, see [`Gd::duplicate_resource_ex()`]. To check for results, use [`done_or_null()`][Self::done_or_null].
275    pub fn done(self) -> Gd<T> {
276        self.try_duplicate()
277            .and_then(|dup| dup.try_cast::<T>().ok())
278            .unwrap_or_else(|| {
279                panic!(
280                    "Failed to duplicate class {t}; is it default-constructible?",
281                    t = self.resource.dynamic_class_string()
282                )
283            })
284    }
285
286    /// Complete the duplication and return the duplicated resource, or `None` if it fails.
287    ///
288    /// See [`Gd::duplicate_resource_ex()`] for details.
289    pub fn done_or_null(self) -> Option<Gd<T>> {
290        self.try_duplicate()?.try_cast::<T>().ok()
291    }
292
293    #[expect(deprecated)]
294    fn try_duplicate(&self) -> Option<Gd<Resource>> {
295        let resource_ref = self.resource.upcast_ref::<Resource>();
296
297        match self.godot_api {
298            // Godot 4.5 renamed parameter, so our default-param method is different.
299            #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
300            GodotDuplicateApi::Duplicate { deep } => resource_ref.duplicate_ex().deep(deep).done(),
301
302            #[cfg(before_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.5")))]
303            GodotDuplicateApi::Duplicate { deep } => {
304                resource_ref.duplicate_ex().subresources(deep).done()
305            }
306
307            #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
308            GodotDuplicateApi::DuplicateDeep { mode } => resource_ref
309                .duplicate_deep_ex()
310                .deep_subresources_mode(mode)
311                .done(),
312        }
313    }
314}