Skip to content

Simplify module structure II #811

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions bindings_generator/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ impl GodotClass {
pub fn is_getter(&self, name: &str) -> bool {
self.properties.iter().any(|p| p.getter == name)
}

/// Whether there is a snake_case module containing related symbols (nested types in C++)
pub fn has_related_module(&self) -> bool {
!self.enums.is_empty()
}
}

pub type ConstantName = String;
Expand Down Expand Up @@ -406,9 +411,8 @@ impl Ty {
}
}
ty => {
let module = format_ident!("{}", module_name_from_class_name(ty));
let ty = format_ident!("{}", ty);
Ty::Object(syn::parse_quote! { crate::generated::#module::#ty })
Ty::Object(syn::parse_quote! { crate::generated::#ty })
}
}
}
Expand Down
17 changes: 11 additions & 6 deletions bindings_generator/src/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ use quote::{format_ident, quote};

use std::collections::HashMap;

pub(crate) fn generate_class_struct(class: &GodotClass) -> TokenStream {
pub(crate) fn generate_class_struct(class: &GodotClass, class_doc: TokenStream) -> TokenStream {
let class_name = format_ident!("{}", &class.name);

// dead_code: 'this' might not be read
// mod private: hide the type in the #module_name module, export it only in gdnative::api
quote! {
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub struct #class_name {
#[allow(dead_code)]
this: RawObject<Self>,
pub(crate) mod private {
#class_doc
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub struct #class_name {
#[allow(dead_code)]
pub(crate) this: super::RawObject<Self>,
}
}
use private::#class_name;
}
}

Expand Down
52 changes: 39 additions & 13 deletions bindings_generator/src/documentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,56 @@ pub fn class_doc_link(class: &GodotClass) -> String {

pub fn official_doc_url(class: &GodotClass) -> String {
format!(
"https://godot.readthedocs.io/en/latest/classes/class_{lower_case}.html",
"https://godot.readthedocs.io/en/stable/classes/class_{lower_case}.html",
lower_case = class.name.to_lowercase(),
)
}

pub fn generate_module_doc(class: &GodotClass) -> TokenStream {
let module_doc = format!(
"This module contains types related to the API class [`{m}`][super::{m}].",
m = class.name
);

quote! {
#![doc=#module_doc]
}
}

pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStream {
let has_parent = !class.base_class.is_empty();
let singleton_str = if class.singleton { "singleton " } else { "" };
let ownership_type = if class.is_refcounted() {
"reference counted"
let memory_type = if class.is_refcounted() {
"reference-counted"
} else {
"unsafe"
"manually managed"
};

let summary_doc = if &class.name == "Reference" {
let mut summary_doc = if &class.name == "Reference" {
"Base class of all reference-counted types. Inherits `Object`.".into()
} else if &class.name == "Object" {
"The base class of most Godot classes.".into()
"The base class of all classes in the Godot hierarchy.".into()
} else if has_parent {
format!(
"`{api_type} {singleton}class {name}` inherits `{base_class}` ({ownership_type}).",
"`{api_type} {singleton}class {name}` inherits `{base_class}` ({memory_type}).",
api_type = class.api_type,
name = class.name,
base_class = class.base_class,
ownership_type = ownership_type,
singleton = singleton_str
memory_type = memory_type,
singleton = singleton_str,
)
} else {
format!(
"`{api_type} {singleton}class {name}` ({ownership_type}).",
"`{api_type} {singleton}class {name}` ({memory_type})",
api_type = class.api_type,
name = class.name,
ownership_type = ownership_type,
memory_type = memory_type,
singleton = singleton_str,
)
};

append_related_module(&mut summary_doc, class);

let official_docs = format!(
r#"## Official documentation

Expand All @@ -66,7 +79,7 @@ The lifetime of this object is automatically managed through reference counting.
format!(
r#"## Memory management

Non reference counted objects such as the ones of this type are usually owned by the engine.
Non-reference-counted objects, such as the ones of this type, are usually owned by the engine.

`{name}` is a reference-only type. Persistent references can
only exist in the unsafe `Ref<{name}>` form.
Expand Down Expand Up @@ -113,7 +126,7 @@ This class is used to interact with Godot's editor."#
let safety_doc = r#"
## Safety

All types in the Godot API have "interior mutability" in Rust parlance.
All types in the Godot API have _interior mutability_ in Rust parlance.
To enforce that the official [thread-safety guidelines][thread-safety] are
followed, the typestate pattern is used in the `Ref` and `TRef` smart pointers,
and the `Instance` API. The typestate `Access` in these types tracks whether the
Expand Down Expand Up @@ -145,3 +158,16 @@ fn list_base_classes(output: &mut impl Write, api: &Api, parent_name: &str) -> G

Ok(())
}

// If present, links to the module with related (C++: nested) types.
fn append_related_module(string: &mut String, class: &GodotClass) {
use std::fmt::Write;
if class.has_related_module() {
write!(
string,
"\n\nThis class has related types in the [`{m}`][super::{m}] module.",
m = class.module()
)
.expect("append to string via write!");
}
}
28 changes: 17 additions & 11 deletions bindings_generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,20 @@ use std::io;

pub type GeneratorResult<T = ()> = Result<T, io::Error>;

pub struct BindingResult {
pub class_bindings: HashMap<String, TokenStream>,
pub struct BindingResult<'a> {
pub class_bindings: Vec<(&'a GodotClass, TokenStream)>,
pub icalls: TokenStream,
}

pub fn generate_bindings(api: &Api, docs: Option<&GodotXmlDocs>) -> BindingResult {
pub fn generate_bindings<'a>(api: &'a Api, docs: Option<&GodotXmlDocs>) -> BindingResult<'a> {
let mut icalls = HashMap::new();

let class_bindings = api
.classes
.iter()
.map(|class| {
(
class.name.clone(),
class,
generate_class_bindings(api, class, &mut icalls, docs),
)
})
Expand Down Expand Up @@ -84,9 +84,9 @@ fn generate_class_bindings(
) -> TokenStream {
// types and methods
let types_and_methods = {
let documentation = generate_class_documentation(api, class);

let class_struct = generate_class_struct(class);
let module_doc = generate_module_doc(class);
let class_doc = generate_class_documentation(api, class);
let class_struct = generate_class_struct(class, class_doc);

let enums = generate_enums(class);

Expand All @@ -99,7 +99,7 @@ fn generate_class_bindings(
let class_impl = generate_class_impl(class, icalls, docs);

quote! {
#documentation
#module_doc
#class_struct
#enums
#constants
Expand Down Expand Up @@ -195,17 +195,23 @@ pub(crate) mod test_prelude {

#[test]
fn sanity_test_generated_code() {
// Tests whether each generated snippet individually constitutes a valid AST representation of Rust code

let api = Api::new(include_str!("../../gdnative-bindings/api.json"));
let mut buffer = BufWriter::new(Vec::with_capacity(16384));
for class in &api.classes {
let mut icalls = HashMap::new();

let code = generate_class_documentation(&api, &class);
let code = generate_module_doc(&class);
write!(&mut buffer, "{}", code).unwrap();
validate_and_clear_buffer!(buffer);

let class_doc = generate_class_documentation(&api, &class);
write!(&mut buffer, "{}", code).unwrap();
write!(&mut buffer, "{}", quote! { struct Docs {} }).unwrap();
write!(&mut buffer, "{}", quote! { struct StructWithDocs {} }).unwrap();
validate_and_clear_buffer!(buffer);

let code = generate_class_struct(&class);
let code = generate_class_struct(&class, class_doc);
write!(&mut buffer, "{}", code).unwrap();
validate_and_clear_buffer!(buffer);

Expand Down
6 changes: 2 additions & 4 deletions bindings_generator/src/special_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,10 @@ pub fn generate_deref_impl(class: &GodotClass) -> TokenStream {
);

let class_name = format_ident!("{}", class.name);
let base_class_module = format_ident!("{}", class.base_class_module());
let base_class = format_ident!("{}", class.base_class);

let qualified_base_class = quote! {
crate::generated::#base_class_module::#base_class
crate::generated::#base_class
};

quote! {
Expand Down Expand Up @@ -190,11 +189,10 @@ pub fn generate_sub_class_impls<'a>(api: &'a Api, mut class: &'a GodotClass) ->
let mut tokens = TokenStream::new();

while let Some(base_class) = class.base_class(api) {
let base_class_module = format_ident!("{}", base_class.module());
let base_class_ident = format_ident!("{}", base_class.name);

tokens.extend(quote! {
unsafe impl SubClass<crate::generated::#base_class_module::#base_class_ident> for #class_name {}
unsafe impl SubClass<crate::generated::#base_class_ident> for #class_name {}
});

class = base_class;
Expand Down
2 changes: 1 addition & 1 deletion examples/array_export/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use gdnative::nativescript::export::property::hint::{ArrayHint, IntHint, RangeHint};
use gdnative::export::hint::{ArrayHint, IntHint, RangeHint};
use gdnative::prelude::*;

#[derive(NativeClass)]
Expand Down
2 changes: 1 addition & 1 deletion examples/spinning_cube/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use gdnative::api::MeshInstance;
use gdnative::prelude::*;

use gdnative::nativescript::export::property::{EnumHint, IntHint, StringHint};
use gdnative::export::hint::{EnumHint, IntHint, StringHint};

#[derive(gdnative::derive::NativeClass)]
#[inherit(MeshInstance)]
Expand Down
4 changes: 2 additions & 2 deletions gdnative-async/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use std::sync::Arc;
use futures_task::{LocalFutureObj, LocalSpawn, SpawnError};

use gdnative_core::core_types::{ToVariant, Variant};
use gdnative_core::export::{Method, NativeClass, Varargs};
use gdnative_core::log::{self, Site};
use gdnative_core::nativescript::export::{Method, Varargs};
use gdnative_core::nativescript::{NativeClass, RefInstance};
use gdnative_core::object::ownership::Shared;
use gdnative_core::object::RefInstance;

use crate::rt::Context;

Expand Down
10 changes: 3 additions & 7 deletions gdnative-async/src/rt.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
use std::marker::PhantomData;

use func_state::FuncState;
use gdnative_bindings::Object;
use gdnative_core::object::SubClass;

use gdnative_core::core_types::{GodotError, Variant};
use gdnative_core::nativescript::export::InitHandle;
use gdnative_core::nativescript::{Instance, RefInstance};
use gdnative_core::init::InitHandle;
use gdnative_core::object::ownership::Shared;
use gdnative_core::object::TRef;
use gdnative_core::object::{Instance, RefInstance, SubClass, TRef};

use crate::future;

mod bridge;
mod func_state;

use func_state::FuncState;

/// Context for creating `yield`-like futures in async methods.
pub struct Context {
func_state: Instance<FuncState, Shared>,
Expand Down
9 changes: 4 additions & 5 deletions gdnative-async/src/rt/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ use parking_lot::Mutex;

use gdnative_bindings::{Object, Reference};
use gdnative_core::core_types::{GodotError, Variant, VariantArray};
use gdnative_core::export::user_data::{ArcData, Map};
use gdnative_core::export::{ClassBuilder, Method, NativeClass, NativeClassMethods, Varargs};
use gdnative_core::godot_site;
use gdnative_core::nativescript::export::method::{Method, Varargs};
use gdnative_core::nativescript::export::ClassBuilder;
use gdnative_core::nativescript::user_data::{ArcData, Map};
use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance};
use gdnative_core::object::{ownership::Shared, TRef};
use gdnative_core::object::ownership::Shared;
use gdnative_core::object::{Instance, RefInstance, TRef};

use crate::future::Resume;

Expand Down
14 changes: 6 additions & 8 deletions gdnative-async/src/rt/func_state.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use gdnative_bindings::Reference;
use gdnative_core::core_types::{ToVariant, Variant, VariantType};
use gdnative_core::godot_site;
use gdnative_core::nativescript::export::method::StaticArgs;
use gdnative_core::nativescript::export::method::StaticArgsMethod;
use gdnative_core::nativescript::export::{
ClassBuilder, ExportInfo, PropertyUsage, Signal, SignalArgument,
use gdnative_core::export::user_data::{LocalCellData, Map, MapMut};
use gdnative_core::export::{
ClassBuilder, ExportInfo, NativeClass, NativeClassMethods, PropertyUsage, Signal,
SignalArgument, StaticArgs, StaticArgsMethod,
};
use gdnative_core::nativescript::user_data::LocalCellData;
use gdnative_core::nativescript::user_data::{Map, MapMut};
use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance};
use gdnative_core::godot_site;
use gdnative_core::object::ownership::{Shared, Unique};
use gdnative_core::object::{Instance, RefInstance};
use gdnative_derive::FromVarargs;

use crate::future::Resume;
Expand Down
Loading