playdate_bindgen/rustify/
rename.rs1use 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 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#[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#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
104pub enum Kind {
105 Item(String),
107 Struct(String),
109 Union(String),
111 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 "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 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 {
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}