1use indexmap::IndexMap;
2use std::fs::File;
3
4use heck::ToShoutySnekCase;
5use proc_macro2::TokenStream;
6use quote::format_ident;
7use quote::quote;
8
9pub fn build() -> TokenStream {
10 let packets_file = File::open("airi-codegen/assets/packets.json").unwrap();
11 let packet_data: IndexMap<String, IndexMap<String, IndexMap<String, i32>>> =
12 serde_json::from_reader(packets_file).unwrap();
13
14 let mut seen_packets: IndexMap<String, (String, String, i32)> = IndexMap::new();
16
17 for (state, dirs) in packet_data {
18 for (dir, packets) in dirs {
19 for (mut packet, id) in packets {
20 packet = packet.strip_prefix("minecraft:").unwrap().to_owned();
21 insert_recurse(&mut seen_packets, packet, &dir, &state, id);
22 }
23 }
24 }
25
26 let mut packet_declarations = Vec::new();
27
28 for (packet, (_, _, id)) in seen_packets {
29 let var_name = format_ident!("{}", packet.replace('/', "_").TO_SHOUTY_SNEK_CASE());
30 packet_declarations.push(quote! { pub const #var_name: i32 = #id; });
31 }
32
33 quote! {
34 #(#packet_declarations)*
35 }
36}
37
38fn insert_recurse(
40 seen_packets: &mut IndexMap<String, (String, String, i32)>,
41 mut packet: String,
42 dir: &String,
43 state: &String,
44 id: i32,
45) {
46 if let Some(other) = seen_packets.shift_remove(&packet) {
48 if &other.0 == dir {
49 packet.push('_');
50 insert_recurse(
51 seen_packets,
52 packet.clone() + &other.1,
53 &other.0,
54 &other.1,
55 other.2,
56 );
57 insert_recurse(seen_packets, packet.clone() + state, dir, state, id);
58 } else {
59 insert_recurse(
60 seen_packets,
61 other.0.clone() + "_" + &packet,
62 &other.0,
63 &other.1,
64 other.2,
65 );
66 insert_recurse(seen_packets, dir.clone() + "_" + &packet, dir, state, id);
67 }
68 } else {
69 seen_packets.insert(packet.clone(), (dir.clone(), state.clone(), id));
70 }
71}