@@ -10,22 +10,24 @@ mod markdown_converter;
10
10
use crate :: class:: { ConstDefinition , Field , FuncDefinition , SignalDefinition } ;
11
11
use proc_macro2:: { Ident , TokenStream } ;
12
12
use quote:: { quote, ToTokens } ;
13
- use venial:: * ;
14
13
15
- pub fn make_definition_docs (
14
+ /// Returns code containing the doc information of a `#[derive(GodotClass)] struct MyClass` declaration.
15
+ pub fn document_struct (
16
16
base : String ,
17
- description : & [ Attribute ] ,
18
- members : & [ Field ] ,
17
+ description : & [ venial :: Attribute ] ,
18
+ fields : & [ Field ] ,
19
19
) -> TokenStream {
20
20
let base_escaped = xml_escape ( base) ;
21
- let Some ( desc_escaped) = make_docs_from_attributes ( description) . map ( xml_escape) else {
21
+ let Some ( desc_escaped) = attribute_docs_to_bbcode ( description) . map ( xml_escape) else {
22
22
return quote ! { None } ;
23
23
} ;
24
- let members = members
24
+
25
+ let members = fields
25
26
. iter ( )
26
- . filter ( |x| x . var . is_some ( ) | x . export . is_some ( ) )
27
- . filter_map ( member )
27
+ . filter ( |field| field . var . is_some ( ) || field . export . is_some ( ) )
28
+ . filter_map ( format_member_xml )
28
29
. collect :: < String > ( ) ;
30
+
29
31
quote ! {
30
32
Some (
31
33
:: godot:: docs:: StructDocs {
@@ -37,79 +39,71 @@ pub fn make_definition_docs(
37
39
}
38
40
}
39
41
40
- pub fn make_inherent_impl_docs (
42
+ /// Returns code containing the doc information of a `#[godot_api] impl MyClass` declaration.
43
+ pub fn document_inherent_impl (
41
44
functions : & [ FuncDefinition ] ,
42
45
constants : & [ ConstDefinition ] ,
43
46
signals : & [ SignalDefinition ] ,
44
47
) -> TokenStream {
45
- /// Generates TokenStream containing field definitions for documented methods and documentation blocks for constants and signals.
46
- fn pieces (
47
- functions : & [ FuncDefinition ] ,
48
- signals : & [ SignalDefinition ] ,
49
- constants : & [ ConstDefinition ] ,
50
- ) -> TokenStream {
51
- let to_tagged = |s : String , tag : & str | -> String {
52
- if s. is_empty ( ) {
53
- s
54
- } else {
55
- format ! ( "<{tag}>{s}</{tag}>" )
56
- }
57
- } ;
58
-
59
- let signals_block = to_tagged (
60
- signals
61
- . iter ( )
62
- . filter_map ( make_signal_docs)
63
- . collect :: < String > ( ) ,
64
- "signals" ,
65
- ) ;
66
- let constants_block = to_tagged (
67
- constants
68
- . iter ( )
69
- . map ( |ConstDefinition { raw_constant } | raw_constant)
70
- . filter_map ( make_constant_docs)
71
- . collect :: < String > ( ) ,
72
- "constants" ,
73
- ) ;
74
-
75
- let methods = functions
76
- . iter ( )
77
- . filter_map ( make_method_docs)
78
- . collect :: < String > ( ) ;
79
-
80
- quote ! {
81
- :: godot:: docs:: InherentImplDocs {
82
- methods: #methods,
83
- signals_block: #signals_block,
84
- constants_block: #constants_block,
85
- }
48
+ let group_xml_block = |s : String , tag : & str | -> String {
49
+ if s. is_empty ( ) {
50
+ s
51
+ } else {
52
+ format ! ( "<{tag}>{s}</{tag}>" )
53
+ }
54
+ } ;
55
+
56
+ let signal_xml_elems = signals
57
+ . iter ( )
58
+ . filter_map ( format_signal_xml)
59
+ . collect :: < String > ( ) ;
60
+ let signals_block = group_xml_block ( signal_xml_elems, "signals" ) ;
61
+
62
+ let constant_xml_elems = constants
63
+ . iter ( )
64
+ . map ( |ConstDefinition { raw_constant } | raw_constant)
65
+ . filter_map ( format_constant_xml)
66
+ . collect :: < String > ( ) ;
67
+ let constants_block = group_xml_block ( constant_xml_elems, "constants" ) ;
68
+
69
+ let method_xml_elems = functions
70
+ . iter ( )
71
+ . filter_map ( format_method_xml)
72
+ . collect :: < String > ( ) ;
73
+
74
+ quote ! {
75
+ :: godot:: docs:: InherentImplDocs {
76
+ methods: #method_xml_elems,
77
+ signals_block: #signals_block,
78
+ constants_block: #constants_block,
86
79
}
87
80
}
88
- pieces ( functions, signals, constants)
89
81
}
90
82
91
- pub fn make_virtual_impl_docs ( vmethods : & [ ImplMember ] ) -> TokenStream {
92
- let virtual_methods = vmethods
83
+ /// Returns code containing the doc information of a `#[godot_api] impl ITrait for MyClass` declaration.
84
+ pub fn document_interface_trait_impl ( impl_members : & [ venial:: ImplMember ] ) -> TokenStream {
85
+ let interface_methods = impl_members
93
86
. iter ( )
94
87
. filter_map ( |x| match x {
95
88
venial:: ImplMember :: AssocFunction ( f) => Some ( f. clone ( ) ) ,
96
89
_ => None ,
97
90
} )
98
- . filter_map ( make_virtual_method_docs )
91
+ . filter_map ( format_virtual_method_xml )
99
92
. collect :: < String > ( ) ;
100
93
101
- quote ! { #virtual_methods }
94
+ quote ! { #interface_methods }
102
95
}
103
96
104
97
/// `///` is expanded to `#[doc = "…"]`.
105
- /// This function goes through and extracts the …
106
- fn siphon_docs_from_attributes ( doc : & [ Attribute ] ) -> impl Iterator < Item = String > + ' _ {
98
+ ///
99
+ /// This function goes through and extracts the "…" part.
100
+ fn extract_docs_from_attributes ( doc : & [ venial:: Attribute ] ) -> impl Iterator < Item = String > + ' _ {
107
101
doc. iter ( )
108
- // find #[doc]
102
+ // Find #[doc].
109
103
. filter ( |x| x. get_single_path_segment ( ) . is_some_and ( |x| x == "doc" ) )
110
- // #[doc = "…"]
104
+ // Limit to occurrences with syntax #[doc = "…"].
111
105
. filter_map ( |x| match & x. value {
112
- AttributeValue :: Equals ( _, doc) => Some ( doc) ,
106
+ venial :: AttributeValue :: Equals ( _, doc) => Some ( doc) ,
113
107
_ => None ,
114
108
} )
115
109
. flat_map ( |doc| {
@@ -122,7 +116,7 @@ fn siphon_docs_from_attributes(doc: &[Attribute]) -> impl Iterator<Item = String
122
116
}
123
117
124
118
fn xml_escape ( value : String ) -> String {
125
- // Most strings have no special characters, so this check helps avoid unnecessary string copying
119
+ // Most strings have no special characters, so this check helps avoid unnecessary string copying.
126
120
if !value. contains ( [ '&' , '<' , '>' , '"' , '\'' ] ) {
127
121
return value;
128
122
}
@@ -143,29 +137,33 @@ fn xml_escape(value: String) -> String {
143
137
result
144
138
}
145
139
146
- /// Calls [`siphon_docs_from_attributes`] and converts the result to BBCode
147
- /// for Godot's consumption.
148
- fn make_docs_from_attributes ( doc : & [ Attribute ] ) -> Option < String > {
149
- let doc = siphon_docs_from_attributes ( doc)
140
+ /// Calls [`extract_docs_from_attributes`] and converts the result to BBCode for Godot's consumption.
141
+ fn attribute_docs_to_bbcode ( doc : & [ venial:: Attribute ] ) -> Option < String > {
142
+ let doc = extract_docs_from_attributes ( doc)
150
143
. collect :: < Vec < String > > ( )
151
144
. join ( "\n " ) ;
152
145
153
146
( !doc. is_empty ( ) ) . then ( || markdown_converter:: to_bbcode ( & doc) )
154
147
}
155
148
156
- fn make_signal_docs ( signal : & SignalDefinition ) -> Option < String > {
149
+ fn format_venial_params_xml ( params : & venial:: Punctuated < venial:: FnParam > ) -> String {
150
+ let non_receiver_params = params. iter ( ) . filter_map ( |( param, _punct) | match param {
151
+ venial:: FnParam :: Receiver ( _) => None ,
152
+ venial:: FnParam :: Typed ( p) => Some ( ( & p. name , & p. ty ) ) ,
153
+ } ) ;
154
+
155
+ format_params_xml ( non_receiver_params)
156
+ }
157
+
158
+ fn format_signal_xml ( signal : & SignalDefinition ) -> Option < String > {
157
159
let name = & signal. fn_signature . name ;
158
- let params = params (
159
- signal
160
- . fn_signature
161
- . params
162
- . iter ( )
163
- . filter_map ( |( x, _) | match x {
164
- FnParam :: Receiver ( _) => None ,
165
- FnParam :: Typed ( y) => Some ( ( & y. name , & y. ty ) ) ,
166
- } ) ,
167
- ) ;
168
- let desc = make_docs_from_attributes ( & signal. external_attributes ) ?;
160
+ let name = xml_escape ( name. to_string ( ) ) ;
161
+
162
+ let params = format_venial_params_xml ( & signal. fn_signature . params ) ;
163
+
164
+ let desc = attribute_docs_to_bbcode ( & signal. external_attributes ) ?;
165
+ let desc = xml_escape ( desc) ;
166
+
169
167
Some ( format ! (
170
168
r#"
171
169
<signal name="{name}">
@@ -174,14 +172,12 @@ fn make_signal_docs(signal: &SignalDefinition) -> Option<String> {
174
172
{desc}
175
173
</description>
176
174
</signal>
177
- "# ,
178
- name = xml_escape( name. to_string( ) ) ,
179
- desc = xml_escape( desc) ,
175
+ "#
180
176
) )
181
177
}
182
178
183
- fn make_constant_docs ( constant : & Constant ) -> Option < String > {
184
- let docs = make_docs_from_attributes ( & constant. attributes ) ?;
179
+ fn format_constant_xml ( constant : & venial :: Constant ) -> Option < String > {
180
+ let docs = attribute_docs_to_bbcode ( & constant. attributes ) ?;
185
181
let name = constant. name . to_string ( ) ;
186
182
let value = constant
187
183
. initializer
@@ -197,11 +193,12 @@ fn make_constant_docs(constant: &Constant) -> Option<String> {
197
193
) )
198
194
}
199
195
200
- pub fn member ( member : & Field ) -> Option < String > {
201
- let docs = make_docs_from_attributes ( & member. attributes ) ?;
196
+ pub fn format_member_xml ( member : & Field ) -> Option < String > {
197
+ let docs = attribute_docs_to_bbcode ( & member. attributes ) ?;
202
198
let name = & member. name ;
203
199
let ty = member. ty . to_token_stream ( ) . to_string ( ) ;
204
200
let default = member. default_val . to_token_stream ( ) . to_string ( ) ;
201
+
205
202
Some ( format ! (
206
203
r#"<member name="{name}" type="{ty}" default="{default}">{docs}</member>"# ,
207
204
name = xml_escape( name. to_string( ) ) ,
@@ -211,76 +208,79 @@ pub fn member(member: &Field) -> Option<String> {
211
208
) )
212
209
}
213
210
214
- fn params < ' a , ' b > ( params : impl Iterator < Item = ( & ' a Ident , & ' b TypeExpr ) > ) -> String {
211
+ fn format_params_xml < ' a , ' b > (
212
+ params : impl Iterator < Item = ( & ' a Ident , & ' b venial:: TypeExpr ) > ,
213
+ ) -> String {
214
+ use std:: fmt:: Write ;
215
+
215
216
let mut output = String :: new ( ) ;
216
217
for ( index, ( name, ty) ) in params. enumerate ( ) {
217
- output. push_str ( & format ! (
218
+ write ! (
219
+ output,
218
220
r#"<param index="{index}" name="{name}" type="{ty}" />"# ,
219
221
name = xml_escape( name. to_string( ) ) ,
220
222
ty = xml_escape( ty. to_token_stream( ) . to_string( ) ) ,
221
- ) ) ;
223
+ )
224
+ . expect ( "write to string failed" ) ;
222
225
}
223
226
output
224
227
}
225
228
226
- pub fn make_virtual_method_docs ( method : Function ) -> Option < String > {
227
- let desc = make_docs_from_attributes ( & method. attributes ) ?;
229
+ fn format_virtual_method_xml ( method : venial:: Function ) -> Option < String > {
230
+ let desc = attribute_docs_to_bbcode ( & method. attributes ) ?;
231
+ let desc = xml_escape ( desc) ;
232
+
228
233
let name = method. name . to_string ( ) ;
229
- let ret = method
234
+ let name = xml_escape ( name) ;
235
+
236
+ let return_ty = method
230
237
. return_ty
231
- . map ( |x| x . to_token_stream ( ) . to_string ( ) )
238
+ . map ( |ty| ty . to_token_stream ( ) . to_string ( ) )
232
239
. unwrap_or_else ( || "void" . to_string ( ) ) ;
240
+ let return_ty = xml_escape ( return_ty) ;
241
+
242
+ let params = format_venial_params_xml ( & method. params ) ;
233
243
234
- let params = params ( method. params . iter ( ) . filter_map ( |( x, _) | match x {
235
- FnParam :: Receiver ( _) => None ,
236
- FnParam :: Typed ( y) => Some ( ( & y. name , & y. ty ) ) ,
237
- } ) ) ;
238
244
Some ( format ! (
239
245
r#"
240
246
<method name="_{name}">
241
- <return type="{ret }" />
247
+ <return type="{return_ty }" />
242
248
{params}
243
249
<description>
244
250
{desc}
245
251
</description>
246
252
</method>
247
- "# ,
248
- name = xml_escape( name) ,
249
- ret = xml_escape( ret) ,
250
- desc = xml_escape( desc) ,
253
+ "#
251
254
) )
252
255
}
253
256
254
- pub fn make_method_docs ( method : & FuncDefinition ) -> Option < String > {
255
- let desc = make_docs_from_attributes ( & method. external_attributes ) ?;
257
+ fn format_method_xml ( method : & FuncDefinition ) -> Option < String > {
258
+ let desc = attribute_docs_to_bbcode ( & method. external_attributes ) ?;
259
+ let desc = xml_escape ( desc) ;
260
+
256
261
let name = method
257
262
. registered_name
258
263
. clone ( )
259
264
. unwrap_or_else ( || method. rust_ident ( ) . to_string ( ) ) ;
260
- let ret = method
261
- . signature_info
262
- . return_type
263
- . to_token_stream ( )
264
- . to_string ( ) ;
265
- let params = params (
266
- method
267
- . signature_info
268
- . param_idents
269
- . iter ( )
270
- . zip ( & method. signature_info . param_types ) ,
271
- ) ;
265
+ let name = xml_escape ( name) ;
266
+
267
+ let signature = & method. signature_info ;
268
+
269
+ let return_ty = signature. return_type . to_token_stream ( ) . to_string ( ) ;
270
+ let return_ty = xml_escape ( return_ty) ;
271
+
272
+ let param_names_and_types = signature. param_idents . iter ( ) . zip ( & signature. param_types ) ;
273
+ let params = format_params_xml ( param_names_and_types) ;
274
+
272
275
Some ( format ! (
273
276
r#"
274
277
<method name="{name}">
275
- <return type="{ret }" />
278
+ <return type="{return_ty }" />
276
279
{params}
277
280
<description>
278
281
{desc}
279
282
</description>
280
283
</method>
281
- "# ,
282
- name = xml_escape( name) ,
283
- ret = xml_escape( ret) ,
284
- desc = xml_escape( desc) ,
284
+ "#
285
285
) )
286
286
}
0 commit comments