From fc5410a60cee72de0d637efe22f91389b9f2972c Mon Sep 17 00:00:00 2001 From: Michal Kosinski Date: Tue, 5 Dec 2023 19:55:45 +0100 Subject: [PATCH] crate rename to gd-props --- Cargo.toml | 6 +- README.md | 165 +++++++++++------- doc_index.html | 2 +- {godot_io_defs => gd-props-defs}/Cargo.toml | 4 +- gd-props-defs/src/errors.rs | 24 +++ .../src/gd_meta.rs | 22 +-- .../gdres.rs => gd-props-defs/src/gdprop.rs | 126 +------------ .../src/gdprop_io.rs | 112 ++++++------ gd-props-defs/src/lib.rs | 10 ++ .../src/serde_gd.rs | 9 +- .../Cargo.toml | 7 +- .../gdres.rs => gd-props-macros/src/gdprop.rs | 16 +- .../src/lib.rs | 82 ++++----- .../src/translate.rs | 0 .../src/utils.rs | 0 {godot_io => gd-props}/Cargo.toml | 7 +- {godot_io => gd-props}/src/lib.rs | 26 +-- godot_io_defs/src/errors.rs | 24 --- godot_io_defs/src/file_access.rs | 165 ------------------ godot_io_defs/src/lib.rs | 10 -- tests/rust/Cargo.toml | 2 +- tests/rust/src/bench/gdbin.rs | 2 +- tests/rust/src/bench/gdron.rs | 6 +- tests/rust/src/itest/gdbin.rs | 2 +- tests/rust/src/itest/gdron.rs | 2 +- tests/rust/src/itest/saver_loader.rs | 2 +- tests/rust/src/lib.rs | 4 +- tests/rust/src/structs/prop_handlers.rs | 6 +- tests/rust/src/structs/resource.rs | 4 +- tests/rust/src/tests.rs | 16 +- 30 files changed, 314 insertions(+), 549 deletions(-) rename {godot_io_defs => gd-props-defs}/Cargo.toml (85%) create mode 100644 gd-props-defs/src/errors.rs rename {godot_io_defs => gd-props-defs}/src/gd_meta.rs (88%) rename godot_io_defs/src/gdres.rs => gd-props-defs/src/gdprop.rs (57%) rename godot_io_defs/src/gdres_io.rs => gd-props-defs/src/gdprop_io.rs (75%) create mode 100644 gd-props-defs/src/lib.rs rename {godot_io_defs => gd-props-defs}/src/serde_gd.rs (98%) rename {godot_io_derive => gd-props-macros}/Cargo.toml (72%) rename godot_io_derive/src/gdres.rs => gd-props-macros/src/gdprop.rs (92%) rename {godot_io_derive => gd-props-macros}/src/lib.rs (65%) rename {godot_io_derive => gd-props-macros}/src/translate.rs (100%) rename {godot_io_derive => gd-props-macros}/src/utils.rs (100%) rename {godot_io => gd-props}/Cargo.toml (67%) rename {godot_io => gd-props}/src/lib.rs (60%) delete mode 100644 godot_io_defs/src/errors.rs delete mode 100644 godot_io_defs/src/file_access.rs delete mode 100644 godot_io_defs/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index a37bfcc..257e46e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] resolver = "2" members = [ - "godot_io", - "godot_io_defs", - "godot_io_derive", + "gd-props", + "gd-props-defs", + "gd-props-macros", # tests "tests/rust" diff --git a/README.md b/README.md index a8fd1ac..42b0c51 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,75 @@ -# godot_io -Creating custom Godot resources with [godot-rust](https://github.com/godot-rust/gdext) and using them in the Godot Editor is fun and useful. There are some drawbacks to the process out of the box, though. +# gd-props -Godot default `ResourceSaver` and `ResourceLoader` can only handle `exported` fields of your resources. These needs to be recognized by Godot editor - so Godot types. This can be cumbersome if you want to save some more complex state inside your resource. +> Resources are akin to the versatile props that set the scene for an interactive masterpiece on the stage of game world. +> Much like actors skillfully employ props to enrich their storytelling, game objects leverage resources to craft a compelling virtual +> narrative. The mastery lies in the thoughtful selection and optimization of these digital tools, guaranteeing a captivating +> performance as players step into the spotlight of the gaming world. -This crate is born from this frustration and its goal is to provide tools to save rust-created Resources straight to and from custom format. +Custom resources created with [godot-rust](https://github.com/godot-rust/gdext) can be very useful. However, as the game data becomes +complex, the workflow available out of the box can be quite limiting. By default, Godot saves all resources into `.tres` and `.res` files, +preserving only the state of fields marked with `#[export]`. This limitation confines the saving of state only to fields converted to +Godot-compatible types. + +`gd-props` aims to address this issue by providing an alternative strategy for loading and saving resources, relying fully on `serde` +serialization and deserialization. Resources can be saved in two formats: + +- `.gdron`: Based on the `Ron` format from the `ron` crate. Intended for human-readable output during development. +- `.gdbin`: Based on the `MessagePack` format from the `rmp_serde` crate. Intended for faster serialization and deserialization +- times, especially in exported games. + +## Current Features + +The following features are currently available. More will be listed in the `In Development` section. + +- `GdProp` derive macro for custom `Resource`s, making them savable to `.gdron` and `.gdbin` formats. +- `GdPropSaver` and `GdPropLoader` macros for easily implementing `CustomFormatSaver` and `CustomFormatLoader` for `.gdron` and `.gdbin` formats. +- `serde_gd` module containing submodules to be used with `serde`, making it easier to implement `Serialize` and `Deserialize` for your + custom resources. ## In Development -> **This crate is not production ready** ⚠️ + +> **This crate is not production-ready** ⚠️ > -> This crate is early in development and its API can certainly change. Contributions, discussions and informed opinions are very welcome. +> This crate is early in development, and its API may change. Contributions, discussions, and informed opinions are very welcome. + +Features that will certainly be expanded upon: + +- Provide more submodules in the `serde_gd` module to support iterable `Gd` collections. +- Make the `gdron` and `gdbin` formats interchangeable for release mode with a custom `EditorExportPlugin`. +- Ensure everything works smoothly in compiled games, especially pointers to `.tres` resources after they are changed into `.res` format. + +## GdProp macro +Consider a scenario where you have a resource with a structure similar to the one below. You might contemplate transforming a `HashMap` +into Godot's Dictionary, but this conversion could entail sacrificing some of its advantages. On the other hand, for structs like +`StatModifiers` that you don't intend to handle as a `Resource`, there is a risk of loss when saving the resource with Godot's `ResourceSaver`. -Features that will be certainly expanded upon: -- add support for more compact formats, like binary and binary compressed -- make the `gdron` format interchangeable with future binary/binary compressed (for release mode) -- make everything work smoothly in compiled game (especially pointers to `.tres` resources) -## GdRonResource macro -So, imagine that you have a Resouce with a structure similiar to one below. You could potentially transform `HashMap` into Godot's Dictionary, but you would also sacrifice some of its pros. Though other structs, like `StatModifiers` which you don't intend to handle like a `Resource` is sure to be lost if saving resource with Godot's resource saver. ```rust -#[derive(GodotClass, Serialize, Deserialize, GdRonResource)] +#[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(base=Resource)] pub struct Statistics { - /// Current character level - #[export] + /// Current character level - only available fully on Godot editor side. Rest can be accessed by other Rust GodotClasses. + #[var] pub level: u32, /// All stats pub stats: HashMap, - /// Experience currently gained by the character. Every 100 experience points grants a level up with the chance of increasing stats + /// Experience currently gained by the character. Every 100 experience points grants a level up with the chance of increasing stats. pub exp: usize, - /// Amount of bane needed to be applied to the character - the higher, the more *boons* it amassed + /// Amount of bane needed to be applied to the character - the higher, the more *boons* it amassed. pub bane: usize, - /// Modifiers from [StatModEffect]. Key is the number of turns left, while value is the stat modifiers + /// Modifiers from [StatModEffect]. Key is the number of turns left, while value is the stat modifiers. pub effect_mods: HashMap, - /// Modifiers from equipped items. Key is the index of the item + /// Modifiers from equipped items. Key is the index of the item. pub item_mods: HashMap, /// Modifiers from character class pub class_mods: StatModifiers, } ``` -I presume that you saw the `GdRonResource` derive macro there, though. It implements `GdRonResource` trait, and makes the Resource saveable with our custom saver straight to `.gdron` file. +`GdProp` derive macro implements `GdProp` trait, and makes the Resource saveable with our `gd-props` straight to `.gdron` and `.gdbin` file. + +The `.gdron` format is a slightly modified `Ron` file, distinguished by the inclusion of a header containing the struct identifier or +resource class name. For a random object of the above structure, the resulting file might look like this: -`.gdron` is a very slightly modified `ron` file - it's only change is an inclusion of a header containing the struct identifier (or resource type identifier in Godot terms). For a random object of above structure it would look like that: ``` (gd_class:"Statistics",uid:"uid://bwgy4ec84b8xv") @@ -66,26 +95,37 @@ I presume that you saw the `GdRonResource` derive macro there, though. It implem ), ) ``` -File is recognizable by Godot editor, could be loaded through it and attached to a node or other Resource. + +The header, in this case, contains `Statistics`, signifying the class name of the serialized struct. This format is designed for +human-readable output during development, aiding in easy inspection and modification of the saved resources. Additionally, +Godot's `uid` path is also preserved there. + +On the other hand, the `.gdbin` format is based on the `MessagePack` format from the `rmp_serde` crate. It is intended for faster +serialization and deserialization times, especially in exported games, and in other aspects is analogous to `.gdron`. + +Both formats, whether human-readable or optimized for performance, offer the flexibility to choose the serialization strategy +that best suits your development and deployment needs. + +Both file are recognizable by Godot editor, can be loaded through it and attached to some Godot class. ## Bundled resources What if we have a Resource which contains another resource, which we would want to save as a bundled resource? There are two modules that handle this case: -- `godot_io::serde_gd::gd_option` - for `Option>` fields -- `godot_io::serde_gd::gd` - for `Gd` fields. +- `gd_props::serde_gd::gd_option` - for `Option>` fields, +- `gd_props::serde_gd::gd` - for `Gd` fields. There are some requirements for this to work: -- `T` needs to be User-defined `GodotClass` inheriting from `Resource` -- `T` needs to derive `Serialize` and `Deserialize` +- `T` needs to be User-defined `GodotClass` inheriting from `Resource`, +- `T` needs to derive `Serialize` and `Deserialize`. ### Example ```rust -#[derive(GodotClass, Serialize, Deserialize, GdRonResource)] +#[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(base=Resource)] pub struct CharacterData { #[export] affiliation: CharacterAffiliation, #[export] - #[serde(with="godot_io::serde_gd::gd_option")] + #[serde(with="gd_props::serde_gd::gd_option")] statistics: Option>, } ``` @@ -119,39 +159,41 @@ Upon saving, we receive file as below: ``` ## External Resources -All right, but what if we would like to preserve the sub resource as an External Resource, just in a way that regular resource saving in Godot works? It is possible with two additional modules: +If you desire to preserve a sub-resource as an External Resource, akin to regular resource saving in Godot, `gd-props` provides two additional modules: -- `godot_io::serde_gd::ext_option` - for `Option>` fields -- `godot_io::serde_gd::ext` - for `Gd` fields. +- `gd_props::serde_gd::ext_option` - designed for `Option>` fields. +- `gd_props::serde_gd::ext` - designed for `Gd` fields. -There are some requirements for this to work: -- `T` needs to be a `Resource` -- `T` needs to be a standalone `Resource` (needs to be saved to a file and loadable from it) +To enable this functionality, a few requirements must be met: + +- `T` needs to be a `Resource`. +- `T` must be a standalone `Resource` and be savable to and loadable from a file. -This approach has numerous benefits: -- `T` doesn't need to be a User-defined `GodotClass` (so built-in resources will work) -- External Resource instance will be reused whenever it is referenced +This approach offers several advantages: + +- `T` doesn't necessarily need to be a User-defined `GodotClass`, making it compatible with built-in resources. +- External Resource instances are reused whenever they are referenced, enhancing efficiency and reducing redundancy in the game data. ### Example ```rust -#[derive(GodotClass, Serialize, Deserialize, GdRonResource)] +#[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(base=Resource)] pub struct CharacterData { #[export] affiliation: CharacterAffiliation, - // As `statistics` is User-defined Resource, we could also use `gd_option` module + // As `statistics` is User-defined Resource, so we could also use `gd_option` module to bundle the Resource. #[export] - #[serde(with="godot_io::serde_gd::ext_option")] + #[serde(with="gd_props::serde_gd::ext_option")] statistics: Option>, #[export] - #[serde(with="godot_io::serde_gd::ext_option")] + #[serde(with="gd_props::serde_gd::ext_option")] nothing_is_here: Option>, #[export] - #[serde(with="godot_io::serde_gd::ext_option")] + #[serde(with="gd_props::serde_gd::ext_option")] texture: Option>, } ``` -Upon saving, we receive file as below: +Upon saving to `.gdron` format we receive file as below: ``` (gd_class:"CharacterData",uid:"uid://dfa37uvpqlnhq") ( @@ -170,41 +212,44 @@ Upon saving, we receive file as below: ) ``` -## GdRonSaver and GdRonLoader macros -As we now have rust Resources fully Serializable to `.gdron`, we now need a tools for saving and loading them - default `ResourceSaver` and `ResourceLoader` don't know and won't recognize our `.gdron` files. +## Custom Format Saving and Loading with `GdProp` + +Now that we have Rust resources fully serializable to `.gdron` and `.gdprop`, the next step is to provide tools for saving and loading +them within the Godot engine. The default `ResourceSaver` and `ResourceLoader` are unaware of our `.gdron` files. -`godot_io` comes with `GdRonSaver` and `GdRonLoader` derive macro, that can be used to easily create `CustomFormatSaver` and `CustomFormatLoader` and register our Resources to them! Their syntax is very similiar: +`gd-props` introduces two powerful derive macros, `GdPropSaver` and `GdPropLoader`, designed to simplify the creation of `CustomFormatSaver` +and `CustomFormatLoader` for formats it introduces. These macros enable the seamless registration of our resources to the Godot engine, +ensuring compatibility with the `.gdron` and `.gdbin` formats. + +The syntax for both macros is quite similar: ```rust // The derive itself -#[derive(GdRonSaver)] +#[derive(GdPropSaver)] // Attribute to register the GdRonResources to be handled by given Saver/Loader #[register(CharacterData, Statistics)] // Multiple `register` macro attributes could be provided, all identifiers contained within will be registered -#[register(AnotherGdResource)] +#[register(AnotherGodotResource)] ``` Full example - defining both Saver and Loader: ```rust -#[derive(GodotClass, GdRonSaver)] +#[derive(GodotClass, GdPropSaver)] #[class(base=ResourceFormatSaver, init, tool)] #[register(CharacterData, Statistics)] pub struct CustomRonSaver {} -#[godot_api] -impl CustomRonSaver {} -#[derive(GodotClass, GdRonLoader)] +#[derive(GodotClass, GdPropLoader)] #[class(base=ResourceFormatLoader, init, tool)] #[register(CharacterData)] #[register(Statistics)] -pub struct CustomRonLoader {} - -#[godot_api] -impl CustomRonLoader {} +pub struct CustomPropLoader {} ``` -All that is left for Godot Editor to use our new `ResourceFormatSaver` and `ResourceFormatLoader` is to register them upon loading out `gdextension`: +All that is left for Godot Editor to use our new `ResourceFormatSaver` and `ResourceFormatLoader` is to register them upon loading out +`gdextension` to Godot's `ResourceSaver` and `ResourceLoader`, respectively. It can be achieved with provided associated methods +in `GdPropSaver` and `GdPropLoader` traits. ```rust // lib.rs -use godot_io::traits::{GdRonLoader, GdRonSaver}; +use godot_io::traits::{GdPropLoader, GdPropSaver}; struct MyExtension; @@ -212,8 +257,8 @@ struct MyExtension; unsafe impl ExtensionLibrary for MyExtension { fn on_level_init(_level: InitLevel) { if _level == InitLevel::Scene { - CustomRonLoader::register_loader(); - CustomRonSaver::register_saver(); + CustomPropLoader::register_loader(); + CustomPropSaver::register_saver(); } } } diff --git a/doc_index.html b/doc_index.html index 79d5d4f..2b0896e 100644 --- a/doc_index.html +++ b/doc_index.html @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/godot_io_defs/Cargo.toml b/gd-props-defs/Cargo.toml similarity index 85% rename from godot_io_defs/Cargo.toml rename to gd-props-defs/Cargo.toml index ea9beb4..69da2b6 100644 --- a/godot_io_defs/Cargo.toml +++ b/gd-props-defs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "godot_io_defs" +name = "gd-props-defs" version = "0.1.0" edition = "2021" @@ -12,4 +12,4 @@ godot = { git = "https://github.com/godot-rust/gdext", branch = "master" } rmp-serde = "1.1.2" [dev-dependencies] -godot_io = { path = "../godot_io" } \ No newline at end of file +gd-props = { path = "../gd-props" } \ No newline at end of file diff --git a/gd-props-defs/src/errors.rs b/gd-props-defs/src/errors.rs new file mode 100644 index 0000000..c828a8e --- /dev/null +++ b/gd-props-defs/src/errors.rs @@ -0,0 +1,24 @@ +use core::fmt; + +use ron::error::SpannedError; + +#[derive(Debug, Clone)] +pub enum GdPropError { + OpenFileRead, + OpenFileWrite, + HeaderDeserialize(SpannedError), + HeaderSerialize, +} + +impl fmt::Display for GdPropError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GdPropError::OpenFileRead => write!(f, "Can't open file for reading"), + GdPropError::OpenFileWrite => write!(f, "Can't open file for writing"), + GdPropError::HeaderDeserialize(spanned) => { + write!(f, "Can't deserialize header: {}", spanned) + } + GdPropError::HeaderSerialize => write!(f, "Can't serialize header"), + } + } +} diff --git a/godot_io_defs/src/gd_meta.rs b/gd-props-defs/src/gd_meta.rs similarity index 88% rename from godot_io_defs/src/gd_meta.rs rename to gd-props-defs/src/gd_meta.rs index 57054b7..0eeea02 100644 --- a/godot_io_defs/src/gd_meta.rs +++ b/gd-props-defs/src/gd_meta.rs @@ -4,7 +4,7 @@ use godot::{ }; use serde::{Deserialize, Serialize}; -use crate::errors::GdRonError; +use crate::errors::GdPropError; #[derive(Serialize, Deserialize)] pub(crate) struct GdMetaHeader { @@ -13,10 +13,10 @@ pub(crate) struct GdMetaHeader { } impl GdMetaHeader { - pub fn read_from_gdron_header(path: GString) -> Result { + pub fn read_from_gdron_header(path: GString) -> Result { let fa = FileAccess::open(path.clone(), ModeFlags::READ); if fa.is_none() { - return Err(GdRonError::OpenFileRead); + return Err(GdPropError::OpenFileRead); } let mut fa = fa.unwrap(); let line = fa.get_line().to_string(); @@ -24,21 +24,21 @@ impl GdMetaHeader { let meta = ron::from_str::(&line); if let Err(error) = meta { - return Err(GdRonError::HeaderDeserialize(error)); + return Err(GdPropError::HeaderDeserialize(error)); } Ok(meta.unwrap()) } - pub fn write_to_gdron_header(&self, path: GString) -> Result<(), GdRonError> { + pub fn write_to_gdron_header(&self, path: GString) -> Result<(), GdPropError> { let ser_res = ron::to_string(&self); if ser_res.is_err() { - return Err(GdRonError::HeaderSerialize); + return Err(GdPropError::HeaderSerialize); } let ser = ser_res.unwrap(); let fa = FileAccess::open(path, ModeFlags::READ_WRITE); if fa.is_none() { - return Err(GdRonError::OpenFileWrite); + return Err(GdPropError::OpenFileWrite); } let mut fa = fa.unwrap(); @@ -46,17 +46,17 @@ impl GdMetaHeader { Ok(()) } - pub fn write_to_gdbin_header(&self, path: GString) -> Result<(), GdRonError> { + pub fn write_to_gdbin_header(&self, path: GString) -> Result<(), GdPropError> { let mut fa = - FileAccess::open(path, ModeFlags::READ_WRITE).ok_or(GdRonError::OpenFileWrite)?; + FileAccess::open(path, ModeFlags::READ_WRITE).ok_or(GdPropError::OpenFileWrite)?; self.write_to_gdbin_fa(&mut fa); Ok(()) } - pub fn read_from_gdbin_header(path: GString) -> Result { - let mut fa = FileAccess::open(path, ModeFlags::READ).ok_or(GdRonError::OpenFileRead)?; + pub fn read_from_gdbin_header(path: GString) -> Result { + let mut fa = FileAccess::open(path, ModeFlags::READ).ok_or(GdPropError::OpenFileRead)?; Ok(Self::read_from_gdbin_fa(&mut fa)) } diff --git a/godot_io_defs/src/gdres.rs b/gd-props-defs/src/gdprop.rs similarity index 57% rename from godot_io_defs/src/gdres.rs rename to gd-props-defs/src/gdprop.rs index e655ca9..32b5bb2 100644 --- a/godot_io_defs/src/gdres.rs +++ b/gd-props-defs/src/gdprop.rs @@ -11,72 +11,23 @@ use serde::{Deserialize, Serialize}; use crate::gd_meta::GdMetaHeader; -/// GdRes saveable resource +/// GdProp saveable resource /// /// Trait which provides methods to serialize and deserialize /// rust-defined [Resource](godot::engine::Resource) to: /// - `.gdbin` files, based on [MessagePack](rmp_serde) /// - `.gdron` files, based on [ron] -pub trait GdRes +pub trait GdProp where Self: Serialize + for<'de> Deserialize<'de> + GodotClass + Inherits, { - /// Struct identifier included in `gdron` file + /// Struct identifier included in `gdron` file. const HEAD_IDENT: &'static str; - /// Save object to a file located at `path` in `.gdbin` format - /// ## Arguments - /// - `path`: [GString] - path to the file - // fn save_bin(&self, path: GString) -> Error { - // let mut uid = -1; - // let mut resource_uid = ResourceUid::singleton(); - - // // Check if resource already exists and have UID assigned - // if let Ok(meta) = GdMetaHeader::read_from_gdbin_header(path.clone()) { - // uid = resource_uid.text_to_id(GString::from(meta.uid)); - // } - // // If UID couldn't be retrieved, create new id - // if uid == -1 { - // uid = resource_uid.create_id(); - // // If UID points to another path, remove the old UID - // } else if resource_uid.has_id(uid) && resource_uid.get_id_path(uid).ne(&path) { - // resource_uid.remove_id(uid); - // } - - // let meta = GdMetaHeader { - // gd_class: Self::HEAD_IDENT.to_string(), - // uid: resource_uid.id_to_text(uid).to_string(), - // }; - - // if let Some(mut access) = FileAccess::open(path.clone(), ModeFlags::WRITE) { - // meta.write_to_gdbin_fa(&mut access); - // let writer = FaWrapper::from(access); - // let mut serializer = Serializer::new(writer); - // let res = self.serialize(&mut serializer); - - // serializer.get_mut().get_mut().close(); - - // if let Err(error) = res { - // godot_error!("Error while serializing: {}", error); - // return Error::ERR_CANT_CREATE; - // } else { - // // Add new UID only after everything else went OK - // let uid_exists = resource_uid.has_id(uid); - // if uid_exists { - // resource_uid.set_id(uid, path) - // } else if !uid_exists { - // resource_uid.add_id(uid, path); - // } - - // serializer.get_mut().get_mut().close(); - // return Error::OK; - // } - // } - // Error::ERR_FILE_CANT_WRITE - // } + /// Save object to a file located at `path` in `.gdbin` format. fn save_bin(&self, path: GString) -> Error { let mut uid = -1; @@ -124,9 +75,7 @@ where Error::ERR_FILE_CANT_WRITE } - /// Load object from a file located at `path` in `.gdbin` format - /// ## Arguments - /// - `path`: [GString] - path to the file + /// Load object from a file located at `path` in `.gdbin` format. fn load_bin(path: GString) -> Variant { if let Some(mut access) = FileAccess::open(path.clone(), ModeFlags::READ) { let meta = GdMetaHeader::read_from_gdbin_fa(&mut access); @@ -165,51 +114,7 @@ where Error::ERR_FILE_CANT_OPEN.to_variant() } - /// Save object to a file located at `path` in [ron] format - /// ## Arguments - /// - `path`: [GString] - path to the file - // fn save_ron(&self, path: GString) -> Error { - // let mut uid = -1; - // let mut resource_uid = ResourceUid::singleton(); - - // // Check if resource already exists and have UID assigned - // if let Ok(meta) = GdMetaHeader::read_from_gdron_header(path.clone()) { - // uid = resource_uid.text_to_id(GString::from(meta.uid)); - // } - // // If UID couldn't be retrieved, or retrieved UID points to other path - // // create new UID - // if uid == -1 || (resource_uid.has_id(uid) && !resource_uid.get_id_path(uid).eq(&path)) { - // uid = resource_uid.create_id(); - // } - - // let meta = GdMetaHeader { - // gd_class: Self::HEAD_IDENT.to_string(), - // uid: resource_uid.id_to_text(uid).to_string(), - // }; - - // if let Some(access) = &mut FileAccess::open(path.clone(), ModeFlags::WRITE) { - // if let (Ok(ser_obj), Ok(ser_meta)) = ( - // ser::to_string_pretty(self, ser::PrettyConfig::default()), - // ser::to_string(&meta), - // ) { - // access.store_line(GString::from(ser_meta)); - // access.store_string(GString::from(ser_obj)); - // access.close(); - - // // Add new UID only after everything else went OK - // let uid_exists = resource_uid.has_id(uid); - // if uid_exists { - // resource_uid.set_id(uid, path) - // } else { - // resource_uid.add_id(uid, path); - // } - - // return Error::OK; - // } - // return Error::ERR_CANT_CREATE; - // } - // Error::ERR_FILE_CANT_WRITE - // } + /// Save object to a file located at `path` in [ron] format. fn save_ron(&self, path: GString) -> Error { let mut uid = -1; let mut resource_uid = ResourceUid::singleton(); @@ -252,24 +157,7 @@ where Error::ERR_FILE_CANT_WRITE } - /// Load object from a file located at `path` in [ron] format - /// ## Arguments - /// - `path`: [GString] - path to the file - // fn load_ron(path: GString) -> Variant { - // if let Some(access) = FileAccess::open(path.clone(), ModeFlags::READ) { - // let serialized = access.get_as_text().to_string(); - // let end_line = serialized.find('\n').unwrap(); - // let res = de::from_str::(&serialized[end_line + 1..serialized.len()]); - // match res { - // Ok(loaded) => return Gd::from_object(loaded).to_variant(), - // Err(error) => { - // godot_error!("{}", error); - // return Error::ERR_FILE_CANT_READ.to_variant(); - // } - // } - // } - // Error::ERR_FILE_CANT_OPEN.to_variant() - // } + /// Load object from a file located at `path` in [ron] format. fn load_ron(path: GString) -> Variant { if let Ok(mut gfile) = GFile::open(path, ModeFlags::READ) { let mut serialized = String::new(); diff --git a/godot_io_defs/src/gdres_io.rs b/gd-props-defs/src/gdprop_io.rs similarity index 75% rename from godot_io_defs/src/gdres_io.rs rename to gd-props-defs/src/gdprop_io.rs index 78a3be5..390c0a7 100644 --- a/godot_io_defs/src/gdres_io.rs +++ b/gd-props-defs/src/gdprop_io.rs @@ -10,16 +10,16 @@ use godot::{ }, }; -use crate::{errors::GdRonError, gd_meta::GdMetaHeader, gdres::GdRes}; +use crate::{errors::GdPropError, gd_meta::GdMetaHeader, gdprop::GdProp}; #[derive(PartialEq, Eq, Copy, Clone)] -pub(crate) enum GdResFormat { +pub(crate) enum GdPropFormat { GdRon, GdBin, None, } -impl GdResFormat { +impl GdPropFormat { const SUPPORTED_EXTENSIONS: [&'static str; 2] = ["gdbin", "gdron"]; pub(crate) fn get_supported_extensions() -> PackedStringArray { @@ -30,25 +30,25 @@ impl GdResFormat { } pub(crate) fn recognize_format(path: &str) -> Self { - if path.ends_with(GdResFormat::GdBin.get_recognized_extension()) { - return GdResFormat::GdBin; + if path.ends_with(GdPropFormat::GdBin.get_recognized_extension()) { + return GdPropFormat::GdBin; } - if path.ends_with(GdResFormat::GdRon.get_recognized_extension()) { - return GdResFormat::GdRon; + if path.ends_with(GdPropFormat::GdRon.get_recognized_extension()) { + return GdPropFormat::GdRon; } - GdResFormat::None + GdPropFormat::None } fn get_recognized_extension(&self) -> &str { match self { - GdResFormat::GdRon => "gdron", - GdResFormat::GdBin => "gdbin", - GdResFormat::None => "", + GdPropFormat::GdRon => "gdron", + GdPropFormat::GdBin => "gdbin", + GdPropFormat::None => "", } } } -pub trait GdResLoader +pub trait GdPropLoader where Self: GodotClass + Inherits @@ -56,11 +56,10 @@ where + IResourceFormatLoader + GodotDefault, { - /// Name under which the object registers in Godot as a singleton + /// Name under which the object registers in Godot as a singleton. const SINGLETON_NAME: &'static str; - /// Associated function to retrieve the pointer to object singleton - /// as [Gd]<[ResourceFormatLoader]> . + /// Associated function to retrieve the pointer to object singleton. fn loader_singleton() -> Gd { let mut engine = Engine::singleton(); // Need to check explicitly to not cause Godot error. @@ -82,21 +81,20 @@ where } } - /// Associated function to register the created [ResourceFormatLoader] - /// in Godot's [ResourceLoader](godot::engine::ResourceLoader). To be used in - /// [ExtensionLibrary](godot::prelude::ExtensionLibrary) implementation. + /// Associated function to register the created [ResourceFormatLoader] in Godot's [ResourceLoader](godot::engine::ResourceLoader). + /// To be used in [ExtensionLibrary](godot::prelude::ExtensionLibrary) implementation. /// /// ## Example /// ```no_run /// # mod loader { - /// # use godot_io::{GdResLoader, GdRes}; + /// # use gd_props::{GdPropLoader, GdProp}; /// # use godot::prelude::GodotClass; /// # use godot::engine::ResourceFormatLoader; /// # use serde::{Serialize, Deserialize}; - /// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] + /// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyResource; - /// # #[derive(GodotClass, GdResLoader)] + /// # #[derive(GodotClass, GdPropLoader)] /// # #[register(MyResource)] /// # #[class(tool, init, base=ResourceFormatLoader)] /// # pub struct MyResLoader; @@ -109,7 +107,7 @@ where /// /// unsafe impl ExtensionLibrary for MyGdExtension { /// fn on_level_init(_level: InitLevel) { - /// use godot_io::traits::GdResLoader as _; + /// use gd_props::traits::GdPropLoader as _; /// if _level == InitLevel::Scene { /// MyResLoader::register_loader(); /// } @@ -125,37 +123,37 @@ where #[doc(hidden)] /// Internal method to get resource UID from file - fn _int_get_uid(&self, path: GString) -> Result { + fn _int_get_uid(&self, path: GString) -> Result { let str_path = &path.to_string(); - match GdResFormat::recognize_format(str_path) { - GdResFormat::GdRon => { + match GdPropFormat::recognize_format(str_path) { + GdPropFormat::GdRon => { let meta = GdMetaHeader::read_from_gdron_header(path)?; let resource_uid = ResourceUid::singleton(); Ok(resource_uid.text_to_id(GString::from(meta.uid))) } - GdResFormat::GdBin => { + GdPropFormat::GdBin => { let meta = GdMetaHeader::read_from_gdbin_header(path)?; let resource_uid = ResourceUid::singleton(); Ok(resource_uid.text_to_id(GString::from(meta.uid))) } - GdResFormat::None => Err(GdRonError::OpenFileRead), + GdPropFormat::None => Err(GdPropError::OpenFileRead), } } #[doc(hidden)] /// Internal method to get resource type from file - fn _int_get_type(&self, path: GString) -> Result { + fn _int_get_type(&self, path: GString) -> Result { let str_path = &path.to_string(); - match GdResFormat::recognize_format(str_path) { - GdResFormat::GdRon => { + match GdPropFormat::recognize_format(str_path) { + GdPropFormat::GdRon => { let meta = GdMetaHeader::read_from_gdron_header(path)?; Ok(meta.gd_class) } - GdResFormat::GdBin => { + GdPropFormat::GdBin => { let meta = GdMetaHeader::read_from_gdbin_header(path)?; Ok(meta.gd_class) } - GdResFormat::None => Err(GdRonError::OpenFileRead), + GdPropFormat::None => Err(GdPropError::OpenFileRead), } } @@ -163,13 +161,13 @@ where /// Internal method to load file from file fn _int_load_file(&self, path: GString) -> Variant where - T: GdRes, + T: GdProp, { let str_path = path.to_string(); - match GdResFormat::recognize_format(&str_path) { - GdResFormat::GdRon => T::load_ron(path), - GdResFormat::GdBin => T::load_bin(path), - GdResFormat::None => { + match GdPropFormat::recognize_format(&str_path) { + GdPropFormat::GdRon => T::load_ron(path), + GdPropFormat::GdBin => T::load_bin(path), + GdPropFormat::None => { godot_warn!("Unrecognized format for: {}", &path); godot::engine::global::Error::ERR_FILE_UNRECOGNIZED.to_variant() } @@ -179,11 +177,11 @@ where #[doc(hidden)] /// Internal method to get the supported extensions fn _int_get_recognized_extensions(&self) -> PackedStringArray { - GdResFormat::get_supported_extensions() + GdPropFormat::get_supported_extensions() } } -pub trait GdResSaver +pub trait GdPropSaver where Self: GodotClass + Inherits @@ -224,14 +222,14 @@ where /// ## Example /// ```no_run /// # mod saver { - /// # use godot_io::{GdResSaver, GdRes}; + /// # use gd_props::{GdPropSaver, GdProp}; /// # use godot::prelude::GodotClass; /// # use godot::engine::ResourceFormatSaver; /// # use serde::{Serialize, Deserialize}; - /// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] + /// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyResource; - /// # #[derive(GodotClass, GdResSaver)] + /// # #[derive(GodotClass, GdPropSaver)] /// # #[register(MyResource)] /// # #[class(tool, init, base=ResourceFormatSaver)] /// # pub struct MyResSaver; @@ -244,7 +242,7 @@ where /// /// unsafe impl ExtensionLibrary for MyGdExtension { /// fn on_level_init(_level: InitLevel) { - /// use godot_io::traits::GdResSaver as _; + /// use gd_props::traits::GdPropSaver as _; /// if _level == InitLevel::Scene { /// MyResSaver::register_saver(); /// } @@ -261,16 +259,16 @@ where /// Internal function. Sets UID in file fn _int_set_uid(&mut self, path: GString, uid: i64) -> Error { let str_path = path.to_string(); - let format = GdResFormat::recognize_format(&str_path); + let format = GdPropFormat::recognize_format(&str_path); - if format == GdResFormat::None { + if format == GdPropFormat::None { return Error::ERR_FILE_UNRECOGNIZED; } let meta_res = match format { - GdResFormat::GdRon => GdMetaHeader::read_from_gdron_header(path.clone()), - GdResFormat::GdBin => GdMetaHeader::read_from_gdbin_header(path.clone()), - GdResFormat::None => unreachable!(), + GdPropFormat::GdRon => GdMetaHeader::read_from_gdron_header(path.clone()), + GdPropFormat::GdBin => GdMetaHeader::read_from_gdbin_header(path.clone()), + GdPropFormat::None => unreachable!(), }; match meta_res { @@ -288,9 +286,9 @@ where meta.uid = resource_uid.id_to_text(uid).to_string(); let write_res = match format { - GdResFormat::GdRon => meta.write_to_gdron_header(path.clone()), - GdResFormat::GdBin => meta.write_to_gdbin_header(path.clone()), - GdResFormat::None => unreachable!(), + GdPropFormat::GdRon => meta.write_to_gdron_header(path.clone()), + GdPropFormat::GdBin => meta.write_to_gdbin_header(path.clone()), + GdPropFormat::None => unreachable!(), }; if write_res.is_err() { @@ -320,18 +318,18 @@ where /// Internal function. Save to file in one of the recognized formats fn _int_save_to_file(&mut self, obj: Gd, path: GString) -> Error where - T: GdRes, + T: GdProp, { let str_path = path.to_string(); - match GdResFormat::recognize_format(&str_path) { - GdResFormat::GdRon => obj.bind().save_ron(path), - GdResFormat::GdBin => obj.bind().save_bin(path), - GdResFormat::None => Error::ERR_UNCONFIGURED, + match GdPropFormat::recognize_format(&str_path) { + GdPropFormat::GdRon => obj.bind().save_ron(path), + GdPropFormat::GdBin => obj.bind().save_bin(path), + GdPropFormat::None => Error::ERR_UNCONFIGURED, } } fn _int_get_recognized_extensions(&self) -> PackedStringArray { - GdResFormat::get_supported_extensions() + GdPropFormat::get_supported_extensions() } } diff --git a/gd-props-defs/src/lib.rs b/gd-props-defs/src/lib.rs new file mode 100644 index 0000000..da41b68 --- /dev/null +++ b/gd-props-defs/src/lib.rs @@ -0,0 +1,10 @@ +pub mod errors; +pub(crate) mod gd_meta; +pub(crate) mod gdprop; +pub(crate) mod gdprop_io; +pub mod serde_gd; + +pub mod traits { + pub use super::gdprop::GdProp; + pub use super::gdprop_io::{GdPropLoader, GdPropSaver}; +} diff --git a/godot_io_defs/src/serde_gd.rs b/gd-props-defs/src/serde_gd.rs similarity index 98% rename from godot_io_defs/src/serde_gd.rs rename to gd-props-defs/src/serde_gd.rs index 1f96ff1..b221873 100644 --- a/godot_io_defs/src/serde_gd.rs +++ b/gd-props-defs/src/serde_gd.rs @@ -63,7 +63,7 @@ where /// #[derive(GodotClass, Serialize, Deserialize)] /// #[class(base=Resource)] /// struct OuterResource { -/// #[serde(with="godot_io::serde_gd::gd")] +/// #[serde(with="gd_props::serde_gd::gd")] /// inner: Gd /// } /// @@ -123,7 +123,7 @@ pub mod gd { /// #[derive(GodotClass, Serialize, Deserialize)] /// #[class(init, base=Resource)] /// struct OuterResource { -/// #[serde(with="godot_io::serde_gd::gd_option")] +/// #[serde(with="gd_props::serde_gd::gd_option")] /// #[export] /// inner: Option> /// } @@ -240,7 +240,7 @@ pub mod gd_vec { /// #[derive(GodotClass, Serialize, Deserialize)] /// #[class(init, base=Resource)] /// struct OuterResource { -/// #[serde(with="godot_io::serde_gd::ext")] +/// #[serde(with="gd_props::serde_gd::ext")] /// inner: Gd /// } /// @@ -319,7 +319,7 @@ pub mod ext { /// #[derive(GodotClass, Serialize, Deserialize)] /// #[class(init, base=Resource)] /// struct OuterResource { -/// #[serde(with="godot_io::serde_gd::ext_option")] +/// #[serde(with="gd_props::serde_gd::ext_option")] /// #[export] /// inner: Option> /// } @@ -363,6 +363,7 @@ pub mod ext_option { } } +#[doc(hidden)] pub mod ext_vec { use crate::gd_meta::{GdExtResource, GdMetaExt}; diff --git a/godot_io_derive/Cargo.toml b/gd-props-macros/Cargo.toml similarity index 72% rename from godot_io_derive/Cargo.toml rename to gd-props-macros/Cargo.toml index eba7239..2a62e30 100644 --- a/godot_io_derive/Cargo.toml +++ b/gd-props-macros/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "godot_io_derive" +name = "gd-props-macros" version = "0.1.0" edition = "2021" @@ -13,10 +13,9 @@ proc-macro2 = "^1.0" quote = "^1.0" ron = "^0.8" serde = { version = "^1", features = ["derive"] } -# godot = {path = "../../../GODOT/gdext/godot"} godot = { git = "https://github.com/godot-rust/gdext", branch = "master" } venial = "^0.5.0" -godot_io_defs = { path = "../godot_io_defs" } +gd-props-defs = { path = "../gd-props-defs" } [dev-dependencies] -godot_io = { path = "../godot_io" } \ No newline at end of file +gd-props = { path = "../gd-props" } \ No newline at end of file diff --git a/godot_io_derive/src/gdres.rs b/gd-props-macros/src/gdprop.rs similarity index 92% rename from godot_io_derive/src/gdres.rs rename to gd-props-macros/src/gdprop.rs index afcaf39..7ebb9cc 100644 --- a/godot_io_derive/src/gdres.rs +++ b/gd-props-macros/src/gdprop.rs @@ -15,12 +15,12 @@ pub fn derive_saver(decl: Declaration) -> Result { Ok(quote!( use ::godot::engine::IResourceFormatSaver as _; - use ::godot_io::traits::GdResSaver as _; + use ::gd_props::traits::GdPropSaver as _; #[godot::prelude::godot_api] impl ::godot::engine::IResourceFormatSaver for #struct_ident { fn save(&mut self, resource: godot::obj::Gd, path: godot::builtin::GString, _flags: u32) -> godot::engine::global::Error { - use ::godot_io::traits::GdRes; + use ::gd_props::traits::GdProp; let class = resource.get_class(); #( if class.eq(&::godot::builtin::GString::from(stringify!(#registers))) { @@ -49,7 +49,7 @@ pub fn derive_saver(decl: Declaration) -> Result { } } - impl ::godot_io::traits::GdResSaver for #struct_ident { + impl ::gd_props::traits::GdPropSaver for #struct_ident { const SINGLETON_NAME: &'static str = stringify!(#struct_ident); } )) @@ -66,7 +66,7 @@ pub fn derive_loader(decl: Declaration) -> Result { Ok(quote!( use ::godot::engine::IResourceFormatLoader as _; - use ::godot_io::traits::GdResLoader as _; + use ::gd_props::traits::GdPropLoader as _; #[godot::prelude::godot_api] impl ::godot::engine::IResourceFormatLoader for #struct_ident { @@ -85,7 +85,7 @@ pub fn derive_loader(decl: Declaration) -> Result { } fn get_resource_type(&self, path: godot::builtin::GString) -> godot::builtin::GString { - use ::godot_io::traits::GdRes; + use ::gd_props::traits::GdProp; if let Ok(struct_name) = self._int_get_type(path) { #( if struct_name.eq(#registers::HEAD_IDENT) { @@ -97,7 +97,7 @@ pub fn derive_loader(decl: Declaration) -> Result { } fn load(&self, path: ::godot::builtin::GString, _original_path: godot::builtin::GString, _use_sub_threads: bool, _cache_mode: i32) -> godot::builtin::Variant { - use ::godot_io::traits::GdRes; + use ::gd_props::traits::GdProp; match self._int_get_type(path.clone()) { Err(error) => ::godot::prelude::godot_error!("Error getting '{}' resource type during load: {}", path, error), @@ -123,7 +123,7 @@ pub fn derive_loader(decl: Declaration) -> Result { } } - impl ::godot_io::traits::GdResLoader for #struct_ident { + impl ::gd_props::traits::GdPropLoader for #struct_ident { const SINGLETON_NAME: &'static str = stringify!(#struct_ident); } )) @@ -137,7 +137,7 @@ pub fn derive_resource(decl: Declaration) -> Result let name = &item.name; Ok(quote!( - impl ::godot_io::traits::GdRes for #name { + impl ::gd_props::traits::GdProp for #name { const HEAD_IDENT: &'static str = stringify!(#name); } )) diff --git a/godot_io_derive/src/lib.rs b/gd-props-macros/src/lib.rs similarity index 65% rename from godot_io_derive/src/lib.rs rename to gd-props-macros/src/lib.rs index f48dcc7..567e442 100644 --- a/godot_io_derive/src/lib.rs +++ b/gd-props-macros/src/lib.rs @@ -1,84 +1,84 @@ use crate::translate::translate; use proc_macro::{self, TokenStream}; -pub(crate) mod gdres; +pub(crate) mod gdprop; pub(crate) mod translate; pub(crate) mod utils; -/// Macro used to implement [GdRes](godot_io_defs::traits::GdRes) trait, which makes a rust-defined Godot [Resource](godot::engine::Resource) +/// Macro used to implement [GdProp](gd_props_defs::traits::GdProp) trait, which makes a rust-defined Godot [Resource](godot::engine::Resource) /// saveable and loadable to and from `.gdron` and `.gdbin` formats. /// -/// Provides compatibility with [GdResSaver] and [GdResLoader] deriving structs. +/// Provides compatibility with [GdPropSaver] and [GdPropLoader] deriving structs. /// /// ## Example /// ```no_run /// use godot::prelude::GodotClass; -/// use godot_io::GdRes; +/// use gd_props::GdProp; /// use serde::{Serialize, Deserialize}; /// -/// #[derive(GodotClass, Serialize, Deserialize, GdRes)] +/// #[derive(GodotClass, Serialize, Deserialize, GdProp)] /// #[class(init, base=Resource)] /// struct MyResource {} /// ``` -#[proc_macro_derive(GdRes)] +#[proc_macro_derive(GdProp)] pub fn derive_gd_resource(input: TokenStream) -> TokenStream { - translate(input, gdres::derive_resource) + translate(input, gdprop::derive_resource) } -/// Create resource loader for [GdRes](godot_io_defs::traits::GdRes) resources. +/// Create resource loader for [GdProp](gd_props_defs::traits::GdProp) resources. /// -/// Macro used to implement [GdResLoader](godot_io_defs::traits::GdResLoader) trait for a bare rust-defined -/// [ResourceFormatLoader](godot::engine::ResourceFormatLoader), allowing registered resources deriving [GdRes] to be +/// Macro used to implement [GdPropLoader](gd_props_defs::traits::GdPropLoader) trait for a bare rust-defined +/// [ResourceFormatLoader](godot::engine::ResourceFormatLoader), allowing registered resources deriving [GdProp] to be /// loaded with it from `.gdron` and `.gdbin` files. /// /// Alongside implementing above trait, macro also implements [IResourceFormatLoader](godot::engine::IResourceFormatLoader), /// so you can't implement it yourself. /// /// ## Macro attributes -/// - `#[register(MyGdResource, MyOtherGdResource)]` - registers `Resource`s deriving [GdRes] to be handled by this +/// - `#[register(MyGdPropource, MyOtherGdPropource)]` - registers `Resource`s deriving [GdProp] to be handled by this /// struct. You can provide multiple resource in one attribute, you can also add multiple `register` attributes with /// resources. /// /// ## Example /// ```no_run /// # mod resource { -/// # use godot_io::GdRes; +/// # use gd_props::GdProp; /// # use godot::prelude::GodotClass; /// # use serde::{Serialize, Deserialize}; -/// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] +/// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyResource; -/// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] +/// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyOtherResource; /// # } /// # use resource::*; /// use godot::prelude::GodotClass; -/// use godot_io::GdResLoader; +/// use gd_props::GdPropLoader; /// -/// #[derive(GodotClass, GdResLoader)] +/// #[derive(GodotClass, GdPropLoader)] /// #[class(init, tool, base=ResourceFormatLoader)] /// #[register(MyResource, MyOtherResource)] /// pub struct MyRonLoader {} /// ``` /// -/// ## Register your `GdResLoader` +/// ## Register your `GdPropLoader` /// /// To make the Loader recognizable by editor, remember to add `tool` value to the `GodotClass` macro `#[class]` attribute. /// Additionally, you need to register the loader in the [ResourceLoader](godot::engine::ResourceLoader) -/// singleton at Godot runtime initialization. Recommended way of registration is to call `GdResourceLoader::register_loader()` associated +/// singleton at Godot runtime initialization. Recommended way of registration is to call `GdPropourceLoader::register_loader()` associated /// function in [ExtensionLibrary](godot::prelude::ExtensionLibrary) implementation: /// /// ```no_run /// # mod loader { -/// # use godot_io::{GdResLoader, GdRes}; +/// # use gd_props::{GdPropLoader, GdProp}; /// # use godot::prelude::GodotClass; /// # use godot::engine::ResourceFormatLoader; /// # use serde::{Serialize, Deserialize}; -/// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] +/// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyResource; -/// # #[derive(GodotClass, GdResLoader)] +/// # #[derive(GodotClass, GdPropLoader)] /// # #[register(MyResource)] /// # #[class(tool, init, base=ResourceFormatLoader)] /// # pub struct MyResLoader; @@ -91,72 +91,72 @@ pub fn derive_gd_resource(input: TokenStream) -> TokenStream { /// /// unsafe impl ExtensionLibrary for MyGdExtension { /// fn on_level_init(_level: InitLevel) { -/// use godot_io::traits::GdResLoader as _; +/// use gd_props::traits::GdPropLoader as _; /// if _level == InitLevel::Scene { /// MyResLoader::register_loader(); /// } /// } /// } /// ``` -#[proc_macro_derive(GdResLoader, attributes(register))] +#[proc_macro_derive(GdPropLoader, attributes(register))] pub fn derive_gd_loader(input: TokenStream) -> TokenStream { - translate(input, gdres::derive_loader) + translate(input, gdprop::derive_loader) } -/// Create resource saver for [GdRes](godot_io_defs::traits::GdRes) resources. +/// Create resource saver for [GdProp](gd_props_defs::traits::GdProp) resources. /// -/// Macro used to implement [GdResSaver](godot_io_defs::traits::GdResSaver) trait for a bare rust-defined -/// [ResourceFormatSaver](godot::engine::ResourceFormatSaver), allowing registered resources deriving [GdRes] to be +/// Macro used to implement [GdPropSaver](gd_props_defs::traits::GdPropSaver) trait for a bare rust-defined +/// [ResourceFormatSaver](godot::engine::ResourceFormatSaver), allowing registered resources deriving [GdProp] to be /// saved using it to `.gdron` and `.gdbin` files. /// /// Alongside implementing above trait, macro also implements [IResourceFormatSaver](godot::engine::IResourceFormatSaver), /// so you can't implement it yourself. /// /// ## Macro attributes -/// - `#[register(MyGdResource, MyOtherGdResource)]` - registers `Resource`s deriving [GdRes] to be handled by this +/// - `#[register(MyGdPropource, MyOtherGdPropource)]` - registers `Resource`s deriving [GdProp] to be handled by this /// struct. You can provide multiple resource in one attribute, you can also add multiple `register` attributes with /// resources. /// /// ## Example /// ```no_run /// # mod resource { -/// # use godot_io::GdRes; +/// # use gd_props::GdProp; /// # use godot::prelude::GodotClass; /// # use serde::{Serialize, Deserialize}; -/// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] +/// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyResource; -/// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] +/// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyOtherResource; /// # } /// # use resource::*; /// use godot::prelude::GodotClass; -/// use godot_io::GdResSaver; +/// use gd_props::GdPropSaver; /// -/// #[derive(GodotClass, GdResSaver)] +/// #[derive(GodotClass, GdPropSaver)] /// #[class(init, tool, base=ResourceFormatSaver)] /// #[register(MyResource, MyOtherResource)] /// pub struct MyRonSaver {} /// ``` /// -/// ## Register your `GdResSaver` +/// ## Register your `GdPropSaver` /// /// To make the Saver recognizable by editor, remember to add `tool` value to the `GodotClass` macro `#[class]` attribute. /// Additionally, you need to register the saver in the [ResourceSaver](godot::engine::ResourceSaver) -/// singleton at Godot runtime initialization. Recommended way of registration is to call `GdResourceSaver::register_saver()` associated +/// singleton at Godot runtime initialization. Recommended way of registration is to call `GdPropourceSaver::register_saver()` associated /// function in [ExtensionLibrary](godot::prelude::ExtensionLibrary) implementation: /// /// ```no_run /// # mod saver { -/// # use godot_io::{GdResSaver, GdRes}; +/// # use gd_props::{GdPropSaver, GdProp}; /// # use godot::prelude::GodotClass; /// # use godot::engine::ResourceFormatSaver; /// # use serde::{Serialize, Deserialize}; -/// # #[derive(GodotClass, GdRes, Serialize, Deserialize)] +/// # #[derive(GodotClass, GdProp, Serialize, Deserialize)] /// # #[class(init, base=Resource)] /// # pub struct MyResource; -/// # #[derive(GodotClass, GdResSaver)] +/// # #[derive(GodotClass, GdPropSaver)] /// # #[register(MyResource)] /// # #[class(tool, init, base=ResourceFormatSaver)] /// # pub struct MyResSaver; @@ -169,14 +169,14 @@ pub fn derive_gd_loader(input: TokenStream) -> TokenStream { /// /// unsafe impl ExtensionLibrary for MyGdExtension { /// fn on_level_init(_level: InitLevel) { -/// use godot_io::traits::GdResSaver as _; +/// use gd_props::traits::GdPropSaver as _; /// if _level == InitLevel::Scene { /// MyResSaver::register_saver(); /// } /// } /// } /// ``` -#[proc_macro_derive(GdResSaver, attributes(register))] +#[proc_macro_derive(GdPropSaver, attributes(register))] pub fn derive_gd_saver(input: TokenStream) -> TokenStream { - translate(input, gdres::derive_saver) + translate(input, gdprop::derive_saver) } diff --git a/godot_io_derive/src/translate.rs b/gd-props-macros/src/translate.rs similarity index 100% rename from godot_io_derive/src/translate.rs rename to gd-props-macros/src/translate.rs diff --git a/godot_io_derive/src/utils.rs b/gd-props-macros/src/utils.rs similarity index 100% rename from godot_io_derive/src/utils.rs rename to gd-props-macros/src/utils.rs diff --git a/godot_io/Cargo.toml b/gd-props/Cargo.toml similarity index 67% rename from godot_io/Cargo.toml rename to gd-props/Cargo.toml index 063531d..de9cfec 100644 --- a/godot_io/Cargo.toml +++ b/gd-props/Cargo.toml @@ -1,14 +1,13 @@ [package] -name = "godot_io" +name = "gd-props" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -godot_io_derive = { path = "../godot_io_derive" } -godot_io_defs = { path = "../godot_io_defs" } -# godot = {path = "../../../GODOT/gdext/godot"} +gd-props-macros = { path = "../gd-props-macros" } +gd-props-defs = { path = "../gd-props-defs" } godot = { git = "https://github.com/godot-rust/gdext", branch = "master" } [dev-dependencies] diff --git a/godot_io/src/lib.rs b/gd-props/src/lib.rs similarity index 60% rename from godot_io/src/lib.rs rename to gd-props/src/lib.rs index 447f06a..890697f 100644 --- a/godot_io/src/lib.rs +++ b/gd-props/src/lib.rs @@ -2,7 +2,7 @@ //! //! Resources defined in Rust using [godot] crate are fully dependent on default Godot load/save rules - saved to //! `.tres` files, which requires all saveable properties to be Godot-recognized types annotated with `#[export]` attribute. -//! `godot_io` provides framework to make their saving and loading independent of Godot rules, using [serde] ruleset +//! `gd-props` provides framework to make their saving and loading independent of Godot rules, using [serde] ruleset //! instead. //! //! It provides two new custom formats, that the Resources will be saved to: @@ -11,25 +11,25 @@ //! //! The core functionality is based in three derive macros: //! -//! - [GdRes] - used to implement [GdRes](crate::traits::GdRes) trait to the user-defined [Resource](godot::engine::Resource), making +//! - [GdProp] - used to implement [GdProp](crate::traits::GdProp) trait to the user-defined [Resource](godot::engine::Resource), making //! it saveable and loadable to/from `.gdron` and `.gdbin` files. -//! - [GdResLoader] and [GdResSaver] - used to implement [GdResLoader](crate::traits::GdResLoader) and [GdResSaver](crate::traits::GdResSaver) +//! - [GdPropLoader] and [GdPropSaver] - used to implement [GdPropLoader](crate::traits::GdPropLoader) and [GdPropSaver](crate::traits::GdPropSaver) //! traits to user-defined [ResourceFormatLoader](godot::engine::ResourceFormatLoader) and [ResourceFormatSaver](godot::engine::ResourceFormatSaver), -//! which will be used by Godot to load and save [GdRes]-annotated resources. +//! which will be used by Godot to load and save [GdProp]-annotated resources. //! //! Additionally, [crate::serde_gd] module contains submodules to be used with `#[serde(with)]` attribute macro, making it possible -//! to serialize sub-resources contained within [GdRes]-annotated resource. +//! to serialize sub-resources contained within [GdProp]-annotated resource. -pub use godot_io_derive::GdRes; -pub use godot_io_derive::GdResLoader; -pub use godot_io_derive::GdResSaver; +pub use gd_props_macros::GdProp; +pub use gd_props_macros::GdPropLoader; +pub use gd_props_macros::GdPropSaver; /// Module containing traits implemented by provided macros. There shouldn't be a necessity to implement them directly by the user. pub mod traits { - pub use godot_io_defs::traits::GdRes; - pub use godot_io_defs::traits::GdResLoader; - pub use godot_io_defs::traits::GdResSaver; + pub use gd_props_defs::traits::GdProp; + pub use gd_props_defs::traits::GdPropLoader; + pub use gd_props_defs::traits::GdPropSaver; } -pub use godot_io_defs::errors; -pub use godot_io_defs::serde_gd; +pub use gd_props_defs::errors; +pub use gd_props_defs::serde_gd; diff --git a/godot_io_defs/src/errors.rs b/godot_io_defs/src/errors.rs deleted file mode 100644 index 921913d..0000000 --- a/godot_io_defs/src/errors.rs +++ /dev/null @@ -1,24 +0,0 @@ -use core::fmt; - -use ron::error::SpannedError; - -#[derive(Debug, Clone)] -pub enum GdRonError { - OpenFileRead, - OpenFileWrite, - HeaderDeserialize(SpannedError), - HeaderSerialize, -} - -impl fmt::Display for GdRonError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - GdRonError::OpenFileRead => write!(f, "Can't open file for reading"), - GdRonError::OpenFileWrite => write!(f, "Can't open file for writing"), - GdRonError::HeaderDeserialize(spanned) => { - write!(f, "Can't deserialize header: {}", spanned) - } - GdRonError::HeaderSerialize => write!(f, "Can't serialize header"), - } - } -} diff --git a/godot_io_defs/src/file_access.rs b/godot_io_defs/src/file_access.rs deleted file mode 100644 index 6c78343..0000000 --- a/godot_io_defs/src/file_access.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::{ - cmp, - io::{BufRead, Error as IoError, ErrorKind, Read, Seek, Write}, -}; - -use godot::{ - engine::{global::Error, FileAccess}, - prelude::Gd, -}; - -pub struct FaWrapper { - fa: Gd, - pos: u64, - buffer: Vec, -} - -impl FaWrapper { - const BUFFER_SIZE: usize = 4096; - - pub fn new(fa: Gd) -> Self { - let pos = fa.get_position(); - Self { - fa, - pos, - buffer: Vec::new(), - } - } - - /// Gets the reference to inner [FileAccess] - pub fn get(&self) -> &Gd { - &self.fa - } - - /// Gets the mutable reference to inner [FileAccess]. After making some operations - /// on it, call `update_pos` to keep the wrappers position aligned with [FileAccess] one - pub fn get_mut(&mut self) -> &mut Gd { - &mut self.fa - } - - /// Deconstructs the wrapper and retrieves the inner [FileAccess] - pub fn to_owned(self) -> Gd { - self.fa - } - - /// Updates the wrapper internal position with [FileAccess] position. Call after - /// making manual writes or reads on inner [FileAccess] through `get_mut()` reference - pub fn update_pos(&mut self) { - self.pos = self.fa.get_position() - } - - fn check_error(&self) -> Result<(), IoError> { - if self.fa.get_error() == Error::OK { - return Ok(()); - } - Err(IoError::new( - ErrorKind::Other, - format!("GodotError: {:?}", self.fa.get_error()), - )) - } -} - -impl From> for FaWrapper { - fn from(value: Gd) -> Self { - Self::new(value) - } -} - -impl Read for FaWrapper { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let length = self.fa.get_length(); - - if self.pos >= length { - return Ok(0); - } - - let remaining_bytes = (length - self.pos) as usize; - let bytes_to_read = cmp::min(buf.len(), remaining_bytes); - - if bytes_to_read == 0 { - return Ok(0); - } - let mut readen = 0; - while readen < bytes_to_read { - buf[readen] = self.fa.get_8(); - readen += 1; - self.pos += 1; - } - Ok(readen) - } -} - -impl Write for FaWrapper { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - let bytes_to_write = buf.len(); - let mut bytes_written = 0; - - while bytes_written < bytes_to_write { - self.fa.store_8(buf[bytes_written]); - bytes_written += 1; - self.pos += 1; - self.check_error()?; - } - Ok(bytes_written) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} - -impl Seek for FaWrapper { - fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { - match pos { - std::io::SeekFrom::Start(position) => { - self.fa.seek(position); - self.pos = position; - Ok(position) - } - std::io::SeekFrom::End(position) => { - if (self.fa.get_length() as i64) < position { - return Err(IoError::new( - ErrorKind::InvalidInput, - "Position set would be negative", - )); - } - self.fa.seek_end_ex().position(position).done(); - self.update_pos(); - Ok(self.pos) - } - std::io::SeekFrom::Current(position) => { - self.update_pos(); - let new_pos = self.pos as i64 + position; - if new_pos < 0 { - return Err(IoError::new( - ErrorKind::InvalidInput, - "Position set would be negative", - )); - } - let new_pos = new_pos as u64; - self.fa.seek(new_pos); - self.pos = new_pos; - Ok(self.pos) - } - } - } -} - -impl BufRead for FaWrapper { - fn fill_buf(&mut self) -> std::io::Result<&[u8]> { - self.buffer = vec![0; Self::BUFFER_SIZE]; - - let gd_buffer = self.fa.get_buffer(Self::BUFFER_SIZE as i64); - self.check_error()?; - - for i in 0..gd_buffer.len() { - self.buffer[i] = gd_buffer.get(i); - } - - Ok(&self.buffer[0..gd_buffer.len()]) - } - - fn consume(&mut self, amt: usize) { - _ = self.seek(std::io::SeekFrom::Current(amt as i64)); - } -} diff --git a/godot_io_defs/src/lib.rs b/godot_io_defs/src/lib.rs deleted file mode 100644 index 48f7134..0000000 --- a/godot_io_defs/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod errors; -pub(crate) mod gd_meta; -pub(crate) mod gdres; -pub(crate) mod gdres_io; -pub mod serde_gd; - -pub mod traits { - pub use super::gdres::GdRes; - pub use super::gdres_io::{GdResLoader, GdResSaver}; -} diff --git a/tests/rust/Cargo.toml b/tests/rust/Cargo.toml index 625bb1c..18de110 100644 --- a/tests/rust/Cargo.toml +++ b/tests/rust/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -godot_io = {path = "../../godot_io"} +gd-props = {path = "../../gd-props"} gd-rehearse = { git = "https://github.com/StatisMike/gd-rehearse", branch = "master" } godot = { git = "https://github.com/godot-rust/gdext", branch = "master" } serde = { version = "^1", features = ["derive"] } diff --git a/tests/rust/src/bench/gdbin.rs b/tests/rust/src/bench/gdbin.rs index 834386d..80cadc4 100644 --- a/tests/rust/src/bench/gdbin.rs +++ b/tests/rust/src/bench/gdbin.rs @@ -1,7 +1,7 @@ +use gd_rehearse::bench::gdbench; use godot::builtin::GString; use godot::engine::{IResourceFormatLoader, IResourceFormatSaver}; use godot::obj::{Gd, UserClass}; -use gd_rehearse::bench::gdbench; use serde::{Deserialize, Serialize}; use crate::remove_file; diff --git a/tests/rust/src/bench/gdron.rs b/tests/rust/src/bench/gdron.rs index cce12c5..5832e89 100644 --- a/tests/rust/src/bench/gdron.rs +++ b/tests/rust/src/bench/gdron.rs @@ -1,7 +1,7 @@ +use gd_rehearse::bench::gdbench; use godot::builtin::GString; use godot::engine::{IResourceFormatLoader, IResourceFormatSaver}; use godot::obj::{Gd, UserClass}; -use gd_rehearse::bench::gdbench; use serde::{Deserialize, Serialize}; use crate::remove_file; @@ -11,7 +11,7 @@ use crate::structs::{prop_handlers::PropSaver, resource::TestResource}; #[gdbench(repeat = 10)] fn serialize() -> bool { - let res = TestResource::new_random(4,4); + let res = TestResource::new_random(4, 4); let mut buffer = Vec::new(); @@ -41,7 +41,7 @@ fn gdron_save() -> bool { let mut saver = PropSaver::new_gd(); - let resource = TestResource::new_random(50,50); + let resource = TestResource::new_random(50, 50); saver .bind_mut() diff --git a/tests/rust/src/itest/gdbin.rs b/tests/rust/src/itest/gdbin.rs index 1f890b1..54ca990 100644 --- a/tests/rust/src/itest/gdbin.rs +++ b/tests/rust/src/itest/gdbin.rs @@ -1,8 +1,8 @@ +use gd_rehearse::itest::gditest; use godot::builtin::meta::FromGodot; use godot::builtin::GString; use godot::engine::{DirAccess, IResourceFormatLoader, IResourceFormatSaver}; use godot::obj::{Gd, UserClass}; -use gd_rehearse::itest::gditest; use crate::remove_file; use crate::structs::prop_handlers::{PropLoader, PropSaver}; diff --git a/tests/rust/src/itest/gdron.rs b/tests/rust/src/itest/gdron.rs index 2b705bb..266616b 100644 --- a/tests/rust/src/itest/gdron.rs +++ b/tests/rust/src/itest/gdron.rs @@ -1,8 +1,8 @@ +use gd_rehearse::itest::gditest; use godot::builtin::meta::FromGodot; use godot::builtin::GString; use godot::engine::{DirAccess, IResourceFormatLoader, IResourceFormatSaver}; use godot::obj::{Gd, UserClass}; -use gd_rehearse::itest::gditest; use crate::remove_file; use crate::structs::prop_handlers::{PropLoader, PropSaver}; diff --git a/tests/rust/src/itest/saver_loader.rs b/tests/rust/src/itest/saver_loader.rs index 8bca395..51ba784 100644 --- a/tests/rust/src/itest/saver_loader.rs +++ b/tests/rust/src/itest/saver_loader.rs @@ -3,7 +3,7 @@ use godot::{ engine::{ResourceLoader, ResourceSaver}, obj::UserClass, }; -use godot_io::traits::{GdResLoader, GdResSaver}; +use gd_props::traits::{GdPropLoader, GdPropSaver}; use gd_rehearse::itest::gditest; use crate::structs::{ diff --git a/tests/rust/src/lib.rs b/tests/rust/src/lib.rs index b155970..76e609f 100644 --- a/tests/rust/src/lib.rs +++ b/tests/rust/src/lib.rs @@ -28,8 +28,8 @@ use structs::prop_handlers::{PropLoader, PropSaver}; unsafe impl ExtensionLibrary for GodotIoTests { fn on_level_init(init: InitLevel) { if init == InitLevel::Scene { - use godot_io::traits::GdResLoader as _; - use godot_io::traits::GdResSaver as _; + use gd_props::traits::GdPropLoader as _; + use gd_props::traits::GdPropSaver as _; PropSaver::register_saver(); PropLoader::register_loader(); // _ = TestResource::singleton(); diff --git a/tests/rust/src/structs/prop_handlers.rs b/tests/rust/src/structs/prop_handlers.rs index cc5578d..98cc8f9 100644 --- a/tests/rust/src/structs/prop_handlers.rs +++ b/tests/rust/src/structs/prop_handlers.rs @@ -1,13 +1,13 @@ use super::resource::TestResource; +use gd_props::{GdPropLoader, GdPropSaver}; use godot::bind::GodotClass; -use godot_io::{GdResLoader, GdResSaver}; -#[derive(GodotClass, GdResSaver)] +#[derive(GodotClass, GdPropSaver)] #[class(init, base = ResourceFormatSaver, tool)] #[register(TestResource)] pub struct PropSaver; -#[derive(GodotClass, GdResLoader)] +#[derive(GodotClass, GdPropLoader)] #[class(init, base = ResourceFormatLoader, tool)] #[register(TestResource)] pub struct PropLoader; diff --git a/tests/rust/src/structs/resource.rs b/tests/rust/src/structs/resource.rs index 44152d6..7cfc21c 100644 --- a/tests/rust/src/structs/resource.rs +++ b/tests/rust/src/structs/resource.rs @@ -1,10 +1,10 @@ use std::collections::HashSet; +use gd_props::GdProp; use godot::{ obj::Gd, prelude::{godot_api, GodotClass}, }; -use godot_io::GdRes; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -25,7 +25,7 @@ impl InnerThing { } } -#[derive(GodotClass, Serialize, Deserialize, GdRes)] +#[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(init,base=Resource)] pub struct TestResource { set: HashSet, diff --git a/tests/rust/src/tests.rs b/tests/rust/src/tests.rs index 7dad6a5..91c8f7e 100644 --- a/tests/rust/src/tests.rs +++ b/tests/rust/src/tests.rs @@ -1,6 +1,6 @@ +use gd_props::{traits::*, *}; use godot::engine::IResource; use godot::prelude::{godot_api, Base, Gd, GodotClass, Resource}; -use godot_io::{traits::*, *}; use serde::{Deserialize, Serialize}; #[test] @@ -52,21 +52,21 @@ fn gd_option_can_serde() { #[test] fn loader_can_be_implemented() { - #[derive(GodotClass, Serialize, Deserialize, GdRes)] + #[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(init, base=Resource)] struct TestStruct {} #[godot_api] impl TestStruct {} - #[derive(GodotClass, Serialize, Deserialize, GdRes)] + #[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(init, base=Resource)] struct TestStruct2 {} #[godot_api] impl TestStruct2 {} - #[derive(GodotClass, GdResLoader)] + #[derive(GodotClass, GdPropLoader)] #[class(init, tool, base=ResourceFormatLoader)] #[register(TestStruct)] #[register(TestStruct2)] @@ -77,21 +77,21 @@ fn loader_can_be_implemented() { #[test] fn saver_can_be_implemented() { - #[derive(GodotClass, Serialize, Deserialize, GdRes)] + #[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(init, base=Resource)] struct TestStruct {} #[godot_api] impl TestStruct {} - #[derive(GodotClass, Serialize, Deserialize, GdRes)] + #[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(init, base=Resource)] struct TestStruct2 {} #[godot_api] impl TestStruct2 {} - #[derive(GodotClass, GdResSaver)] + #[derive(GodotClass, GdPropSaver)] #[class(init, tool, base=ResourceFormatSaver)] #[register(TestStruct)] #[register(TestStruct2)] @@ -102,7 +102,7 @@ fn saver_can_be_implemented() { #[test] fn gdres_trait_can_be_implemented() { - #[derive(GodotClass, Serialize, Deserialize, GdRes)] + #[derive(GodotClass, Serialize, Deserialize, GdProp)] #[class(init, base=Resource)] struct TestStruct {}