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::{AbiFn, CallShape, EnumBinding, ModuleBinding, StructBinding};
18
19pub fn emit_doc(out: &mut String, doc: &Option<String>, indent: &str) {
21 common_emit_doc(out, doc, indent, DocCommentStyle::Javadoc);
22}
23
24pub fn params_str(params: &[AbiParam], prefix: &str) -> String {
26 params
27 .iter()
28 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name))
29 .collect::<Vec<_>>()
30 .join(", ")
31}
32
33fn export_macro(prefix: &str) -> String {
40 format!("{}_API", prefix.to_uppercase())
41}
42
43fn deprecated_macro(prefix: &str) -> String {
48 format!("{}_DEPRECATED", prefix.to_uppercase())
49}
50
51pub fn render_visibility_macros(out: &mut String, prefix: &str) {
77 let body = r#"#ifndef @U@_API
78# if defined(_WIN32) || defined(__CYGWIN__)
79# ifdef @U@_BUILD
80# define @U@_API __declspec(dllexport)
81# else
82# define @U@_API __declspec(dllimport)
83# endif
84# elif defined(__EMSCRIPTEN__)
85# define @U@_API __attribute__((used, visibility("default")))
86# elif defined(__GNUC__) && (__GNUC__ >= 4)
87# define @U@_API __attribute__((visibility("default")))
88# else
89# define @U@_API
90# endif
91#endif
92
93#ifndef @U@_DEPRECATED
94# if defined(_MSC_VER)
95# define @U@_DEPRECATED(msg) __declspec(deprecated(msg))
96# elif defined(__GNUC__) || defined(__clang__)
97# define @U@_DEPRECATED(msg) __attribute__((deprecated(msg)))
98# else
99# define @U@_DEPRECATED(msg)
100# endif
101#endif
102
103"#;
104 out.push_str(&body.replace("@U@", &prefix.to_uppercase()));
105}
106
107pub fn fn_decl(out: &mut String, f: &AbiFn, prefix: &str) {
111 let _ = writeln!(
112 out,
113 "{} {} {}({});",
114 export_macro(prefix),
115 f.ret.render_c(prefix),
116 f.symbol,
117 params_str(&f.params, prefix)
118 );
119}
120
121pub fn render_runtime_decls(out: &mut String, prefix: &str) {
131 let api = export_macro(prefix);
132 let _ = write!(
133 out,
134 "typedef uint64_t {prefix}_handle_t;\n\n\
135 typedef struct {prefix}_error {{ int32_t code; const char* message; }} {prefix}_error;\n\n\
136 {api} void {prefix}_error_clear({prefix}_error* err);\n\
137 {api} void {prefix}_free_string(const char* ptr);\n\
138 {api} void {prefix}_free_bytes(uint8_t* ptr, size_t len);\n\n\
139 /* Linear-memory allocator used by the Wasm JS glue to stage call\n \
140 arguments. Native consumers never call these; producers targeting\n \
141 WebAssembly must export them (the generated {prefix}.c provides\n \
142 malloc/free-backed defaults). */\n\
143 {api} uint8_t* {prefix}_alloc(uint32_t size);\n\
144 {api} void {prefix}_dealloc(uint8_t* ptr, uint32_t size);\n\n\
145 typedef struct {prefix}_cancel_token {prefix}_cancel_token;\n\
146 {api} {prefix}_cancel_token* {prefix}_cancel_token_create(void);\n\
147 {api} void {prefix}_cancel_token_cancel({prefix}_cancel_token* token);\n\
148 {api} bool {prefix}_cancel_token_is_cancelled(const {prefix}_cancel_token* token);\n\
149 {api} void {prefix}_cancel_token_destroy({prefix}_cancel_token* token);\n\n",
150 );
151}
152
153fn render_enum_constants(out: &mut String, e: &EnumBinding, type_name: &str) {
156 let mut w = CodeWriter::four_space();
157 w.doc(&e.doc, DocCommentStyle::Javadoc);
158 if e.variants.iter().any(|v| v.doc.is_some()) {
159 w.block("typedef enum {", format!("}} {type_name};"), |w| {
160 let last = e.variants.len();
161 for (i, v) in e.variants.iter().enumerate() {
162 w.doc(&v.doc, DocCommentStyle::Javadoc);
163 let comma = if i + 1 == last { "" } else { "," };
164 w.line(format!("{} = {}{comma}", v.c_const, v.value));
165 }
166 });
167 } else {
168 let variants: Vec<String> = e
169 .variants
170 .iter()
171 .map(|v| format!("{} = {}", v.c_const, v.value))
172 .collect();
173 w.line(format!(
174 "typedef enum {{ {} }} {type_name};",
175 variants.join(", ")
176 ));
177 }
178 out.push_str(&w.finish());
179}
180
181pub fn render_enum_decl(out: &mut String, e: &EnumBinding) {
183 render_enum_constants(out, e, &e.c_tag);
184}
185
186fn render_rich_enum_tag_decl(out: &mut String, e: &EnumBinding) {
191 let tag_enum = format!("{}_Tag", e.c_tag);
192 render_enum_constants(out, e, &tag_enum);
193}
194
195fn render_rich_enum_fn_decls(out: &mut String, e: &EnumBinding, prefix: &str) {
199 let Some(rich) = &e.rich else {
200 return;
201 };
202 let api = export_macro(prefix);
203 let tag = &e.c_tag;
204 emit_doc(out, &e.doc, "");
205 let _ = writeln!(out, "{api} int32_t {}(const {tag}* self);", rich.tag_symbol);
206 for v in &rich.variants {
207 emit_doc(out, &v.doc, "");
208 fn_decl(out, &v.create, prefix);
209 for field in &v.fields {
210 emit_doc(out, &field.doc, "");
211 let mut parts = vec![format!("const {tag}* self")];
212 parts.extend(
213 field
214 .getter_out_params
215 .iter()
216 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name)),
217 );
218 let _ = writeln!(
219 out,
220 "{api} {} {}({});",
221 field.getter_ret.render_c(prefix),
222 field.getter_symbol,
223 parts.join(", ")
224 );
225 }
226 }
227 let _ = writeln!(out, "{api} void {}({tag}* self);", rich.destroy_symbol);
228 out.push('\n');
229}
230
231fn render_struct_tags(out: &mut String, s: &StructBinding) {
237 let tag = &s.c_tag;
238 let _ = writeln!(out, "typedef struct {tag} {tag};");
239 if let Some(b) = &s.builder {
240 let bt = &b.builder_tag;
241 let _ = writeln!(out, "typedef struct {bt} {bt};");
242 }
243}
244
245fn render_struct_fn_decls(out: &mut String, s: &StructBinding, prefix: &str) {
250 let api = export_macro(prefix);
251 let tag = &s.c_tag;
252 emit_doc(out, &s.doc, "");
253 fn_decl(out, &s.create, prefix);
254 let _ = writeln!(out, "{api} void {}({tag}* ptr);", s.destroy_symbol);
255 for field in &s.fields {
256 emit_doc(out, &field.doc, "");
257 let mut parts = vec![format!("const {tag}* ptr")];
258 parts.extend(
259 field
260 .getter_out_params
261 .iter()
262 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name)),
263 );
264 let _ = writeln!(
265 out,
266 "{api} {} {}({});",
267 field.getter_ret.render_c(prefix),
268 field.getter_symbol,
269 parts.join(", ")
270 );
271 }
272 out.push('\n');
273
274 if let Some(b) = &s.builder {
275 let bt = &b.builder_tag;
276 let _ = writeln!(out, "{api} {bt}* {}(void);", b.new_symbol);
277 for (field, (_, setter)) in s.fields.iter().zip(&b.setters) {
278 emit_doc(out, &field.doc, "");
279 let _ = writeln!(
280 out,
281 "{api} void {setter}({bt}* builder, {});",
282 params_str(&field.value_params, prefix)
283 );
284 }
285 let _ = writeln!(
286 out,
287 "{api} {tag}* {}({bt}* builder, {prefix}_error* out_err);",
288 b.build_symbol
289 );
290 let _ = writeln!(out, "{api} void {}({bt}* builder);", b.destroy_symbol);
291 out.push('\n');
292 }
293}
294
295pub fn render_module_enum_defs(out: &mut String, module: &ModuleBinding) {
298 for e in &module.enums {
299 if e.is_rich() {
300 render_rich_enum_tag_decl(out, e);
301 } else {
302 render_enum_decl(out, e);
303 }
304 }
305}
306
307pub fn render_module_type_tags(out: &mut String, module: &ModuleBinding) {
311 for e in &module.enums {
313 if e.is_rich() {
314 let t = &e.c_tag;
315 let _ = writeln!(out, "typedef struct {t} {t};");
316 }
317 }
318 for s in &module.structs {
319 render_struct_tags(out, s);
320 }
321 for f in &module.functions {
322 if let CallShape::Iterator(it) = &f.shape {
323 let t = &it.iter_tag;
324 let _ = writeln!(out, "typedef struct {t} {t};");
325 }
326 }
327}
328
329pub fn render_module_callback_types(out: &mut String, module: &ModuleBinding, prefix: &str) {
333 for cb in &module.callbacks {
334 emit_doc(out, &cb.doc, "");
335 let _ = writeln!(
336 out,
337 "typedef void (*{})({});",
338 cb.c_fn_type,
339 params_str(&cb.abi_params, prefix)
340 );
341 }
342 for f in &module.functions {
343 if let CallShape::Async(a) = &f.shape {
344 let _ = writeln!(
345 out,
346 "typedef void (*{})({});",
347 a.callback_type,
348 params_str(&a.callback_params, prefix)
349 );
350 }
351 }
352}
353
354pub fn render_module_fn_decls(out: &mut String, module: &ModuleBinding, prefix: &str) {
359 let api = export_macro(prefix);
360 let deprecated = deprecated_macro(prefix);
361 for e in &module.enums {
362 render_rich_enum_fn_decls(out, e, prefix);
363 }
364 for s in &module.structs {
365 render_struct_fn_decls(out, s, prefix);
366 }
367 for l in &module.listeners {
368 emit_doc(out, &l.doc, "");
369 let _ = writeln!(
370 out,
371 "{api} uint64_t {}({} callback, void* context);",
372 l.register_symbol, l.callback_c_fn_type
373 );
374 emit_doc(out, &l.doc, "");
375 let _ = writeln!(out, "{api} void {}(uint64_t id);", l.unregister_symbol);
376 }
377 for f in &module.functions {
378 emit_doc(out, &f.doc, "");
379 if let Some(msg) = &f.deprecated {
380 let _ = writeln!(out, "{deprecated}(\"{}\")", msg.replace('"', "\\\""));
381 }
382 match &f.shape {
383 CallShape::Iterator(it) => {
384 let t = &it.iter_tag;
385 fn_decl(out, &it.launch, prefix);
386 fn_decl(out, &it.next, prefix);
387 let _ = writeln!(out, "{api} void {}({t}* iter);", it.destroy_symbol);
388 }
389 CallShape::Async(a) => {
390 fn_decl(out, &a.launch, prefix);
391 }
392 CallShape::Sync(abi) => {
393 fn_decl(out, abi, prefix);
394 }
395 }
396 }
397}
398
399pub fn render_decls(
410 out: &mut String,
411 modules: &[ModuleBinding],
412 prefix: &str,
413 module_comments: bool,
414) {
415 for m in modules {
416 render_module_enum_defs(out, m);
417 }
418 for m in modules {
419 render_module_type_tags(out, m);
420 }
421 for m in modules {
422 render_module_callback_types(out, m, prefix);
423 }
424 out.push('\n');
425 for m in modules {
426 if module_comments {
427 let _ = writeln!(out, "// Module: {}", m.path);
428 }
429 render_module_fn_decls(out, m, prefix);
430 out.push('\n');
431 }
432}