oo_bindgen/backend/dotnet/
wrappers.rs

1use crate::backend::dotnet::*;
2
3fn filter_has_error(
4    x: &Handle<Function<Validated>>,
5) -> Option<(Handle<Function<Validated>>, ErrorType<Validated>)> {
6    x.error_type.get().map(|err| (x.clone(), err.clone()))
7}
8
9pub(crate) fn generate_native_functions_class(
10    f: &mut dyn Printer,
11    lib: &Library,
12    config: &DotnetBindgenConfig,
13) -> FormattingResult<()> {
14    print_license(f, &lib.info.license_description)?;
15    print_imports(f)?;
16    f.newline()?;
17
18    doxygen(f, |f| {
19        // Doxygen main page
20        f.writeln("@mainpage")?;
21        f.newline()?;
22        f.writeln(&lib.info.description)?;
23        f.newline()?;
24        f.writeln(&format!(
25            "For complete documentation, see @ref {} namespace",
26            lib.settings.name
27        ))?;
28        f.newline()?;
29        f.writeln("@section license License")?;
30        f.newline()?;
31        for line in &lib.info.license_description {
32            f.writeln(line)?;
33        }
34
35        Ok(())
36    })?;
37    f.newline()?;
38
39    namespaced(f, &lib.settings.name, |f| {
40        f.writeln(&format!("internal class {NATIVE_FUNCTIONS_CLASSNAME}"))?;
41        blocked(f, |f| {
42            f.writeln(&format!("const string VERSION = \"{}\";", lib.version))?;
43
44            // Static constructor used to verify the version
45            f.writeln(&format!("static {NATIVE_FUNCTIONS_CLASSNAME}()"))?;
46            blocked(f, |f| {
47                f.writeln("var loadedVersion = Helpers.RustString.FromNative(Version());")?;
48                f.writeln("if (loadedVersion != VERSION)")?;
49                blocked(f, |f| {
50                    f.writeln(&format!("throw new Exception(\"{} module version mismatch. Expected \" + VERSION + \" but loaded \" + loadedVersion);", lib.settings.name))
51                })
52            })?;
53
54            f.newline()?;
55
56            for func in lib.functions() {
57                f.newline()?;
58                write_conversion_wrapper(f, func)?;
59            }
60
61            Ok(())
62        })?;
63
64        f.newline()?;
65        f.writeln("internal class ExceptionWrappers")?;
66        blocked(f, |f| {
67            for (func, err) in lib.functions().filter_map(filter_has_error) {
68                f.newline()?;
69                write_exception_wrapper(f, &func, &err)?;
70            }
71            Ok(())
72        })?;
73
74        f.newline()?;
75
76        f.writeln("internal class PInvoke")?;
77        blocked(f, |f| {
78            for func in lib.functions() {
79                write_pinvoke_signature(f, func, &lib.settings.c_ffi_prefix, config)?;
80            }
81            Ok(())
82        })
83    })
84}
85
86fn write_exception_and_return_blocks(
87    f: &mut dyn Printer,
88    err: &ErrorType<Validated>,
89    func: &Handle<Function<Validated>>,
90    params: &str,
91) -> FormattingResult<()> {
92    match func.return_type.get() {
93        Some(ret) => {
94            f.writeln(&format!(
95                "var _error_result = PInvoke.{}({}, out {} _return_value);",
96                func.name.camel_case(),
97                params,
98                ret.value.get_native_type()
99            ))?;
100            f.writeln(&format!(
101                "if(_error_result != {}.Ok)",
102                err.inner.name.camel_case()
103            ))?;
104            blocked(f, |f| {
105                f.writeln(&format!(
106                    "throw new {}(_error_result);",
107                    err.exception_name.camel_case()
108                ))
109            })?;
110            f.writeln("return _return_value;")
111        }
112        None => {
113            f.writeln(&format!(
114                "var error = PInvoke.{}({});",
115                func.name.camel_case(),
116                params
117            ))?;
118            f.writeln(&format!("if(error != {}.Ok)", err.inner.name.camel_case()))?;
119            blocked(f, |f| {
120                f.writeln(&format!(
121                    "throw new {}(error);",
122                    err.exception_name.camel_case()
123                ))
124            })
125        }
126    }
127}
128
129fn write_conversion_wrapper(
130    f: &mut dyn Printer,
131    func: &Handle<Function<Validated>>,
132) -> FormattingResult<()> {
133    f.write(&format!(
134        "internal static {} {}(",
135        func.return_type.get_native_type(),
136        func.name.camel_case()
137    ))?;
138
139    f.write(
140        &func
141            .arguments
142            .iter()
143            .map(|param| format!("{} {}", param.arg_type.get_native_type(), param.name))
144            .collect::<Vec<String>>()
145            .join(", "),
146    )?;
147
148    f.write(")")?;
149
150    let params = func
151        .arguments
152        .iter()
153        .map(|p| p.name.to_string())
154        .collect::<Vec<String>>()
155        .join(", ");
156
157    let target = if func.error_type.is_some() {
158        "ExceptionWrappers"
159    } else {
160        "PInvoke"
161    };
162
163    blocked(f, |f| {
164        f.newline()?;
165        if func.return_type.is_some() {
166            f.write("return ")?;
167        }
168        f.write(&format!(
169            "{}.{}({});",
170            target,
171            func.name.camel_case(),
172            params
173        ))
174    })
175}
176
177fn write_exception_wrapper(
178    f: &mut dyn Printer,
179    func: &Handle<Function<Validated>>,
180    err: &ErrorType<Validated>,
181) -> FormattingResult<()> {
182    f.write(&format!(
183        "internal static {} {}(",
184        func.return_type.get_native_type(),
185        func.name.camel_case(),
186    ))?;
187
188    f.write(
189        &func
190            .arguments
191            .iter()
192            .map(|param| format!("{} {}", param.arg_type.get_native_type(), param.name))
193            .collect::<Vec<String>>()
194            .join(", "),
195    )?;
196
197    f.write(")")?;
198
199    let params = func
200        .arguments
201        .iter()
202        .map(|p| p.name.to_string())
203        .collect::<Vec<String>>()
204        .join(", ");
205
206    blocked(f, |f| {
207        write_exception_and_return_blocks(f, err, func, &params)
208    })
209}
210
211fn write_pinvoke_signature(
212    f: &mut dyn Printer,
213    handle: &Handle<Function<Validated>>,
214    prefix: &str,
215    config: &DotnetBindgenConfig,
216) -> FormattingResult<()> {
217    f.writeln(&format!(
218        "[DllImport(\"{}\", CallingConvention = CallingConvention.Cdecl, EntryPoint = \"{}_{}\")]",
219        config.ffi_name, prefix, handle.name
220    ))?;
221    f.newline()?;
222
223    if let Some(err) = handle.error_type.get() {
224        f.write(&format!(
225            "internal static extern {} {}(",
226            err.inner.get_native_type(),
227            handle.name.camel_case(),
228        ))?;
229    } else {
230        f.write(&format!(
231            "internal static extern {} {}(",
232            handle.return_type.get_native_type(),
233            handle.name.camel_case()
234        ))?;
235    }
236
237    f.write(
238        &handle
239            .arguments
240            .iter()
241            .map(|param| format!("{} {}", param.arg_type.get_native_type(), param.name))
242            .collect::<Vec<String>>()
243            .join(", "),
244    )?;
245
246    if let SignatureType::ErrorWithReturn(_, ret, _) = handle.get_signature_type() {
247        if !handle.arguments.is_empty() {
248            f.write(", ")?;
249        }
250        f.write(&format!("out {} @out", ret.get_native_type()))?
251    }
252
253    f.write(");")
254}