1use std::fmt::Write;
13
14use crate::abi::AbiParam;
15use crate::codegen::common::{emit_doc as common_emit_doc, DocCommentStyle};
16use crate::codegen::CodeWriter;
17use crate::model::{
18 AbiFn, CallShape, EnumBinding, ErrorBinding, FnBinding, InterfaceBinding, ModuleBinding,
19 StructBinding,
20};
21
22pub fn emit_doc(out: &mut String, doc: &Option<String>, indent: &str) {
24 common_emit_doc(out, doc, indent, DocCommentStyle::Javadoc);
25}
26
27pub fn params_str(params: &[AbiParam], prefix: &str) -> String {
29 params
30 .iter()
31 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name))
32 .collect::<Vec<_>>()
33 .join(", ")
34}
35
36fn export_macro(prefix: &str) -> String {
43 format!("{}_API", prefix.to_uppercase())
44}
45
46fn deprecated_macro(prefix: &str) -> String {
51 format!("{}_DEPRECATED", prefix.to_uppercase())
52}
53
54pub fn render_visibility_macros(out: &mut String, prefix: &str) {
80 let body = r#"#ifndef @U@_API
81# if defined(_WIN32) || defined(__CYGWIN__)
82# ifdef @U@_BUILD
83# define @U@_API __declspec(dllexport)
84# else
85# define @U@_API __declspec(dllimport)
86# endif
87# elif defined(__EMSCRIPTEN__)
88# define @U@_API __attribute__((used, visibility("default")))
89# elif defined(__GNUC__) && (__GNUC__ >= 4)
90# define @U@_API __attribute__((visibility("default")))
91# else
92# define @U@_API
93# endif
94#endif
95
96#ifndef @U@_DEPRECATED
97# if defined(_MSC_VER)
98# define @U@_DEPRECATED(msg) __declspec(deprecated(msg))
99# elif defined(__GNUC__) || defined(__clang__)
100# define @U@_DEPRECATED(msg) __attribute__((deprecated(msg)))
101# else
102# define @U@_DEPRECATED(msg)
103# endif
104#endif
105
106"#;
107 out.push_str(&body.replace("@U@", &prefix.to_uppercase()));
108}
109
110pub fn fn_decl(out: &mut String, f: &AbiFn, prefix: &str) {
114 let _ = writeln!(
115 out,
116 "{} {} {}({});",
117 export_macro(prefix),
118 f.ret.render_c(prefix),
119 f.symbol,
120 params_str(&f.params, prefix)
121 );
122}
123
124pub fn render_runtime_decls(out: &mut String, prefix: &str) {
134 let api = export_macro(prefix);
135 let _ = write!(
136 out,
137 "typedef uint64_t {prefix}_handle_t;\n\n\
138 typedef struct {prefix}_error {{ int32_t code; const char* message; }} {prefix}_error;\n\n\
139 {api} void {prefix}_error_clear({prefix}_error* err);\n\
140 {api} void {prefix}_free_string(const char* ptr);\n\
141 {api} void {prefix}_free_bytes(uint8_t* ptr, size_t len);\n\n\
142 /* Linear-memory allocator used by the Wasm JS glue to stage call\n \
143 arguments. Native consumers never call these; producers targeting\n \
144 WebAssembly must export them (the generated {prefix}.c provides\n \
145 malloc/free-backed defaults). */\n\
146 {api} uint8_t* {prefix}_alloc(uint32_t size);\n\
147 {api} void {prefix}_dealloc(uint8_t* ptr, uint32_t size);\n\n\
148 typedef struct {prefix}_cancel_token {prefix}_cancel_token;\n\
149 {api} {prefix}_cancel_token* {prefix}_cancel_token_create(void);\n\
150 {api} void {prefix}_cancel_token_cancel({prefix}_cancel_token* token);\n\
151 {api} bool {prefix}_cancel_token_is_cancelled(const {prefix}_cancel_token* token);\n\
152 {api} void {prefix}_cancel_token_destroy({prefix}_cancel_token* token);\n\n",
153 );
154}
155
156fn render_enum_constants(out: &mut String, e: &EnumBinding, type_name: &str) {
159 let mut w = CodeWriter::four_space();
160 w.doc(&e.doc, DocCommentStyle::Javadoc);
161 if e.variants.iter().any(|v| v.doc.is_some()) {
162 w.block("typedef enum {", format!("}} {type_name};"), |w| {
163 let last = e.variants.len();
164 for (i, v) in e.variants.iter().enumerate() {
165 w.doc(&v.doc, DocCommentStyle::Javadoc);
166 let comma = if i + 1 == last { "" } else { "," };
167 w.line(format!("{} = {}{comma}", v.c_const, v.value));
168 }
169 });
170 } else {
171 let variants: Vec<String> = e
172 .variants
173 .iter()
174 .map(|v| format!("{} = {}", v.c_const, v.value))
175 .collect();
176 w.line(format!(
177 "typedef enum {{ {} }} {type_name};",
178 variants.join(", ")
179 ));
180 }
181 out.push_str(&w.finish());
182}
183
184pub fn render_enum_decl(out: &mut String, e: &EnumBinding) {
186 render_enum_constants(out, e, &e.c_tag);
187}
188
189fn render_rich_enum_tag_decl(out: &mut String, e: &EnumBinding) {
194 let tag_enum = format!("{}_Tag", e.c_tag);
195 render_enum_constants(out, e, &tag_enum);
196}
197
198fn render_rich_enum_fn_decls(out: &mut String, e: &EnumBinding, prefix: &str) {
202 let Some(rich) = &e.rich else {
203 return;
204 };
205 let api = export_macro(prefix);
206 let tag = &e.c_tag;
207 emit_doc(out, &e.doc, "");
208 let _ = writeln!(out, "{api} int32_t {}(const {tag}* self);", rich.tag_symbol);
209 for v in &rich.variants {
210 emit_doc(out, &v.doc, "");
211 fn_decl(out, &v.create, prefix);
212 for field in &v.fields {
213 emit_doc(out, &field.doc, "");
214 let mut parts = vec![format!("const {tag}* self")];
215 parts.extend(
216 field
217 .getter_out_params
218 .iter()
219 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name)),
220 );
221 let _ = writeln!(
222 out,
223 "{api} {} {}({});",
224 field.getter_ret.render_c(prefix),
225 field.getter_symbol,
226 parts.join(", ")
227 );
228 }
229 }
230 let _ = writeln!(out, "{api} void {}({tag}* self);", rich.destroy_symbol);
231 out.push('\n');
232}
233
234fn render_struct_tags(out: &mut String, s: &StructBinding) {
240 let tag = &s.c_tag;
241 let _ = writeln!(out, "typedef struct {tag} {tag};");
242 if let Some(b) = &s.builder {
243 let bt = &b.builder_tag;
244 let _ = writeln!(out, "typedef struct {bt} {bt};");
245 }
246}
247
248fn render_struct_fn_decls(out: &mut String, s: &StructBinding, prefix: &str) {
253 let api = export_macro(prefix);
254 let tag = &s.c_tag;
255 emit_doc(out, &s.doc, "");
256 fn_decl(out, &s.create, prefix);
257 let _ = writeln!(out, "{api} void {}({tag}* ptr);", s.destroy_symbol);
258 for field in &s.fields {
259 emit_doc(out, &field.doc, "");
260 let mut parts = vec![format!("const {tag}* ptr")];
261 parts.extend(
262 field
263 .getter_out_params
264 .iter()
265 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name)),
266 );
267 let _ = writeln!(
268 out,
269 "{api} {} {}({});",
270 field.getter_ret.render_c(prefix),
271 field.getter_symbol,
272 parts.join(", ")
273 );
274 }
275 out.push('\n');
276
277 if let Some(b) = &s.builder {
278 let bt = &b.builder_tag;
279 let _ = writeln!(out, "{api} {bt}* {}(void);", b.new_symbol);
280 for (field, (_, setter)) in s.fields.iter().zip(&b.setters) {
281 emit_doc(out, &field.doc, "");
282 let _ = writeln!(
283 out,
284 "{api} void {setter}({bt}* builder, {});",
285 params_str(&field.value_params, prefix)
286 );
287 }
288 let _ = writeln!(
289 out,
290 "{api} {tag}* {}({bt}* builder, {prefix}_error* out_err);",
291 b.build_symbol
292 );
293 let _ = writeln!(out, "{api} void {}({bt}* builder);", b.destroy_symbol);
294 out.push('\n');
295 }
296}
297
298fn render_error_domain_decl(out: &mut String, e: &ErrorBinding) {
302 let mut w = CodeWriter::four_space();
303 w.doc(
304 &Some(format!(
305 "Error codes reported by throwing functions in the `{}` module tree.",
306 e.owner_path.replace('_', ".")
307 )),
308 DocCommentStyle::Javadoc,
309 );
310 if e.codes.iter().any(|c| c.doc.is_some()) {
311 w.block("typedef enum {", format!("}} {};", e.c_tag), |w| {
312 let last = e.codes.len();
313 for (i, c) in e.codes.iter().enumerate() {
314 w.doc(&c.doc, DocCommentStyle::Javadoc);
315 let comma = if i + 1 == last { "" } else { "," };
316 w.line(format!("{} = {}{comma}", c.c_const, c.value));
317 }
318 });
319 } else {
320 let codes: Vec<String> = e
321 .codes
322 .iter()
323 .map(|c| format!("{} = {}", c.c_const, c.value))
324 .collect();
325 w.line(format!(
326 "typedef enum {{ {} }} {};",
327 codes.join(", "),
328 e.c_tag
329 ));
330 }
331 out.push_str(&w.finish());
332}
333
334pub fn render_module_enum_defs(out: &mut String, module: &ModuleBinding) {
337 for e in &module.enums {
338 if e.is_rich() {
339 render_rich_enum_tag_decl(out, e);
340 } else {
341 render_enum_decl(out, e);
342 }
343 }
344 if let Some(err) = &module.error {
345 if err.declared_here {
346 render_error_domain_decl(out, err);
347 }
348 }
349}
350
351pub fn render_module_type_tags(out: &mut String, module: &ModuleBinding) {
355 for e in &module.enums {
357 if e.is_rich() {
358 let t = &e.c_tag;
359 let _ = writeln!(out, "typedef struct {t} {t};");
360 }
361 }
362 for s in &module.structs {
363 render_struct_tags(out, s);
364 }
365 for i in &module.interfaces {
366 let t = &i.c_tag;
367 let _ = writeln!(out, "typedef struct {t} {t};");
368 }
369 for f in module.callables() {
370 if let CallShape::Iterator(it) = &f.shape {
371 let t = &it.iter_tag;
372 let _ = writeln!(out, "typedef struct {t} {t};");
373 }
374 }
375}
376
377pub fn render_module_callback_types(out: &mut String, module: &ModuleBinding, prefix: &str) {
381 for cb in &module.callbacks {
382 emit_doc(out, &cb.doc, "");
383 let _ = writeln!(
384 out,
385 "typedef void (*{})({});",
386 cb.c_fn_type,
387 params_str(&cb.abi_params, prefix)
388 );
389 }
390 for f in module.callables() {
391 if let CallShape::Async(a) = &f.shape {
392 let _ = writeln!(
393 out,
394 "typedef void (*{})({});",
395 a.callback_type,
396 params_str(&a.callback_params, prefix)
397 );
398 }
399 }
400}
401
402fn render_callable_decl(out: &mut String, f: &FnBinding, prefix: &str) {
406 let api = export_macro(prefix);
407 let deprecated = deprecated_macro(prefix);
408 emit_doc(out, &f.doc, "");
409 if let Some(msg) = &f.deprecated {
410 let _ = writeln!(out, "{deprecated}(\"{}\")", msg.replace('"', "\\\""));
411 }
412 match &f.shape {
413 CallShape::Iterator(it) => {
414 let t = &it.iter_tag;
415 fn_decl(out, &it.launch, prefix);
416 fn_decl(out, &it.next, prefix);
417 let _ = writeln!(out, "{api} void {}({t}* iter);", it.destroy_symbol);
418 }
419 CallShape::Async(a) => {
420 fn_decl(out, &a.launch, prefix);
421 }
422 CallShape::Sync(abi) => {
423 fn_decl(out, abi, prefix);
424 }
425 }
426}
427
428fn render_interface_fn_decls(out: &mut String, i: &InterfaceBinding, prefix: &str) {
432 let api = export_macro(prefix);
433 let tag = &i.c_tag;
434 emit_doc(out, &i.doc, "");
435 for c in &i.constructors {
436 render_callable_decl(out, c, prefix);
437 }
438 for s in &i.statics {
439 render_callable_decl(out, s, prefix);
440 }
441 for m in &i.methods {
442 render_callable_decl(out, m, prefix);
443 }
444 let _ = writeln!(out, "{api} void {}({tag}* self);", i.destroy_symbol);
445 out.push('\n');
446}
447
448pub fn render_module_fn_decls(out: &mut String, module: &ModuleBinding, prefix: &str) {
454 let api = export_macro(prefix);
455 for e in &module.enums {
456 render_rich_enum_fn_decls(out, e, prefix);
457 }
458 for s in &module.structs {
459 render_struct_fn_decls(out, s, prefix);
460 }
461 for i in &module.interfaces {
462 render_interface_fn_decls(out, i, prefix);
463 }
464 for l in &module.listeners {
465 emit_doc(out, &l.doc, "");
466 let _ = writeln!(
467 out,
468 "{api} uint64_t {}({} callback, void* context);",
469 l.register_symbol, l.callback_c_fn_type
470 );
471 emit_doc(out, &l.doc, "");
472 let _ = writeln!(out, "{api} void {}(uint64_t id);", l.unregister_symbol);
473 }
474 for f in &module.functions {
475 render_callable_decl(out, f, prefix);
476 }
477}
478
479pub fn render_decls(
490 out: &mut String,
491 modules: &[ModuleBinding],
492 prefix: &str,
493 module_comments: bool,
494) {
495 for m in modules {
496 render_module_enum_defs(out, m);
497 }
498 for m in modules {
499 render_module_type_tags(out, m);
500 }
501 for m in modules {
502 render_module_callback_types(out, m, prefix);
503 }
504 out.push('\n');
505 for m in modules {
506 if module_comments {
507 let _ = writeln!(out, "// Module: {}", m.path);
508 }
509 render_module_fn_decls(out, m, prefix);
510 out.push('\n');
511 }
512}