wicked-waifus-rs/shorekeeper-protocol/build.rs

190 lines
5.7 KiB
Rust
Raw Normal View History

2024-09-11 16:37:46 +00:00
use std::{
fs,
io::{self, BufRead},
path::Path,
};
2024-09-24 18:59:18 +00:00
use std::io::Write;
2024-09-11 16:37:46 +00:00
2024-09-24 18:59:18 +00:00
use quote::{format_ident, quote};
2024-09-11 16:37:46 +00:00
const CODEGEN_OUT_DIR: &str = "generated/";
pub fn main() {
let _ = fs::create_dir(CODEGEN_OUT_DIR);
let config_file = "proto/config.csv";
let config_path = Path::new("proto/config.csv");
if config_path.exists() {
println!("cargo:rerun-if-changed={config_file}");
impl_proto_config(config_path, Path::new("generated/proto_config.rs")).unwrap();
2024-09-11 16:37:46 +00:00
}
let proto_file = "proto/shorekeeper.proto";
if Path::new(&proto_file).exists() {
println!("cargo:rerun-if-changed={proto_file}");
prost_build::Config::new()
.out_dir(CODEGEN_OUT_DIR)
.type_attribute(".", "#[derive(shorekeeper_protocol_derive::MessageID)]")
2024-09-24 18:59:18 +00:00
.type_attribute(".", "#[derive(serde::Serialize,serde::Deserialize)]")
2024-09-11 16:37:46 +00:00
.compile_protos(&[proto_file], &["shorekeeper"])
.unwrap();
2024-09-24 18:59:18 +00:00
impl_dumper(Path::new("generated/shorekeeper.rs")).unwrap();
2024-09-11 16:37:46 +00:00
impl_message_id(Path::new("generated/shorekeeper.rs")).unwrap();
}
let proto_file = "proto/internal.proto";
if Path::new(&proto_file).exists() {
println!("cargo:rerun-if-changed={proto_file}");
prost_build::Config::new()
.out_dir(CODEGEN_OUT_DIR)
.type_attribute(".", "#[derive(shorekeeper_protocol_derive::MessageID)]")
.compile_protos(&[proto_file], &["internal"])
.unwrap();
impl_message_id(Path::new("generated/internal.rs")).unwrap();
}
let proto_file = "proto/data.proto";
if Path::new(&proto_file).exists() {
println!("cargo:rerun-if-changed={proto_file}");
prost_build::Config::new()
.out_dir(CODEGEN_OUT_DIR)
.compile_protos(&[proto_file], &["data"])
.unwrap();
}
}
pub fn impl_proto_config(csv_path: &Path, codegen_path: &Path) -> io::Result<()> {
let file = fs::File::open(csv_path)?;
let reader = io::BufReader::new(file);
let mut match_arms = quote! {};
for line in reader.lines() {
let line = line?;
let mut row = line.split(',');
let message_id = row.next().unwrap().parse::<u16>().unwrap();
let flags = row.next().unwrap().parse::<u8>().unwrap();
match_arms = quote! {
#match_arms
#message_id => MessageFlags(#flags),
}
}
let generated_code = quote! {
pub mod proto_config {
#[derive(Clone, Copy)]
pub struct MessageFlags(u8);
impl MessageFlags {
pub fn value(self) -> u8 {
self.0
}
}
pub fn get_message_flags(id: u16) -> MessageFlags {
match id {
#match_arms
_ => MessageFlags(0),
}
}
}
2024-09-24 18:59:18 +00:00
};
2024-09-11 16:37:46 +00:00
2024-09-24 18:59:18 +00:00
let syntax_tree = syn::parse2(generated_code).unwrap();
let formatted = prettyplease::unparse(&syntax_tree);
fs::write(codegen_path, formatted.as_bytes())?;
2024-09-11 16:37:46 +00:00
Ok(())
}
pub fn impl_message_id(path: &Path) -> io::Result<()> {
let file = fs::File::open(path)?;
let reader = io::BufReader::new(file);
let mut output = Vec::new();
let mut attr = None;
for line in reader.lines() {
let line = line?;
if line.contains("MessageId:") {
attr = Some(make_message_id_attr(&line).unwrap());
} else {
output.push(line);
if let Some(attr) = attr.take() {
output.push(attr);
}
}
}
fs::write(path, output.join("\n").as_bytes())?;
Ok(())
}
fn make_message_id_attr(line: &str) -> Option<String> {
let id = line.trim_start().split(' ').nth(2)?.parse::<u16>().ok()?;
Some(format!("#[message_id({id})]"))
}
2024-09-24 18:59:18 +00:00
pub fn impl_dumper(codegen_path: &Path) -> io::Result<()> {
let file = fs::File::open(codegen_path)?;
let reader = io::BufReader::new(file);
let mut match_arms = quote! {};
let mut id = None;
for line in reader.lines() {
let line = line?;
if line.contains("MessageId:") {
id = Some(
line.trim_start().split(' ').nth(2).unwrap().parse::<u16>().ok().unwrap()
);
} else if line.contains("pub struct") {
if let Some(id) = id.take() {
2024-09-25 01:42:19 +00:00
let name = line.trim_start().split(' ').nth(2).unwrap().to_string();
let name_ident = format_ident!("{}", name);
2024-09-24 18:59:18 +00:00
match_arms = quote! {
#match_arms
2024-09-25 01:42:19 +00:00
#id => Ok((#name, serde_json::to_string_pretty(&#name_ident::decode(data)?)?)),
2024-09-24 18:59:18 +00:00
};
}
}
}
let generated_code = quote! {
pub mod proto_dumper {
use prost::Message;
use crate::*;
use crate::ai::*;
use crate::combat_message::*;
use crate::debug::*;
use crate::summon::*;
2024-09-25 01:42:19 +00:00
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("serde_json::Error: {0}")]
Json(#[from] serde_json::Error),
#[error("serde_json::Error: {0}")]
Decode(#[from] prost::DecodeError),
}
pub fn get_debug_info(id: u16, data: &[u8]) -> Result<(&str, String), Error> {
2024-09-24 18:59:18 +00:00
match id {
#match_arms
2024-09-25 01:42:19 +00:00
_ => Ok(("UnknownType", "".to_string())),
}
2024-09-24 18:59:18 +00:00
}
}
};
let syntax_tree = syn::parse2(generated_code).unwrap();
let formatted = prettyplease::unparse(&syntax_tree);
let mut file = fs::OpenOptions::new().append(true).open(codegen_path)?;
file.write(formatted.as_bytes())?;
Ok(())
}