Skip to main content

brk_bindgen/backends/
javascript.rs

1//! JavaScript language syntax implementation.
2
3use crate::{GenericSyntax, LanguageSyntax, to_camel_case};
4
5/// JavaScript-specific code generation syntax.
6pub struct JavaScriptSyntax;
7
8impl LanguageSyntax for JavaScriptSyntax {
9    fn field_name(&self, name: &str) -> String {
10        to_camel_case(name)
11    }
12
13    fn path_expr(&self, base_var: &str, suffix: &str) -> String {
14        // Convert base_var to camelCase for JavaScript
15        let var_name = to_camel_case(base_var);
16        format!("`${{{}}}{}`", var_name, suffix)
17    }
18
19    fn suffix_expr(&self, acc_var: &str, relative: &str) -> String {
20        let var_name = to_camel_case(acc_var);
21        if relative.is_empty() {
22            // Identity: just return acc
23            var_name
24        } else {
25            // _m(acc, relative) -> acc ? `${acc}_relative` : 'relative'
26            format!("_m({}, '{}')", var_name, relative)
27        }
28    }
29
30    fn prefix_expr(&self, prefix: &str, acc_var: &str) -> String {
31        let var_name = to_camel_case(acc_var);
32        if prefix.is_empty() {
33            // Identity: just return acc
34            var_name
35        } else {
36            // _p(prefix, acc) -> acc ? `${prefix}${acc}` : 'prefix_without_underscore'
37            let prefix_base = prefix.trim_end_matches('_');
38            format!("_p('{}', {})", prefix_base, var_name)
39        }
40    }
41
42    fn constructor(&self, type_name: &str, path_expr: &str) -> String {
43        format!("create{}(client, {})", type_name, path_expr)
44    }
45
46    fn field_init(&self, indent: &str, name: &str, _type_ann: &str, value: &str) -> String {
47        // JavaScript uses object literal syntax; type is in JSDoc, not in assignment
48        format!("{}{}: {},", indent, name, value)
49    }
50
51    fn generic_syntax(&self) -> GenericSyntax {
52        GenericSyntax::JAVASCRIPT
53    }
54
55    fn string_literal(&self, value: &str) -> String {
56        format!("'{}'", value)
57    }
58
59    fn constructor_name(&self, type_name: &str) -> String {
60        format!("create{}", type_name)
61    }
62
63    fn disc_arg_expr(&self, template: &str) -> String {
64        if template == "{disc}" {
65            "disc".to_string()
66        } else if template.is_empty() {
67            "''".to_string()
68        } else if !template.contains("{disc}") {
69            format!("'{}'", template)
70        } else if template.ends_with("{disc}") {
71            let static_part = template.trim_end_matches("{disc}").trim_end_matches('_');
72            format!("_m('{}', disc)", static_part)
73        } else {
74            let js_template = template.replace("{disc}", "${disc}");
75            format!("`{}`", js_template)
76        }
77    }
78
79    fn template_expr(&self, acc_var: &str, template: &str) -> String {
80        let var_name = to_camel_case(acc_var);
81        if template.is_empty() {
82            // Identity — just pass disc
83            format!("_m({}, disc)", var_name)
84        } else if template == "{disc}" {
85            // Template IS the discriminator
86            format!("_m({}, disc)", var_name)
87        } else if !template.contains("{disc}") {
88            // Static suffix — no disc involved
89            format!("_m({}, '{}')", var_name, template)
90        } else {
91            // Template with {disc}: use nested _m for proper separator handling
92            // "ratio_{disc}_bps" → split on {disc} → _m(_m(acc, 'ratio'), disc) then _bps
93            // But this is complex. For embedded disc, use string interpolation.
94            // For suffix disc (ends with {disc}), use _m composition.
95            if let Some(static_part) = template.strip_suffix("{disc}") {
96                if static_part.is_empty() {
97                    format!("_m({}, disc)", var_name)
98                } else {
99                    let static_part = static_part.trim_end_matches('_');
100                    format!("_m(_m({}, '{}'), disc)", var_name, static_part)
101                }
102            } else {
103                // Embedded disc — use template literal
104                let js_template = template.replace("{disc}", "${disc}");
105                format!("_m({}, `{}`)", var_name, js_template)
106            }
107        }
108    }
109}