1#![deny(
2 unsafe_code,
3 missing_docs,
4 missing_debug_implementations,
5 missing_copy_implementations,
6 elided_lifetimes_in_paths,
7 rust_2018_idioms,
8 clippy::fallible_impl_from,
9 clippy::missing_const_for_fn
10)]
11#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55122894")]
12use std::{
41 collections::HashMap,
42 fmt, fs,
43 io::{self, Write},
44 path::PathBuf,
45};
46
47use clang::{Clang, Entity, EntityKind, Index, Type, TypeKind};
48use log::debug;
49
50use config::DynamicLibraryConfig;
51use dart_source_writer::{DartSourceWriter, ImportedUri};
52use enumeration::{Enum, EnumField};
53use errors::CodegenError;
54use func::{Func, Param};
55use structure::{Field, Struct};
56
57pub mod config;
59mod dart_source_writer;
60mod enumeration;
61mod errors;
62mod func;
63mod structure;
64
65trait Element {
67 fn name(&self) -> &str;
69
70 fn documentation(&self) -> Option<&str>;
72
73 fn generate_source(&self, w: &mut DartSourceWriter) -> io::Result<()>;
75}
76
77pub struct Codegen {
79 src_header: PathBuf,
80 lib_name: String,
81 allo_isolate: bool,
82 config: DynamicLibraryConfig,
83 elements: HashMap<String, Box<dyn Element>>,
84}
85
86impl Codegen {
87 pub fn builder() -> CodegenBuilder { CodegenBuilder::default() }
89
90 pub fn generate(mut self) -> Result<Bindings, CodegenError> {
92 debug!("Starting Codegen!");
93 debug!("Building dsw");
94 let mut dsw = Self::build_dsw();
95 debug!("dsw is ready");
96 self.generate_open_dl(&mut dsw)?;
97 let clang = Clang::new()?;
98 let index = Index::new(&clang, true, false);
99 debug!("start parsing the C header file at {:?}.", self.src_header);
100 let parser = index.parser(self.src_header);
101 let tu = parser.parse()?;
102 debug!("Done Parsed the header file");
103 let entity = tu.get_entity();
104 let entities = entity
105 .get_children()
106 .into_iter()
107 .filter(|e| !e.is_in_system_header())
108 .peekable();
109
110 for e in entities {
111 let kind = e.get_kind();
112 debug!("Entity: {:?}", e);
113
114 match kind {
115 EntityKind::FunctionDecl => {
116 debug!("Got Function: {:?}", e);
117 let func = Self::parse_function(e)?;
119 self.elements
120 .insert(func.name().to_owned(), Box::new(func));
121 },
122 EntityKind::StructDecl => {
123 debug!("Got Struct: {:?}", e);
124
125 match Self::parse_struct(e, None) {
126 Err(CodegenError::UnnamedStruct) => Ok(()),
130 Err(err) => Err(err),
131 Ok(s) => {
132 self.elements
133 .insert(s.name().to_owned(), Box::new(s));
134
135 Ok(())
136 },
137 }?;
138 },
139 EntityKind::EnumDecl => {
140 debug!("Got Enum: {:?}", e);
141
142 match Self::parse_enum(e, None) {
143 Err(CodegenError::UnnamedEnum) => Ok(()),
147 Err(err) => Err(err),
148 Ok(s) => {
149 self.elements
150 .insert(s.name().to_owned(), Box::new(s));
151
152 Ok(())
153 },
154 }?;
155 },
156 EntityKind::TypedefDecl => {
157 debug!("Got Typedef: {:?}", e);
158
159 for child in e.get_children() {
160 match child.get_kind() {
161 EntityKind::StructDecl => {
162 debug!("Got struct in Typedef: {:?}", child);
163 let s =
164 Self::parse_struct(child, e.get_name())?;
165
166 self.elements
167 .insert(s.name().to_owned(), Box::new(s));
168 },
169 EntityKind::EnumDecl => {
170 debug!("Got enum in Typedef: {:?}", child);
171 let s = Self::parse_enum(child, e.get_name())?;
172
173 self.elements
174 .insert(s.name().to_owned(), Box::new(s));
175 },
176 _ => {},
177 }
178 }
179 },
180 _ => {},
181 }
182 }
183 if self.allo_isolate {
184 let func = Func::new(
185 "store_dart_post_cobject".to_string(),
186 Some(String::from("Binding to `allo-isolate` crate")),
187 vec![Param::new(
188 Some("ptr".to_string()),
189 String::from("Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>>"),
190 )],
191 String::from("void"),
192 );
193 self.elements.insert(
195 String::from("store_dart_post_cobject"),
196 Box::new(func),
197 );
198 }
199 debug!("Generating Dart Source...");
200 let mut elements: Vec<_> = self.elements.values().collect();
203 elements.sort_by_key(|k| k.name());
204
205 for el in elements {
206 el.generate_source(&mut dsw)?;
207 }
208 debug!("Done.");
209 Ok(Bindings::new(dsw))
210 }
211
212 fn generate_open_dl(
213 &self,
214 dsw: &mut DartSourceWriter,
215 ) -> Result<(), CodegenError> {
216 dsw.set_lib_name(&self.lib_name);
217 debug!("Generating Code for opening DynamicLibrary");
218 writeln!(dsw, "final DynamicLibrary _dl = _open();")?;
219 writeln!(dsw, "/// Reference to the Dynamic Library, it should be only used for low-level access")?;
220 writeln!(dsw, "final DynamicLibrary dl = _dl;")?;
221 writeln!(dsw, "DynamicLibrary _open() {{")?;
222 if let Some(ref config) = self.config.windows {
223 debug!("Generating _open Code for Windows");
224 writeln!(dsw, " if (Platform.isWindows) return {};", config)?;
225 }
226 if let Some(ref config) = self.config.linux {
227 debug!("Generating _open Code for Linux");
228 writeln!(dsw, " if (Platform.isLinux) return {};", config)?;
229 }
230 if let Some(ref config) = self.config.android {
231 debug!("Generating _open Code for Android");
232 writeln!(dsw, " if (Platform.isAndroid) return {};", config)?;
233 }
234 if let Some(ref config) = self.config.ios {
235 debug!("Generating _open Code for iOS");
236 writeln!(dsw, " if (Platform.isIOS) return {};", config)?;
237 }
238 if let Some(ref config) = self.config.macos {
239 debug!("Generating _open Code for macOS");
240 writeln!(dsw, " if (Platform.isMacOS) return {};", config)?;
241 }
242 if let Some(ref config) = self.config.fuchsia {
243 debug!("Generating _open Code for Fuchsia");
244 writeln!(dsw, " if (Platform.isFuchsia) return {};", config)?;
245 }
246 writeln!(
247 dsw,
248 " throw UnsupportedError('This platform is not supported.');"
249 )?;
250 writeln!(dsw, "}}")?;
251 debug!("Generating Code for opening DynamicLibrary done.");
252 Ok(())
253 }
254
255 fn parse_function(
256 entity: Entity<'_>,
257 ) -> Result<impl Element, CodegenError> {
258 let name = entity.get_name().ok_or(CodegenError::UnnamedFunction)?;
259 debug!("Function: {}", name);
260 let params = match entity.get_arguments() {
261 Some(entities) => Self::parse_fn_params(entities)?,
262 None => Vec::new(),
263 };
264 debug!("Function Params: {:?}", params);
265 let docs = entity.get_parsed_comment().map(|c| c.as_html());
266 debug!("Function Docs: {:?}", docs);
267 let return_ty = entity
268 .get_result_type()
269 .ok_or(CodegenError::UnknownFunctionReturnType)?
270 .get_canonical_type()
271 .get_display_name();
272 debug!("Function Return Type: {}", return_ty);
273 Ok(Func::new(name, docs, params, return_ty))
274 }
275
276 fn parse_fn_params(
277 entities: Vec<Entity<'_>>,
278 ) -> Result<Vec<Param>, CodegenError> {
279 let mut params = Vec::with_capacity(entities.capacity());
280 for e in entities {
281 debug!("Param: {:?}", e);
282 let name = e.get_name();
283 debug!("Param Name: {:?}", name);
284 let ty = e
285 .get_type()
286 .ok_or(CodegenError::UnknownParamType)?
287 .get_canonical_type();
288 debug!("Param Type: {:?}", ty);
289 let ty = Self::parse_ty(ty)?;
290 debug!("Param Type Display Name: {}", ty);
291 params.push(Param::new(name, ty));
292 }
293 Ok(params)
294 }
295
296 fn parse_fn_proto(ty: Type<'_>) -> Result<String, CodegenError> {
297 let mut dsw = DartSourceWriter::new();
298 debug!("Function Proto: {:?}", ty);
299 let return_ty = ty
300 .get_canonical_type()
301 .get_result_type()
302 .ok_or(CodegenError::UnknownFunctionReturnType)?
303 .get_canonical_type()
304 .get_display_name();
305 let params = match ty.get_argument_types() {
306 Some(arg_ty) => arg_ty
307 .iter()
308 .map(|ty| ty.get_display_name())
309 .map(|ty| dsw.get_ctype(&ty))
310 .collect(),
311 None => Vec::new(),
312 };
313 write!(
314 dsw,
315 "Pointer<NativeFunction<{} Function({})>>",
316 dsw.get_ctype(&return_ty),
317 params.join(", ")
318 )?;
319 Ok(dsw.to_string())
320 }
321
322 fn parse_struct(
323 entity: Entity<'_>,
324 name: Option<String>,
325 ) -> Result<impl Element, CodegenError> {
326 if entity.is_anonymous() {
327 return Err(CodegenError::AnonymousEntity);
328 }
329
330 let name = name
331 .or_else(|| entity.get_name())
332 .ok_or(CodegenError::UnnamedStruct)?;
333
334 debug!("Struct: {}", name);
335 let children = entity.get_children();
336 let mut fields = Vec::with_capacity(children.capacity());
337
338 for child in children {
339 let name =
340 child.get_name().ok_or(CodegenError::UnnamedStructField)?;
341
342 let ty = child
343 .get_type()
344 .ok_or(CodegenError::UnknownParamType)?
345 .get_canonical_type();
346 let ty = Self::parse_ty(ty)?;
347 fields.push(Field::new(name, ty));
348 }
349 let docs = entity.get_parsed_comment().map(|c| c.as_html());
350 Ok(Struct::new(name, docs, fields))
351 }
352
353 fn parse_enum(
354 entity: Entity<'_>,
355 name: Option<String>,
356 ) -> Result<impl Element, CodegenError> {
357 if entity.is_anonymous() {
358 return Err(CodegenError::AnonymousEntity);
359 }
360
361 let name = name
362 .or_else(|| entity.get_name())
363 .ok_or(CodegenError::UnnamedEnum)?;
364
365 debug!("Enum: {}", name);
366 let children = entity.get_children();
367 let mut fields = Vec::with_capacity(children.capacity());
368
369 for child in children {
370 debug!("Enum field: {:?}", child);
371 let name =
372 child.get_name().ok_or(CodegenError::UnnamedEnumField)?;
373
374 let value = child
375 .get_enum_constant_value()
376 .ok_or(CodegenError::UnknownEnumFieldConstantValue)?
377 .1;
378
379 fields.push(EnumField::new(name, value));
380 }
381 let docs = entity.get_parsed_comment().map(|c| c.as_html());
382 Ok(Enum::new(name, docs, fields))
383 }
384
385 fn parse_ty(ty: Type<'_>) -> Result<String, CodegenError> {
386 use TypeKind::*;
387 if ty.get_kind() == Pointer {
388 let pointee_type = ty
389 .get_pointee_type()
390 .ok_or(CodegenError::UnknownPointeeType)?;
391 let kind = pointee_type.get_kind();
392 if kind == FunctionPrototype || kind == FunctionNoPrototype {
393 Self::parse_fn_proto(pointee_type)
394 } else {
395 Ok(ty.get_display_name())
396 }
397 } else {
398 Ok(ty.get_display_name())
399 }
400 }
401
402 fn build_dsw() -> DartSourceWriter {
403 let mut dsw = DartSourceWriter::new();
404 writeln!(dsw, "// ignore_for_file: unused_import, camel_case_types, non_constant_identifier_names").unwrap();
406 dsw.import(ImportedUri::new(String::from("dart:ffi")));
407 dsw.import(ImportedUri::new(String::from("dart:io")));
408 let mut ffi = ImportedUri::new(String::from("package:ffi/ffi.dart"));
409 ffi.with_prefix(String::from("ffi"));
410 dsw.import(ffi);
411 dsw
412 }
413}
414
415impl fmt::Debug for Codegen {
416 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
417 f.debug_struct("Codegen")
418 .field("src_header", &self.src_header)
419 .field("lib_name", &self.lib_name)
420 .field("config", &self.config)
421 .finish()
422 }
423}
424
425#[derive(Clone, Debug, Default)]
429pub struct CodegenBuilder {
430 src_header: PathBuf,
431 lib_name: String,
432 allo_isolate: bool,
433 config: Option<DynamicLibraryConfig>,
434}
435
436impl CodegenBuilder {
437 pub fn with_src_header(mut self, path: impl Into<PathBuf>) -> Self {
439 self.src_header = path.into();
440 self
441 }
442
443 pub fn with_lib_name(mut self, name: impl Into<String>) -> Self {
447 self.lib_name = name.into();
448 self
449 }
450
451 #[allow(clippy::missing_const_for_fn)]
454 pub fn with_config(mut self, config: DynamicLibraryConfig) -> Self {
455 self.config = Some(config);
456 self
457 }
458
459 pub const fn with_allo_isolate(mut self) -> Self {
464 self.allo_isolate = true;
465 self
466 }
467
468 pub fn build(self) -> Result<Codegen, CodegenError> {
470 if self.lib_name.is_empty() {
471 return Err(CodegenError::Builder(
472 "Please Provide the C lib name.",
473 ));
474 }
475
476 let config = self.config.ok_or(
477 CodegenError::Builder("Missing `DynamicLibraryConfig` did you forget to call `with_config` builder method?.")
478 )?;
479
480 Ok(Codegen {
481 src_header: self.src_header,
482 lib_name: self.lib_name,
483 allo_isolate: self.allo_isolate,
484 config,
485 elements: HashMap::new(),
486 })
487 }
488}
489
490#[derive(Debug)]
492pub struct Bindings {
493 dsw: DartSourceWriter,
494}
495
496impl Bindings {
497 pub(crate) const fn new(dsw: DartSourceWriter) -> Self { Self { dsw } }
498
499 pub fn write_to_file(
501 &self,
502 path: impl Into<PathBuf>,
503 ) -> Result<(), CodegenError> {
504 let mut out = fs::OpenOptions::new()
505 .read(false)
506 .write(true)
507 .truncate(true)
508 .create(true)
509 .open(path.into())?;
510 debug!("Writing Dart Source File...");
511 write!(out, "{}", self.dsw)?;
512 Ok(())
513 }
514
515 pub fn write(&self, w: &mut impl Write) -> Result<(), CodegenError> {
520 debug!("Writing Dart Source File...");
521 write!(w, "{}", self.dsw)?;
522 Ok(())
523 }
524}