Skip to content

Commit d7bf5bd

Browse files
authored
Merge pull request #723 from godot-rust/qol/document-experimental-apis
Annotate `#[doc(cfg)]` for all generated experimental classes
2 parents b259a81 + fd5128b commit d7bf5bd

File tree

22 files changed

+123
-19
lines changed

22 files changed

+123
-19
lines changed

godot-bindings/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license = "MPL-2.0"
77
keywords = ["gamedev", "godot", "engine", "ffi", "sys"]
88
categories = ["game-engines", "graphics"]
99

10-
# Since features are additive and we want the user to user prebuilt by default, we need to have `prebuilt-godot` as the
10+
# Since features are additive, and we want the user to user prebuilt by default, we need to have `prebuilt-godot` as the
1111
# default feature. However, it's not possible to _disable_ the prebuilt dependency when specifying `api-custom` (without
1212
# requiring no-default-features), so we unfortunately still need to depend on prebuilt and just ignore it.
1313
# The artifact generator explicitly excludes that though (to avoid a quasi-circular dependency back to its repo).
@@ -34,3 +34,8 @@ which = { optional = true, version = "6" }
3434
[dev-dependencies]
3535
# For tests, we need regex unconditionally. Keep this in sync with above dependency.
3636
regex = { version = "1.5.5", default-features = false, features = ["std", "unicode-gencat"] }
37+
38+
# https://docs.rs/about/metadata
39+
[package.metadata.docs.rs]
40+
features = ["experimental-godot-api"]
41+
rustdoc-args = ["--cfg", "published_docs"]

godot-cell/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ proptest = ["dep:proptest"]
1212

1313
[dependencies]
1414
proptest = { version = "1.4.0", optional = true }
15+
16+
# https://docs.rs/about/metadata
17+
[package.metadata.docs.rs]
18+
features = ["experimental-godot-api"]
19+
rustdoc-args = ["--cfg", "published_docs"]

godot-codegen/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ regex = { version = "1.5.5", default-features = false, features = ["std", "unico
3333

3434
[build-dependencies]
3535
godot-bindings = { path = "../godot-bindings" } # emit_godot_version_cfg
36+
37+
# https://docs.rs/about/metadata
38+
[package.metadata.docs.rs]
39+
features = ["experimental-godot-api"]
40+
rustdoc-args = ["--cfg", "published_docs"]

godot-codegen/src/generator/builtins.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn make_builtin_class(class: &BuiltinClass, ctx: &mut Context) -> GeneratedBuilt
101101
} = make_builtin_methods(class, &class.methods, ctx);
102102

103103
let imports = util::make_imports();
104-
let enums = enums::make_enums(&class.enums);
104+
let enums = enums::make_enums(&class.enums, &TokenStream::new());
105105
let special_constructors = make_special_builtin_methods(class.name(), ctx);
106106

107107
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
@@ -245,5 +245,6 @@ fn make_builtin_method_definition(
245245
ptrcall_invocation,
246246
},
247247
safety_doc,
248+
&TokenStream::new(),
248249
)
249250
}

godot-codegen/src/generator/classes.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,42 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
9494
let api_level = class.api_level;
9595
let init_level = api_level.to_init_level();
9696

97+
// These attributes are for our nightly docs pipeline, which enables "only available in ..." labels in the HTML output. The website CI sets
98+
// RUSTFLAGS="--cfg published_docs" during the `cargo +nightly doc` invocation. They are applied to classes, interface traits, sidecar modules,
99+
// the notification enum, other enums and default-parameter extender structs.
100+
//
101+
// In other parts of the codebase, #[cfg] statements are replaced with #[doc(cfg)] using sed/sd. However, that doesn't work here, because
102+
// generated files are output in ./target/build/debug. Upon doing sed/sd replacements on these files, cargo doc will either treat them as
103+
// unchanged (doing nothing), or rebuild the generated files into a _different_ folder. Therefore, the generator itself must already provide
104+
// the correct attributes from the start.
105+
let (cfg_attributes, cfg_inner_attributes);
106+
if class.is_experimental {
107+
cfg_attributes = quote! {
108+
// #[cfg(feature = "experimental-godot-api")]
109+
#[cfg_attr(published_docs, doc(cfg(feature = "experimental-godot-api")))]
110+
};
111+
cfg_inner_attributes = quote! {
112+
// #![cfg(feature = "experimental-godot-api")]
113+
#![cfg_attr(published_docs, doc(cfg(feature = "experimental-godot-api")))]
114+
};
115+
} else {
116+
cfg_attributes = TokenStream::new();
117+
cfg_inner_attributes = TokenStream::new();
118+
};
119+
97120
let FnDefinitions {
98121
functions: methods,
99122
builders,
100-
} = make_class_methods(class, &class.methods, ctx);
123+
} = make_class_methods(class, &class.methods, &cfg_attributes, ctx);
101124

102-
let enums = enums::make_enums(&class.enums);
125+
let enums = enums::make_enums(&class.enums, &cfg_attributes);
103126
let constants = constants::make_constants(&class.constants);
104127
let inherits_macro = format_ident!("unsafe_inherits_transitive_{}", class_name.rust_ty);
105128
let deref_impl = make_deref_impl(class_name, &base_ty);
106129

107130
let all_bases = ctx.inheritance_tree().collect_all_bases(class_name);
108131
let (notification_enum, notification_enum_name) =
109-
notifications::make_notification_enum(class_name, &all_bases, ctx);
132+
notifications::make_notification_enum(class_name, &all_bases, &cfg_attributes, ctx);
110133

111134
// Associated "sidecar" module is made public if there are other symbols related to the class, which are not
112135
// in top-level godot::engine module (notification enums are not in the sidecar, but in godot::engine::notify).
@@ -120,11 +143,13 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
120143
has_sidecar_module,
121144
);
122145
let module_doc = docs::make_module_doc(class_name);
146+
123147
let virtual_trait = virtual_traits::make_virtual_methods_trait(
124148
class,
125149
&all_bases,
126150
&virtual_trait_str,
127151
&notification_enum_name,
152+
&cfg_attributes,
128153
view,
129154
);
130155

@@ -156,6 +181,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
156181
let imports = util::make_imports();
157182
let tokens = quote! {
158183
#![doc = #module_doc]
184+
#cfg_inner_attributes
159185

160186
#imports
161187
use crate::engine::notify::*;
@@ -166,6 +192,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
166192

167193
#[doc = #class_doc]
168194
#[doc = #construct_doc]
195+
#cfg_attributes
169196
#[derive(Debug)]
170197
#[repr(C)]
171198
pub struct #class_name {
@@ -406,12 +433,17 @@ fn make_bounds(class: &Class) -> (Ident, Ident) {
406433
(assoc_memory, assoc_dyn_memory)
407434
}
408435

409-
fn make_class_methods(class: &Class, methods: &[ClassMethod], ctx: &mut Context) -> FnDefinitions {
436+
fn make_class_methods(
437+
class: &Class,
438+
methods: &[ClassMethod],
439+
cfg_attributes: &TokenStream,
440+
ctx: &mut Context,
441+
) -> FnDefinitions {
410442
let get_method_table = class.api_level.table_global_getter();
411443

412-
let definitions = methods
413-
.iter()
414-
.map(|method| make_class_method_definition(class, method, &get_method_table, ctx));
444+
let definitions = methods.iter().map(|method| {
445+
make_class_method_definition(class, method, &get_method_table, cfg_attributes, ctx)
446+
});
415447

416448
FnDefinitions::expand(definitions)
417449
}
@@ -420,6 +452,7 @@ fn make_class_method_definition(
420452
class: &Class,
421453
method: &ClassMethod,
422454
get_method_table: &Ident,
455+
cfg_attributes: &TokenStream,
423456
ctx: &mut Context,
424457
) -> FnDefinition {
425458
let FnDirection::Outbound { hash } = method.direction() else {
@@ -489,5 +522,6 @@ fn make_class_method_definition(
489522
ptrcall_invocation,
490523
},
491524
None,
525+
cfg_attributes,
492526
)
493527
}

godot-codegen/src/generator/default_parameters.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub fn make_function_definition_with_defaults(
1717
sig: &dyn Function,
1818
code: &FnCode,
1919
full_fn_name: &Ident,
20+
cfg_attributes: &TokenStream,
2021
) -> (TokenStream, TokenStream) {
2122
let (default_fn_params, required_fn_params): (Vec<_>, Vec<_>) = sig
2223
.params()
@@ -62,6 +63,7 @@ pub fn make_function_definition_with_defaults(
6263
let builders = quote! {
6364
#[doc = #builder_doc]
6465
#[must_use]
66+
#cfg_attributes
6567
pub struct #builder_ty #builder_lifetime {
6668
// #builder_surround_ref
6769
#( #builder_fields, )*

godot-codegen/src/generator/enums.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use crate::models::domain::{Enum, Enumerator};
1313
use proc_macro2::TokenStream;
1414
use quote::{quote, ToTokens};
1515

16-
pub fn make_enums(enums: &[Enum]) -> TokenStream {
16+
pub fn make_enums(enums: &[Enum], cfg_attributes: &TokenStream) -> TokenStream {
1717
let definitions = enums.iter().map(make_enum_definition);
1818

1919
quote! {
20-
#( #definitions )*
20+
#( #cfg_attributes #definitions )*
2121
}
2222
}
2323

godot-codegen/src/generator/functions_common.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,12 @@ pub fn make_function_definition(
8686
sig: &dyn Function,
8787
code: &FnCode,
8888
safety_doc: Option<TokenStream>,
89+
cfg_attributes: &TokenStream,
8990
) -> FnDefinition {
9091
let has_default_params = default_parameters::function_uses_default_params(sig);
9192
let vis = if has_default_params {
9293
// Public API mapped by separate function.
93-
// Needs to be crate-public because default-arg builder lives outside of the module.
94+
// Needs to be crate-public because default-arg builder lives outside the module.
9495
quote! { pub(crate) }
9596
} else {
9697
make_vis(sig.is_private())
@@ -127,7 +128,12 @@ pub fn make_function_definition(
127128
};
128129

129130
let (default_fn_code, default_structs_code) = if has_default_params {
130-
default_parameters::make_function_definition_with_defaults(sig, code, &primary_fn_name)
131+
default_parameters::make_function_definition_with_defaults(
132+
sig,
133+
code,
134+
&primary_fn_name,
135+
cfg_attributes,
136+
)
131137
} else {
132138
(TokenStream::new(), TokenStream::new())
133139
};

godot-codegen/src/generator/notifications.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub fn make_notify_methods(class_name: &TyName, ctx: &mut Context) -> TokenStrea
6161
pub fn make_notification_enum(
6262
class_name: &TyName,
6363
all_bases: &Vec<TyName>,
64+
cfg_attributes: &TokenStream,
6465
ctx: &mut Context,
6566
) -> (Option<TokenStream>, Ident) {
6667
let Some(all_constants) = ctx.notification_constants(class_name) else {
@@ -98,6 +99,7 @@ pub fn make_notification_enum(
9899
/// notifications defined in base classes.
99100
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
100101
#[repr(i32)]
102+
#cfg_attributes
101103
pub enum #enum_name {
102104
#(
103105
#notification_enumerators_pascal = #notification_enumerators_ord,

godot-codegen/src/generator/utility_functions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub(crate) fn make_utility_function_definition(function: &UtilityFunction) -> To
7979
ptrcall_invocation,
8080
},
8181
None,
82+
&TokenStream::new(),
8283
);
8384

8485
// Utility functions have no builders.

0 commit comments

Comments
 (0)