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) {
73 let body = r#"#ifndef @U@_API
74# if defined(_WIN32) || defined(__CYGWIN__)
75# ifdef @U@_BUILD
76# define @U@_API __declspec(dllexport)
77# else
78# define @U@_API __declspec(dllimport)
79# endif
80# elif defined(__GNUC__) && (__GNUC__ >= 4)
81# define @U@_API __attribute__((visibility("default")))
82# else
83# define @U@_API
84# endif
85#endif
86
87#ifndef @U@_DEPRECATED
88# if defined(_MSC_VER)
89# define @U@_DEPRECATED(msg) __declspec(deprecated(msg))
90# elif defined(__GNUC__) || defined(__clang__)
91# define @U@_DEPRECATED(msg) __attribute__((deprecated(msg)))
92# else
93# define @U@_DEPRECATED(msg)
94# endif
95#endif
96
97"#;
98 out.push_str(&body.replace("@U@", &prefix.to_uppercase()));
99}
100
101pub fn fn_decl(out: &mut String, f: &AbiFn, prefix: &str) {
105 let _ = writeln!(
106 out,
107 "{} {} {}({});",
108 export_macro(prefix),
109 f.ret.render_c(prefix),
110 f.symbol,
111 params_str(&f.params, prefix)
112 );
113}
114
115pub fn render_runtime_decls(out: &mut String, prefix: &str) {
118 let api = export_macro(prefix);
119 let _ = write!(
120 out,
121 "typedef uint64_t {prefix}_handle_t;\n\n\
122 typedef struct {prefix}_error {{ int32_t code; const char* message; }} {prefix}_error;\n\n\
123 {api} void {prefix}_error_clear({prefix}_error* err);\n\
124 {api} void {prefix}_free_string(const char* ptr);\n\
125 {api} void {prefix}_free_bytes(uint8_t* ptr, size_t len);\n\n\
126 typedef struct {prefix}_cancel_token {prefix}_cancel_token;\n\
127 {api} {prefix}_cancel_token* {prefix}_cancel_token_create(void);\n\
128 {api} void {prefix}_cancel_token_cancel({prefix}_cancel_token* token);\n\
129 {api} bool {prefix}_cancel_token_is_cancelled(const {prefix}_cancel_token* token);\n\
130 {api} void {prefix}_cancel_token_destroy({prefix}_cancel_token* token);\n\n",
131 );
132}
133
134fn render_enum_constants(out: &mut String, e: &EnumBinding, type_name: &str) {
137 let mut w = CodeWriter::four_space();
138 w.doc(&e.doc, DocCommentStyle::Javadoc);
139 if e.variants.iter().any(|v| v.doc.is_some()) {
140 w.block("typedef enum {", format!("}} {type_name};"), |w| {
141 let last = e.variants.len();
142 for (i, v) in e.variants.iter().enumerate() {
143 w.doc(&v.doc, DocCommentStyle::Javadoc);
144 let comma = if i + 1 == last { "" } else { "," };
145 w.line(format!("{} = {}{comma}", v.c_const, v.value));
146 }
147 });
148 } else {
149 let variants: Vec<String> = e
150 .variants
151 .iter()
152 .map(|v| format!("{} = {}", v.c_const, v.value))
153 .collect();
154 w.line(format!(
155 "typedef enum {{ {} }} {type_name};",
156 variants.join(", ")
157 ));
158 }
159 out.push_str(&w.finish());
160}
161
162pub fn render_enum_decl(out: &mut String, e: &EnumBinding) {
164 render_enum_constants(out, e, &e.c_tag);
165}
166
167fn render_rich_enum_tag_decl(out: &mut String, e: &EnumBinding) {
172 let tag_enum = format!("{}_Tag", e.c_tag);
173 render_enum_constants(out, e, &tag_enum);
174}
175
176fn render_rich_enum_fn_decls(out: &mut String, e: &EnumBinding, prefix: &str) {
180 let Some(rich) = &e.rich else {
181 return;
182 };
183 let api = export_macro(prefix);
184 let tag = &e.c_tag;
185 emit_doc(out, &e.doc, "");
186 let _ = writeln!(out, "{api} int32_t {}(const {tag}* self);", rich.tag_symbol);
187 for v in &rich.variants {
188 emit_doc(out, &v.doc, "");
189 fn_decl(out, &v.create, prefix);
190 for field in &v.fields {
191 emit_doc(out, &field.doc, "");
192 let mut parts = vec![format!("const {tag}* self")];
193 parts.extend(
194 field
195 .getter_out_params
196 .iter()
197 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name)),
198 );
199 let _ = writeln!(
200 out,
201 "{api} {} {}({});",
202 field.getter_ret.render_c(prefix),
203 field.getter_symbol,
204 parts.join(", ")
205 );
206 }
207 }
208 let _ = writeln!(out, "{api} void {}({tag}* self);", rich.destroy_symbol);
209 out.push('\n');
210}
211
212fn render_struct_tags(out: &mut String, s: &StructBinding) {
218 let tag = &s.c_tag;
219 let _ = writeln!(out, "typedef struct {tag} {tag};");
220 if let Some(b) = &s.builder {
221 let bt = &b.builder_tag;
222 let _ = writeln!(out, "typedef struct {bt} {bt};");
223 }
224}
225
226fn render_struct_fn_decls(out: &mut String, s: &StructBinding, prefix: &str) {
231 let api = export_macro(prefix);
232 let tag = &s.c_tag;
233 emit_doc(out, &s.doc, "");
234 fn_decl(out, &s.create, prefix);
235 let _ = writeln!(out, "{api} void {}({tag}* ptr);", s.destroy_symbol);
236 for field in &s.fields {
237 emit_doc(out, &field.doc, "");
238 let mut parts = vec![format!("const {tag}* ptr")];
239 parts.extend(
240 field
241 .getter_out_params
242 .iter()
243 .map(|p| format!("{} {}", p.ty.render_c(prefix), p.name)),
244 );
245 let _ = writeln!(
246 out,
247 "{api} {} {}({});",
248 field.getter_ret.render_c(prefix),
249 field.getter_symbol,
250 parts.join(", ")
251 );
252 }
253 out.push('\n');
254
255 if let Some(b) = &s.builder {
256 let bt = &b.builder_tag;
257 let _ = writeln!(out, "{api} {bt}* {}(void);", b.new_symbol);
258 for (field, (_, setter)) in s.fields.iter().zip(&b.setters) {
259 emit_doc(out, &field.doc, "");
260 let _ = writeln!(
261 out,
262 "{api} void {setter}({bt}* builder, {});",
263 params_str(&field.value_params, prefix)
264 );
265 }
266 let _ = writeln!(
267 out,
268 "{api} {tag}* {}({bt}* builder, {prefix}_error* out_err);",
269 b.build_symbol
270 );
271 let _ = writeln!(out, "{api} void {}({bt}* builder);", b.destroy_symbol);
272 out.push('\n');
273 }
274}
275
276pub fn render_module_enum_defs(out: &mut String, module: &ModuleBinding) {
279 for e in &module.enums {
280 if e.is_rich() {
281 render_rich_enum_tag_decl(out, e);
282 } else {
283 render_enum_decl(out, e);
284 }
285 }
286}
287
288pub fn render_module_type_tags(out: &mut String, module: &ModuleBinding) {
292 for e in &module.enums {
294 if e.is_rich() {
295 let t = &e.c_tag;
296 let _ = writeln!(out, "typedef struct {t} {t};");
297 }
298 }
299 for s in &module.structs {
300 render_struct_tags(out, s);
301 }
302 for f in &module.functions {
303 if let CallShape::Iterator(it) = &f.shape {
304 let t = &it.iter_tag;
305 let _ = writeln!(out, "typedef struct {t} {t};");
306 }
307 }
308}
309
310pub fn render_module_callback_types(out: &mut String, module: &ModuleBinding, prefix: &str) {
314 for cb in &module.callbacks {
315 emit_doc(out, &cb.doc, "");
316 let _ = writeln!(
317 out,
318 "typedef void (*{})({});",
319 cb.c_fn_type,
320 params_str(&cb.abi_params, prefix)
321 );
322 }
323 for f in &module.functions {
324 if let CallShape::Async(a) = &f.shape {
325 let _ = writeln!(
326 out,
327 "typedef void (*{})({});",
328 a.callback_type,
329 params_str(&a.callback_params, prefix)
330 );
331 }
332 }
333}
334
335pub fn render_module_fn_decls(out: &mut String, module: &ModuleBinding, prefix: &str) {
340 let api = export_macro(prefix);
341 let deprecated = deprecated_macro(prefix);
342 for e in &module.enums {
343 render_rich_enum_fn_decls(out, e, prefix);
344 }
345 for s in &module.structs {
346 render_struct_fn_decls(out, s, prefix);
347 }
348 for l in &module.listeners {
349 emit_doc(out, &l.doc, "");
350 let _ = writeln!(
351 out,
352 "{api} uint64_t {}({} callback, void* context);",
353 l.register_symbol, l.callback_c_fn_type
354 );
355 emit_doc(out, &l.doc, "");
356 let _ = writeln!(out, "{api} void {}(uint64_t id);", l.unregister_symbol);
357 }
358 for f in &module.functions {
359 emit_doc(out, &f.doc, "");
360 if let Some(msg) = &f.deprecated {
361 let _ = writeln!(out, "{deprecated}(\"{}\")", msg.replace('"', "\\\""));
362 }
363 match &f.shape {
364 CallShape::Iterator(it) => {
365 let t = &it.iter_tag;
366 fn_decl(out, &it.launch, prefix);
367 fn_decl(out, &it.next, prefix);
368 let _ = writeln!(out, "{api} void {}({t}* iter);", it.destroy_symbol);
369 }
370 CallShape::Async(a) => {
371 fn_decl(out, &a.launch, prefix);
372 }
373 CallShape::Sync(abi) => {
374 fn_decl(out, abi, prefix);
375 }
376 }
377 }
378}
379
380pub fn render_decls(
391 out: &mut String,
392 modules: &[ModuleBinding],
393 prefix: &str,
394 module_comments: bool,
395) {
396 for m in modules {
397 render_module_enum_defs(out, m);
398 }
399 for m in modules {
400 render_module_type_tags(out, m);
401 }
402 for m in modules {
403 render_module_callback_types(out, m, prefix);
404 }
405 out.push('\n');
406 for m in modules {
407 if module_comments {
408 let _ = writeln!(out, "// Module: {}", m.path);
409 }
410 render_module_fn_decls(out, m, prefix);
411 out.push('\n');
412 }
413}