GDNative Rust ~ Importing external rs files to the main.rs file

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Joe0239

Say if I wanted to import another file and do all the programming in their would this work? Take a look at the example (to avoid reading so much code pay attention to the importing file, the _ready and the _physics_process in the main.rs file and the entire import_file.rs file)

main.rs

#[macro_use]
// Importing
mod import_file;

extern crate gdnative as godot;

use godot::init::{Property, PropertyHint, PropertyUsage};
use godot::GodotString;

struct RustTest {
    start: godot::Vector3,
    time: f32,
    rotate_speed: f64,
}

impl godot::NativeClass for RustTest {
    type Base = godot::MeshInstance;

    fn class_name() -> &'static str {
        "RustTest"
    }

    fn init(_owner: Self::Base) -> Self {
        Self::_init()
    }

    fn register_properties(builder: &godot::init::ClassBuilder<Self>) {
        builder.add_property(
            Property {
                name: "base/rotate_speed",
                default: 0.05,
                hint: PropertyHint::Range {
                    range: 0.05..1.0,
                    step: 0.01,
                    slider: true
                },
                getter: |this: &mut RustTest| this.rotate_speed,
                setter: |this: &mut RustTest, v| this.rotate_speed = v,
                usage: PropertyUsage::DEFAULT,
            }
        );

        builder.add_property(
            Property {
                name: "test/test_enum",
                default: GodotString::from_str("Hello"),
                hint: PropertyHint::Enum {
                    values: &[
                        "Hello",
                        "World",
                        "Testing",
                    ]
                },
                getter: |_: &mut RustTest| { GodotString::from_str("Hello") },
                setter: (),
                usage: PropertyUsage::DEFAULT,
            }
        );

        builder.add_property(
            Property {
                name: "test/test_flags",
                default: 0,
                hint: PropertyHint::Flags {
                    values: &["A", "B", "C", "D" ],
                },
                getter: |_: &mut RustTest| 0,
                setter: (),
                usage: PropertyUsage::DEFAULT,
            }
        );
    }
}

#[godot::methods]
impl RustTest {

    fn _init() -> Self {
        RustTest {
            start: godot::Vector3::new(0.0, 0.0, 0.0),
            time: 0.0,
            rotate_speed: 0.05,
        }
    }

    #[export]
    unsafe fn _ready(&mut self, mut owner: godot::MeshInstance) {
        import_file::ready();
    }

    #[export]
    unsafe fn _physics_process(&mut self, mut owner: godot::MeshInstance, delta: f64) {
        import_file::physics_process();
    }
}

fn init(handle: godot::init::InitHandle) {
    handle.add_class::<RustTest>();
}

godot_gdnative_init!();
godot_nativescript_init!(init);
godot_gdnative_terminate!();

import_file.rs

pub fn ready()
{
        owner.set_physics_process(true);
        self.start = owner.get_translation();
        godot_warn!("Start: {:?}", self.start);
        godot_warn!(
            "Parent name: {:?}",
            owner.get_parent().expect("Missing parent").get_name()
        );
}

pub fn physics_process()
{
       use godot::{Color, Vector3, SpatialMaterial};

        self.time += delta as f32;
        owner.rotate_y(self.rotate_speed * delta);

        let offset = Vector3::new(0.0, 1.0, 0.0) * self.time.cos() * 0.5;
        owner.set_translation(self.start + offset);

        if let Some(mat) = owner.get_surface_material(0) {
            let mut mat = mat.cast::<SpatialMaterial>().expect("Incorrect material");
            mat.set_albedo(Color::rgba(self.time.cos().abs(), 0.0, 0.0, 1.0));
        }

}

The reason why I wanted to do it like this is so I can avoid the entire boilerplate code and not really worry about it. Would this work or I can’t do this at all, or am I missing something? This is for GDNative Rust.

:bust_in_silhouette: Reply From: bluenote

That is perfectly possible, you mainly have to learn to structure Rust code. In general the mod and use statements allow you to use code from another file. It is probably a good idea to skim e.g. the chapter of the Rust book on managing projects.

There is also a good Rust project example on GitHub showing how to do that: godot-pong-rust

If you look at the lib.rs, you can see that it contains minimal code:

#[macro_use]
extern crate gdnative as godot;

extern crate rand;

mod ball;
mod hello_world;
mod paddle;
mod utils;

use self::{ball::Ball, hello_world::HelloWorld, paddle::Paddle};

// Function that registers all exposed classes to Godot
fn init(handle: godot::init::InitHandle) {
    handle.add_class::<HelloWorld>();
    handle.add_class::<Paddle>();
    handle.add_class::<Ball>();
}

// macros that create the entry-points of the dynamic library.
godot_gdnative_init!();
godot_nativescript_init!(init);
godot_gdnative_terminate!();

In simple term, the mod ... statements tell the compiler which files to look for. The use statements then make use of the public stuff in these submodules.