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}