66 */
77
88use crate :: builtin:: GString ;
9- use crate :: gen:: classes:: { Resource , ResourceLoader } ;
9+ use crate :: engine:: global:: Error as GodotError ;
10+ use crate :: gen:: classes:: { Resource , ResourceLoader , ResourceSaver } ;
1011use crate :: obj:: { Gd , GodotClass , Inherits } ;
1112
13+ use super :: GdIoError ;
14+
1215/// Loads a resource from the filesystem located at `path`, panicking on error.
1316///
1417/// See [`try_load`] for more information.
@@ -29,15 +32,15 @@ where
2932 T : GodotClass + Inherits < Resource > ,
3033{
3134 let path = path. into ( ) ;
32- load_impl ( & path) . unwrap_or_else ( || panic ! ( "failed to load node at path `{path}` " ) )
35+ load_impl ( & path) . unwrap_or_else ( |err | panic ! ( "failed: {err} " ) )
3336}
3437
3538/// Loads a resource from the filesystem located at `path`.
3639///
3740/// The resource is loaded on the method call (unless it's referenced already elsewhere, e.g. in another script or in the scene),
3841/// which might cause slight delay, especially when loading scenes.
3942///
40- /// If the resource cannot be loaded, or is not of type `T` or inherited, this method returns `None` .
43+ /// This function can fail if resource can't be loaded by [`ResourceLoader`] or if the subsequent cast into `T` fails .
4144///
4245/// This method is a simplified version of [`ResourceLoader::load()`][crate::engine::ResourceLoader::load],
4346/// which can be used for more advanced scenarios.
@@ -55,33 +58,125 @@ where
5558/// ```no_run
5659/// use godot::prelude::*;
5760///
58- /// if let Some (scene) = try_load::<PackedScene>("res://path/to/Main.tscn") {
61+ /// if let Ok (scene) = try_load::<PackedScene>("res://path/to/Main.tscn") {
5962/// // all good
6063/// } else {
6164/// // handle error
6265/// }
6366/// ```
64- // TODO Result to differentiate 2 errors
6567#[ inline]
66- pub fn try_load < T > ( path : impl Into < GString > ) -> Option < Gd < T > >
68+ pub fn try_load < T > ( path : impl Into < GString > ) -> Result < Gd < T > , GdIoError >
6769where
6870 T : GodotClass + Inherits < Resource > ,
6971{
7072 load_impl ( & path. into ( ) )
7173}
7274
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 savestate = SavedGame::new_gd();
123+ /// let res = try_save(savestate, "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 < ( ) , GdIoError >
129+ where
130+ T : GodotClass + Inherits < Resource > ,
131+ {
132+ save_impl ( obj, & path. into ( ) )
133+ }
134+
73135// ----------------------------------------------------------------------------------------------------------------------------------------------
74136// Implementation of this file
75137
76138// Separate function, to avoid constructing string twice
77139// Note that more optimizations than that likely make no sense, as loading is quite expensive
78- fn load_impl < T > ( path : & GString ) -> Option < Gd < T > >
140+ fn load_impl < T > ( path : & GString ) -> Result < Gd < T > , GdIoError >
79141where
80142 T : GodotClass + Inherits < Resource > ,
81143{
82- ResourceLoader :: singleton ( )
144+ // TODO unclone GString
145+ match ResourceLoader :: singleton ( )
83146 . load_ex ( path. clone ( ) )
84147 . type_hint ( T :: class_name ( ) . to_godot_string ( ) )
85- . done ( ) // TODO unclone
86- . and_then ( |res| res. try_cast :: < T > ( ) . ok ( ) )
148+ . done ( )
149+ {
150+ Some ( res) => match res. try_cast :: < T > ( ) {
151+ Ok ( obj) => Ok ( obj) ,
152+ Err ( _) => Err ( GdIoError :: loading_cast (
153+ T :: class_name ( ) . to_string ( ) ,
154+ path. to_string ( ) ,
155+ ) ) ,
156+ } ,
157+ None => Err ( GdIoError :: 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 < ( ) , GdIoError >
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 ( GdIoError :: saving (
178+ res,
179+ T :: class_name ( ) . to_string ( ) ,
180+ path. to_string ( ) ,
181+ ) )
87182}
0 commit comments