Skip to content

Commit d00b9f5

Browse files
committed
Add bidirectional links between api::MyClass and api::my_class; remove empty nested modules
1 parent f185e61 commit d00b9f5

File tree

4 files changed

+67
-18
lines changed

4 files changed

+67
-18
lines changed

bindings_generator/src/api.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ impl GodotClass {
160160
pub fn is_getter(&self, name: &str) -> bool {
161161
self.properties.iter().any(|p| p.getter == name)
162162
}
163+
164+
/// Whether there is a snake_case module containing related symbols (nested types in C++)
165+
pub fn has_related_module(&self) -> bool {
166+
!self.enums.is_empty()
167+
}
163168
}
164169

165170
pub type ConstantName = String;

bindings_generator/src/documentation.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ pub fn official_doc_url(class: &GodotClass) -> String {
1717
)
1818
}
1919

20+
pub fn generate_module_doc(class: &GodotClass) -> TokenStream {
21+
let module_doc = format!(
22+
"This module contains types related to the API class [`{m}`][super::{m}].",
23+
m = class.name
24+
);
25+
26+
quote! {
27+
#![doc=#module_doc]
28+
}
29+
}
30+
2031
pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStream {
2132
let has_parent = !class.base_class.is_empty();
2233
let singleton_str = if class.singleton { "singleton " } else { "" };
@@ -26,7 +37,7 @@ pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStrea
2637
"unsafe"
2738
};
2839

29-
let summary_doc = if &class.name == "Reference" {
40+
let mut summary_doc = if &class.name == "Reference" {
3041
"Base class of all reference-counted types. Inherits `Object`.".into()
3142
} else if &class.name == "Object" {
3243
"The base class of most Godot classes.".into()
@@ -37,18 +48,20 @@ pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStrea
3748
name = class.name,
3849
base_class = class.base_class,
3950
ownership_type = ownership_type,
40-
singleton = singleton_str
51+
singleton = singleton_str,
4152
)
4253
} else {
4354
format!(
44-
"`{api_type} {singleton}class {name}` ({ownership_type}).",
55+
"`{api_type} {singleton}class {name}` ({ownership_type})",
4556
api_type = class.api_type,
4657
name = class.name,
4758
ownership_type = ownership_type,
4859
singleton = singleton_str,
4960
)
5061
};
5162

63+
append_related_module(&mut summary_doc, class);
64+
5265
let official_docs = format!(
5366
r#"## Official documentation
5467
@@ -145,3 +158,16 @@ fn list_base_classes(output: &mut impl Write, api: &Api, parent_name: &str) -> G
145158

146159
Ok(())
147160
}
161+
162+
// If present, links to the module with related (C++: nested) types.
163+
fn append_related_module(string: &mut String, class: &GodotClass) {
164+
use std::fmt::Write;
165+
if class.has_related_module() {
166+
write!(
167+
string,
168+
"\n\nThis class has related types in the [`{m}`][super::{m}] module.",
169+
m = class.module()
170+
)
171+
.expect("append to string via write!");
172+
}
173+
}

bindings_generator/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,20 @@ use std::io;
3838

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

41-
pub struct BindingResult {
42-
pub class_bindings: HashMap<String, TokenStream>,
41+
pub struct BindingResult<'a> {
42+
pub class_bindings: Vec<(&'a GodotClass, TokenStream)>,
4343
pub icalls: TokenStream,
4444
}
4545

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

4949
let class_bindings = api
5050
.classes
5151
.iter()
5252
.map(|class| {
5353
(
54-
class.name.clone(),
54+
class,
5555
generate_class_bindings(api, class, &mut icalls, docs),
5656
)
5757
})
@@ -84,6 +84,7 @@ fn generate_class_bindings(
8484
) -> TokenStream {
8585
// types and methods
8686
let types_and_methods = {
87+
let module_doc = generate_module_doc(class);
8788
let class_doc = generate_class_documentation(api, class);
8889
let class_struct = generate_class_struct(class, class_doc);
8990

@@ -98,6 +99,7 @@ fn generate_class_bindings(
9899
let class_impl = generate_class_impl(class, icalls, docs);
99100

100101
quote! {
102+
#module_doc
101103
#class_struct
102104
#enums
103105
#constants

gdnative-bindings/build.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,26 @@ fn generate(
5050
generated_file: &mut BufWriter<File>,
5151
binding_res: &BindingResult,
5252
) {
53-
for (class_name, code) in &binding_res.class_bindings {
53+
// Note: 'use super::*;' needs to be after content, as the latter may contain #![doc] attributes,
54+
// which need to be at the beginning of the module
55+
for (class, code) in &binding_res.class_bindings {
56+
let modifier = if class.has_related_module() {
57+
"pub"
58+
} else {
59+
"pub(crate)"
60+
};
5461
write!(
5562
generated_file,
5663
r#"
57-
pub mod {mod_name} {{
58-
use super::*;
64+
{modifier} mod {mod_name} {{
5965
{content}
66+
use super::*;
6067
}}
6168
pub use crate::generated::{mod_name}::private::{class_name};
6269
"#,
63-
mod_name = module_name_from_class_name(class_name),
64-
class_name = class_name,
70+
modifier = modifier,
71+
mod_name = module_name_from_class_name(&class.name),
72+
class_name = class.name,
6573
content = code,
6674
)
6775
.unwrap();
@@ -76,16 +84,18 @@ fn generate(
7684
generated_file: &mut BufWriter<File>,
7785
binding_res: &BindingResult,
7886
) {
79-
for (class_name, code) in &binding_res.class_bindings {
80-
let mod_name = module_name_from_class_name(class_name);
87+
for (class, code) in &binding_res.class_bindings {
88+
let mod_name = module_name_from_class_name(&class.name);
8189

8290
let mod_path = out_path.join(format!("{}.rs", mod_name));
8391
let mut mod_output = BufWriter::new(File::create(&mod_path).unwrap());
8492

8593
write!(
8694
&mut mod_output,
87-
r#"use super::*;
88-
{content}"#,
95+
r#"
96+
{content}
97+
use super::*;
98+
"#,
8999
content = code,
90100
)
91101
.unwrap();
@@ -96,16 +106,22 @@ fn generate(
96106
format_file(&mod_path);
97107
}
98108

109+
let modifier = if class.has_related_module() {
110+
"pub"
111+
} else {
112+
"pub(crate)"
113+
};
99114
writeln!(
100115
generated_file,
101116
r#"
102117
#[path = {:?}]
103-
pub mod {mod_name};
118+
{modifier} mod {mod_name};
104119
pub use crate::generated::{mod_name}::private::{class_name};
105120
"#,
106121
mod_path.display(),
122+
modifier = modifier,
107123
mod_name = mod_name,
108-
class_name = class_name,
124+
class_name = class.name,
109125
)
110126
.unwrap();
111127
}

0 commit comments

Comments
 (0)