|
| 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 | +use crate::builtin::GString; |
| 9 | +use crate::engine::global::Error as GodotError; |
| 10 | +use crate::gen::classes::{Resource, ResourceLoader, ResourceSaver}; |
| 11 | +use crate::obj::{Gd, GodotClass, Inherits}; |
| 12 | + |
| 13 | +use super::IoError; |
| 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] |
| 30 | +pub fn load<T>(path: impl Into<GString>) -> Gd<T> |
| 31 | +where |
| 32 | + T: GodotClass + Inherits<Resource>, |
| 33 | +{ |
| 34 | + let path = path.into(); |
| 35 | + load_impl(&path).unwrap_or_else(|err| panic!("failed: {err}")) |
| 36 | +} |
| 37 | + |
| 38 | +/// Loads a resource from the filesystem located at `path`. |
| 39 | +/// |
| 40 | +/// The resource is loaded on the method call (unless it's referenced already elsewhere, e.g. in another script or in the scene), |
| 41 | +/// which 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::engine::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] |
| 68 | +pub fn try_load<T>(path: impl Into<GString>) -> Result<Gd<T>, IoError> |
| 69 | +where |
| 70 | + T: GodotClass + Inherits<Resource>, |
| 71 | +{ |
| 72 | + load_impl(&path.into()) |
| 73 | +} |
| 74 | + |
| 75 | +/// Saves a [`Resource`]-inheriting [`GodotClass`] `obj` into file located at `path`. |
| 76 | +/// |
| 77 | +/// See [`try_save`] for more information. |
| 78 | +/// |
| 79 | +/// # Panics |
| 80 | +/// If the resouce cannot be saved. |
| 81 | +/// |
| 82 | +/// # Example |
| 83 | +/// ```no_run |
| 84 | +/// use godot::prelude::*; |
| 85 | +/// use godot::engine::save; |
| 86 | +/// |
| 87 | +/// save(Resource::new(), "res://base_resource.tres") |
| 88 | +/// ``` |
| 89 | +/// use godot:: |
| 90 | +#[inline] |
| 91 | +pub fn save<T>(obj: Gd<T>, path: impl Into<GString>) |
| 92 | +where |
| 93 | + T: GodotClass + Inherits<Resource>, |
| 94 | +{ |
| 95 | + let path = path.into(); |
| 96 | + save_impl(obj, &path) |
| 97 | + .unwrap_or_else(|err| panic!("failed to save resource at path '{}': {}", &path, err)); |
| 98 | +} |
| 99 | + |
| 100 | +/// Saves a [Resource]-inheriting [GodotClass] `obj` into file located at `path`. |
| 101 | +/// |
| 102 | +/// This function can fail if [`ResourceSaver`] can't save the resource to file, as it is a simplified version of |
| 103 | +/// [`ResourceSaver::save()`][crate::engine::ResourceSaver::save]. The underlying method can be used for more advances scenarios. |
| 104 | +/// |
| 105 | +/// # Note |
| 106 | +/// Target path must be presented in Godot-recognized format, mainly the ones beginning with `res://` and `user://`. Saving |
| 107 | +/// to `res://` is possible only when working with unexported project - after its export only `user://` is viable. |
| 108 | +/// |
| 109 | +/// # Example |
| 110 | +/// ```no_run |
| 111 | +/// use godot::prelude::*; |
| 112 | +/// use godot::engine::try_save; |
| 113 | +/// |
| 114 | +/// #[derive(GodotClass)] |
| 115 | +/// #[class(base=Resource, init)] |
| 116 | +/// struct SavedGame { |
| 117 | +/// // Exported properties are saved in `.tres` files. |
| 118 | +/// #[export] |
| 119 | +/// level: u32 |
| 120 | +/// }; |
| 121 | +/// |
| 122 | +/// let save_state = SavedGame::new_gd(); |
| 123 | +/// let res = try_save(save_state, "user://save.tres"); |
| 124 | +/// |
| 125 | +/// assert!(res.is_ok()); |
| 126 | +/// ``` |
| 127 | +#[inline] |
| 128 | +pub fn try_save<T>(obj: Gd<T>, path: impl Into<GString>) -> Result<(), IoError> |
| 129 | +where |
| 130 | + T: GodotClass + Inherits<Resource>, |
| 131 | +{ |
| 132 | + save_impl(obj, &path.into()) |
| 133 | +} |
| 134 | + |
| 135 | +// ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 136 | +// Implementation of this file |
| 137 | + |
| 138 | +// Separate function, to avoid constructing string twice |
| 139 | +// Note that more optimizations than that likely make no sense, as loading is quite expensive |
| 140 | +fn load_impl<T>(path: &GString) -> Result<Gd<T>, IoError> |
| 141 | +where |
| 142 | + T: GodotClass + Inherits<Resource>, |
| 143 | +{ |
| 144 | + // TODO unclone GString |
| 145 | + match ResourceLoader::singleton() |
| 146 | + .load_ex(path.clone()) |
| 147 | + .type_hint(T::class_name().to_gstring()) |
| 148 | + .done() |
| 149 | + { |
| 150 | + Some(res) => match res.try_cast::<T>() { |
| 151 | + Ok(obj) => Ok(obj), |
| 152 | + Err(_) => Err(IoError::loading_cast( |
| 153 | + T::class_name().to_string(), |
| 154 | + path.to_string(), |
| 155 | + )), |
| 156 | + }, |
| 157 | + None => Err(IoError::loading( |
| 158 | + T::class_name().to_string(), |
| 159 | + path.to_string(), |
| 160 | + )), |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +fn save_impl<T>(obj: Gd<T>, path: &GString) -> Result<(), IoError> |
| 165 | +where |
| 166 | + T: GodotClass + Inherits<Resource>, |
| 167 | +{ |
| 168 | + // TODO unclone GString |
| 169 | + let res = ResourceSaver::singleton() |
| 170 | + .save_ex(obj.upcast()) |
| 171 | + .path(path.clone()) |
| 172 | + .done(); |
| 173 | + |
| 174 | + if res == GodotError::OK { |
| 175 | + return Ok(()); |
| 176 | + } |
| 177 | + Err(IoError::saving( |
| 178 | + res, |
| 179 | + T::class_name().to_string(), |
| 180 | + path.to_string(), |
| 181 | + )) |
| 182 | +} |
0 commit comments