playdate_bindgen/rustify/
rename.rs

1use std::borrow::Cow;
2use std::collections::BTreeMap;
3use std::collections::BTreeSet;
4use std::fmt::Display;
5use std::sync::Arc;
6use std::sync::RwLock;
7
8use bindgen::callbacks::DiscoveredItem;
9use bindgen::callbacks::DiscoveredItemId;
10use convert_case::{Case, Casing};
11
12
13pub type SharedRenamed = Arc<RwLock<BTreeMap<Kind, String>>>;
14
15
16pub fn reduce(changes: SharedRenamed) {
17	let mut items = BTreeSet::new();
18	{
19		let changes = changes.read().expect("renamed set is locked");
20		for k in changes.keys().filter(|k| matches!(k, Kind::Struct(_))) {
21			let name = match k {
22				Kind::Struct(name) | Kind::Union(name) => name.as_str(),
23				Kind::Item(_) | Kind::EnumVariant(..) => unreachable!("already filtered-out"),
24			};
25			let ik = Kind::Item(name.to_string());
26			if changes.contains_key(&ik) {
27				items.insert(ik);
28			}
29		}
30	}
31
32	let mut changes = changes.write().expect("renamed set is locked");
33	for k in items {
34		changes.remove(&k);
35	}
36}
37
38
39pub fn print_as_md_table(changes: SharedRenamed) {
40	{
41		let mut enums = BTreeSet::new();
42		let changes = changes.read().expect("renamed set is locked");
43		let iter = changes.keys().filter_map(|k| {
44			                         if let Kind::EnumVariant(name, _) = k {
45				                         Some(name)
46			                         } else {
47				                         None
48			                         }
49		                         });
50		enums.extend(iter);
51
52		let find_item = |name: &str| {
53			let key = Kind::Item(name.to_owned());
54			changes.get(&key)
55		};
56
57		// print
58		println!("| kind | original | generated |");
59		println!("| ----: | :-------- | :-------- |");
60		for (was, now) in changes.iter() {
61			match was {
62				Kind::Item(name) => {
63					let kind = if enums.contains(name) { "enum" } else { "item" };
64					println!("| {kind} | `{name}` | `{now}` |");
65				},
66				Kind::Struct(name) => {
67					println!("| struct | `{name}` | `{now}` |");
68				},
69				Kind::Union(name) => {
70					println!("| union | `{name}` | `{now}` |");
71				},
72				Kind::EnumVariant(name, var) => {
73					let ren = find_item(name).map(String::as_str).unwrap_or("_");
74					println!("| enum ctor | `{name}::{var}` | `{ren}::{now}` |");
75				},
76			}
77		}
78
79		println!("\n_total: {}_", changes.len());
80	}
81}
82
83
84/// Renames symbols in the bindings.
85#[derive(Debug, Default)]
86pub struct RenameMap {
87	pub renamed: SharedRenamed,
88}
89
90impl RenameMap {
91	pub fn new() -> Self { Default::default() }
92
93	fn renamed(&self, was: Kind, now: String) {
94		self.renamed
95		    .write()
96		    .expect("renamed set is locked")
97		    .insert(was, now);
98	}
99}
100
101
102/// Contains original names.
103#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
104pub enum Kind {
105	/// Item with original name
106	Item(String),
107	/// Struct with original name
108	Struct(String),
109	/// Union with original name
110	Union(String),
111	/// `(enum name, variant name)`
112	EnumVariant(String, String),
113}
114
115impl Display for Kind {
116	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117		match self {
118			Kind::Item(name) => {
119				write!(f, "item `{name}`")
120			},
121			Kind::Struct(name) => {
122				write!(f, "struct `{name}`")
123			},
124			Kind::Union(name) => {
125				write!(f, "union `{name}`")
126			},
127			Kind::EnumVariant(name, variant) => {
128				write!(f, "enum `{name}::{variant}`")
129			},
130		}
131	}
132}
133
134
135impl bindgen::callbacks::ParseCallbacks for RenameMap {
136	fn new_item_found(&self, _id: DiscoveredItemId, item: DiscoveredItem) {
137		match item {
138			DiscoveredItem::Struct { original_name: Some(original_name),
139			                         final_name, } => {
140				self.renamed(Kind::Struct(original_name), final_name);
141			},
142			DiscoveredItem::Union { original_name: Some(original_name),
143			                        final_name, } => {
144				self.renamed(Kind::Union(original_name), final_name);
145			},
146			_ => {},
147		}
148	}
149
150	fn item_name(&self, name: &str) -> Option<String> {
151		if name.starts_with("__") ||
152		   name.starts_with("_bindgen_") ||
153		   name.starts_with("ptr_") ||
154		   name.ends_with("_t")
155		{
156			return None;
157		}
158
159		let mut exact = BTreeMap::new();
160		exact.insert("PDNetErr", "NetworkError");
161		exact.insert("PlaydateAPI", "Playdate");
162		exact.insert("playdate_videostream", "PlaydateVideoStream");
163		exact.insert("l_valtype", "LuaValueType");
164		exact.insert("PDRect", "Rect");
165		exact.insert("LCDRect", "Aabb");
166
167		let mut ignore = BTreeSet::new();
168		ignore.extend([
169			"void",
170			"root",
171			"unsigned_int",
172			"unsigned_long",
173			"va_list",
174			"float",
175			//
176			"SEEK_SET",
177			"SEEK_CUR",
178			"SEEK_END",
179			"LCD_COLUMNS",
180			"LCD_ROWS",
181			"LCD_ROWSIZE",
182			"AUDIO_FRAMES_PER_CYCLE",
183			"NOTE_C4",
184		]);
185
186		if ignore.contains(name) {
187			return None;
188		}
189
190		if let Some(s) = exact.get(name) {
191			self.renamed(Kind::Item(name.to_owned()), s.to_string());
192			return Some(s.to_string());
193		}
194
195		let res = name.strip_prefix("PD")
196		              .or_else(|| name.strip_prefix("LCD"))
197		              .unwrap_or(name)
198		              .to_case(Case::UpperCamel);
199
200		if res != name {
201			self.renamed(Kind::Item(name.to_owned()), res.clone());
202			Some(res)
203		} else {
204			None
205		}
206	}
207
208	fn enum_variant_name(&self,
209	                     ename: Option<&str>,
210	                     vname: &str,
211	                     _: bindgen::callbacks::EnumVariantValue)
212	                     -> Option<String> {
213		let mut exact_var = BTreeMap::new();
214		exact_var.insert("kASCIIEncoding", "ASCII");
215		exact_var.insert("kUTF8Encoding", "UTF8");
216		exact_var.insert("k16BitLEEncoding", "UTF16");
217
218		exact_var.insert("kSound8bitMono", "Mono8bit");
219		exact_var.insert("kSound8bitStereo", "Stereo8bit");
220		exact_var.insert("kSound16bitMono", "Mono16bit");
221		exact_var.insert("kSound16bitStereo", "Stereo16bit");
222		exact_var.insert("kSoundADPCMMono", "MonoADPCM");
223		exact_var.insert("kSoundADPCMStereo", "StereoADPCM");
224		exact_var.insert("kColorXOR", "XOR");
225		exact_var.insert("kDrawModeXOR", "XOR");
226		exact_var.insert("kDrawModeNXOR", "NXOR");
227
228		let mut prefix = BTreeMap::new();
229		prefix.insert("PDTextWrappingMode", "Wrap");
230		prefix.insert("PDTextAlignment", "AlignText");
231		prefix.insert("MicSource", "MicInput");
232		prefix.insert("PDLanguage", "PdLanguage");
233		prefix.insert("LCDFontLanguage", "LcdFontLanguage");
234		prefix.insert("LFOType", "LfoType");
235		prefix.insert("PDButtons", "Button");
236		prefix.insert("json_value_type", "Json");
237
238		let mut ignore = BTreeSet::new();
239		ignore.extend(["idtype_t"]);
240
241		let ename = ename.expect("enum name for {vname} must not be empty");
242
243
244		if ename.starts_with("__") || vname.starts_with("__") {
245			#[cfg(feature = "log")]
246			println!("skip renaming: {ename}::{vname}");
247			return None;
248		}
249		if ignore.contains(&ename) || ignore.contains(vname) {
250			return None;
251		}
252
253
254		// workaround bindgen's bug: enum name is prefixed with "enum " sometimes
255		let ename = ename.strip_prefix("enum ").unwrap_or(ename);
256
257
258		if let Some(s) = exact_var.get(vname) {
259			self.renamed(
260			             Kind::EnumVariant(ename.to_owned(), vname.to_owned()),
261			             s.to_string(),
262			);
263			return Some(s.to_string());
264		}
265
266
267		let res = if vname.starts_with('k') {
268			Cow::Owned((&vname[1..]).to_case(Case::UpperCamel))
269		} else {
270			vname.into()
271		};
272
273
274		let res = if let Some(prefix) = prefix.get(&ename) {
275			res.strip_prefix(prefix).unwrap_or(&res)
276		} else {
277			&res
278		};
279
280		let mut res = res.to_case(Case::UpperCamel);
281
282		// auto-trim-prefix:
283		{
284			let eparts = Case::Pascal.split(&ename);
285			let vparts = Case::Pascal.split(&res);
286			let mut rparts = vparts.as_slice();
287
288			for word in eparts.iter() {
289				if let Some(parts) = rparts.strip_prefix(&[*word]) {
290					rparts = parts;
291				}
292			}
293
294			if rparts != vparts {
295				res = rparts.join("");
296			}
297		}
298
299
300		if res != vname {
301			self.renamed(Kind::EnumVariant(ename.to_owned(), vname.to_owned()), res.clone());
302		}
303
304		if res != vname { Some(res) } else { None }
305	}
306}