playdate_bindgen/gen/
mod.rs

1#![cfg(feature = "extra-codegen")]
2use std::borrow::Cow;
3use std::collections::HashMap;
4use std::io::Write;
5use std::path::Path;
6use std::sync::Arc;
7use utils::toolchain::sdk::Sdk;
8use quote::ToTokens;
9use proc_macro2::TokenStream;
10
11use crate::Result;
12use crate::error::Error;
13use crate::rustify::rename::{self, Kind, SharedRenamed};
14
15pub mod docs;
16pub mod fixes;
17
18
19#[allow(unused_variables)]
20pub fn engage(source: &bindgen::Bindings,
21              renamed: SharedRenamed,
22              features: &crate::cfg::Features,
23              target: &crate::cfg::Target,
24              sdk: &Sdk,
25              root: Option<&str>)
26              -> Result<Bindings> {
27	if features.rustify {
28		rename::reduce(Arc::clone(&renamed));
29	}
30
31	let root_struct_name = {
32		let orig = root.as_ref().map(AsRef::as_ref).unwrap_or("PlaydateAPI");
33
34		// find the renamed root:
35		let key = Kind::Struct(orig.to_owned());
36		renamed.read()
37		       .map_err(|err| {
38			       let s = Box::leak(Box::new(format!("renamed set is locked: {err}"))).as_str();
39			       Error::Internal(s)
40		       })?
41		       .get(&key)
42		       .map(ToOwned::to_owned)
43		       .map(Cow::from)
44		       .unwrap_or_else(|| Cow::from(orig))
45	};
46
47
48	// rename::print_as_md_table(Arc::clone(&renamed));
49
50
51	#[allow(unused_mut)]
52	let mut bindings = syn::parse_file(&source.to_string())?;
53
54	#[allow(unused_assignments)]
55	#[cfg(feature = "documentation")]
56	let docset = if features.documentation {
57		let docset = docs::parser::parse(sdk)?;
58		docs::gen::engage(&mut bindings, &root_struct_name, &docset)?;
59		Some(docset)
60	} else {
61		None
62	};
63
64
65	#[cfg(feature = "extra-codegen")]
66	if features.rustify {
67		// let fixes = if target.is_playdate() {
68		let mut fixes = HashMap::new();
69		fixes.insert("system.error".to_owned(), fixes::Fix::ReturnNever);
70		// fixes
71		// } else {
72		// 	Default::default()
73		// };
74		fixes::engage(&mut bindings, &root_struct_name, target, &fixes)?;
75	}
76
77	let mut module = TokenStream::new();
78	bindings.to_tokens(&mut module);
79
80	Ok(Bindings { module,
81	              #[cfg(feature = "documentation")]
82	              docset })
83}
84
85
86/// Engaged bindings with doc-comments.
87#[derive(Debug)]
88pub struct Bindings {
89	module: TokenStream,
90
91	// for cache:
92	#[cfg(feature = "documentation")]
93	docset: Option<docs::DocsMap>,
94	// TODO: minimal cfg from parent such as formatter & rustfmt-cfg.
95}
96
97impl crate::Bindings {
98	#[cfg(feature = "documentation")]
99	pub fn docset(&self) -> Option<&docs::DocsMap> {
100		match self {
101			crate::Bindings::Bindgen(_) => None,
102			crate::Bindings::Engaged(this) => this.docset.as_ref(),
103		}
104	}
105}
106
107impl Bindings {
108	/// Write these bindings as source text to a file.
109	pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
110		let file = std::fs::OpenOptions::new().write(true)
111		                                      .truncate(true)
112		                                      .create(true)
113		                                      .open(path.as_ref())?;
114		self.write(Box::new(file))?;
115		Ok(())
116	}
117
118	/// Write these bindings as source text to the given `Write`able.
119	pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> std::io::Result<()> {
120		// formatting:
121		let source = self.module.to_string();
122		let output = match crate::rustfmt(None, source.clone(), None) {
123			Ok(output) => output,
124			Err(err) => {
125				println!("cargo::warning=Rustfmt error: {err}");
126
127				let output: String;
128				#[cfg(feature = "pretty-please")]
129				{
130					let tokens = &self.module;
131					output = prettyplease::unparse(&syn::parse_quote!(#tokens));
132				}
133				#[cfg(not(feature = "prettyplease"))]
134				{
135					output = source;
136				}
137				output
138			},
139		};
140
141		writer.write_all(output.as_bytes())?;
142		Ok(())
143	}
144}
145
146
147impl std::fmt::Display for Bindings {
148	#[inline(always)]
149	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150		let mut bytes = vec![];
151		self.write(Box::new(&mut bytes) as Box<dyn Write>)
152		    .expect("writing to a vec cannot fail");
153		f.write_str(std::str::from_utf8(&bytes).expect("we should only write bindings that are valid utf-8"))
154	}
155}