godot_core/tools/
save_load.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
8use crate::builtin::GString;
9use crate::classes::{Resource, ResourceLoader, ResourceSaver};
10use crate::global::Error as GodotError;
11use crate::meta::error::IoError;
12use crate::meta::{arg_into_ref, AsArg};
13use crate::obj::{Gd, Inherits, Singleton};
14
15/// ⚠️ Loads a resource from the filesystem located at `path`, panicking on error.
16///
17/// See [`try_load`] for more information.
18///
19/// # Example
20///
21/// ```no_run
22/// use godot::prelude::*;
23///
24/// let scene = load::<PackedScene>("res://path/to/Main.tscn");
25/// ```
26///
27/// # Panics
28/// If the resource cannot be loaded, or is not of type `T` or inherited.
29#[inline]
30pub fn load<T>(path: impl AsArg<GString>) -> Gd<T>
31where
32    T: Inherits<Resource>,
33{
34    arg_into_ref!(path);
35    load_impl(path).unwrap_or_else(|err| panic!("failed to load resource at '{path}': {err}"))
36}
37
38/// Loads a resource from the filesystem located at `path`.
39///
40/// The resource is loaded during the method call, unless it is already referenced elsewhere, e.g. in another script or in the scene.
41/// This might cause slight delay, especially when loading scenes.
42///
43/// This function can fail if resource can't be loaded by [`ResourceLoader`] or if the subsequent cast into `T` fails.
44///
45/// This method is a simplified version of [`ResourceLoader::load()`][crate::classes::ResourceLoader::load],
46/// which can be used for more advanced scenarios.
47///
48/// # Note
49/// Resource paths can be obtained by right-clicking on a resource in the Godot editor (_FileSystem_ dock) and choosing _Copy Path_,
50/// or by dragging the file from the _FileSystem_ dock into the script.
51///
52/// The path must be absolute (typically starting with `res://`), a local path will fail.
53///
54/// # Example
55/// Loads a scene called `Main` located in the `path/to` subdirectory of the Godot project and caches it in a variable.
56/// The resource is directly stored with type `PackedScene`.
57///
58/// ```no_run
59/// use godot::prelude::*;
60///
61/// if let Ok(scene) = try_load::<PackedScene>("res://path/to/Main.tscn") {
62///     // all good
63/// } else {
64///     // handle error
65/// }
66/// ```
67#[inline]
68pub fn try_load<T>(path: impl AsArg<GString>) -> Result<Gd<T>, IoError>
69where
70    T: Inherits<Resource>,
71{
72    arg_into_ref!(path);
73    load_impl(path)
74}
75
76/// ⚠️ Saves a [`Resource`]-inheriting object into the file located at `path`.
77///
78/// See [`try_save`] for more information.
79///
80/// # Panics
81/// If the resource cannot be saved.
82///
83/// # Example
84/// ```no_run
85/// use godot::prelude::*;
86///
87/// let obj = Resource::new_gd();
88/// save(&obj, "res://BaseResource.tres")
89/// ```
90/// use godot::
91#[inline]
92pub fn save<T>(obj: &Gd<T>, path: impl AsArg<GString>)
93where
94    T: Inherits<Resource>,
95{
96    arg_into_ref!(path);
97
98    save_impl(obj, path)
99        .unwrap_or_else(|err| panic!("failed to save resource at path '{}': {}", &path, err));
100}
101
102/// Saves a [`Resource`]-inheriting object into the file located at `path`.
103///
104/// This function can fail if [`ResourceSaver`] can't save the resource to file, as it is a simplified version of
105/// [`ResourceSaver::save()`][crate::classes::ResourceSaver::save]. The underlying method can be used for more advances scenarios.
106///
107/// # Note
108/// Target path must be presented in Godot-recognized format, mainly the ones beginning with `res://` and `user://`. Saving
109/// to `res://` is possible only when working with unexported project - after its export only `user://` is viable.
110///
111/// # Example
112/// ```no_run
113/// use godot::prelude::*;
114///
115/// #[derive(GodotClass)]
116/// #[class(base=Resource, init)]
117/// struct SavedGame {
118///   // Exported properties are saved in `.tres` files.
119///   #[export]
120///   level: u32
121/// };
122///
123/// let save_state = SavedGame::new_gd();
124/// let res = try_save(&save_state, "user://save.tres");
125///
126/// assert!(res.is_ok());
127/// ```
128#[inline]
129pub fn try_save<T>(obj: &Gd<T>, path: impl AsArg<GString>) -> Result<(), IoError>
130where
131    T: Inherits<Resource>,
132{
133    arg_into_ref!(path);
134
135    save_impl(obj, path)
136}
137
138// ----------------------------------------------------------------------------------------------------------------------------------------------
139// Implementation of this file
140
141// Separate function, to avoid constructing string twice
142// Note that more optimizations than that likely make no sense, as loading is quite expensive
143fn load_impl<T>(path: &GString) -> Result<Gd<T>, IoError>
144where
145    T: Inherits<Resource>,
146{
147    let loaded = ResourceLoader::singleton()
148        .load_ex(path)
149        .type_hint(&T::class_id().to_gstring())
150        .done();
151
152    match loaded {
153        Some(res) => match res.try_cast::<T>() {
154            Ok(obj) => Ok(obj),
155            Err(_) => Err(IoError::loading_cast(
156                T::class_id().to_string(),
157                path.to_string(),
158            )),
159        },
160        None => Err(IoError::loading(
161            T::class_id().to_string(),
162            path.to_string(),
163        )),
164    }
165}
166
167fn save_impl<T>(obj: &Gd<T>, path: &GString) -> Result<(), IoError>
168where
169    T: Inherits<Resource>,
170{
171    let res = ResourceSaver::singleton().save_ex(obj).path(path).done();
172
173    if res == GodotError::OK {
174        Ok(())
175    } else {
176        Err(IoError::saving(
177            res,
178            T::class_id().to_string(),
179            path.to_string(),
180        ))
181    }
182}