This commit is contained in:
Vladislavs Golubs 2021-02-09 03:04:40 +03:00
parent e8a061e672
commit c6d1cdecc7
4 changed files with 142 additions and 129 deletions

View File

@ -1,7 +1,11 @@
use crate::frontend;
use handlebars::{Handlebars, TemplateRenderError};
use serde::Serialize; use serde::Serialize;
use serde_json::json;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt; use std::fmt;
use std::fmt::Display; use std::fmt::Display;
use std::io::Write;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum State { pub enum State {
@ -172,3 +176,48 @@ impl Protocol {
.collect() .collect()
} }
} }
#[derive(Serialize)]
struct GenerateContext<'a> {
packet_enum_name: String,
packets: &'a Vec<frontend::Packet>,
}
pub fn generate_rust_file<W: Write>(
protocol: &frontend::Protocol,
template_engine: &Handlebars,
mut writer: W,
) -> Result<(), TemplateRenderError> {
let server_bound_ctx = GenerateContext {
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, frontend::Bound::Server),
packets: &protocol.server_bound_packets,
};
let client_bound_ctx = GenerateContext {
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, frontend::Bound::Client),
packets: &protocol.client_bound_packets,
};
let mut imports = vec![
"crate::DecodeError",
"crate::Decoder",
"std::io::Read",
"minecraft_protocol_derive::Packet",
];
imports.extend(protocol.data_type_imports().iter());
template_engine.render_to_write(
"packet_imports",
&json!({ "imports": imports }),
&mut writer,
)?;
template_engine.render_to_write("packet_enum", &server_bound_ctx, &mut writer)?;
template_engine.render_to_write("packet_enum", &client_bound_ctx, &mut writer)?;
template_engine.render_to_write("packet_structs", &server_bound_ctx, &mut writer)?;
template_engine.render_to_write("packet_structs", &client_bound_ctx, &mut writer)?;
Ok(())
}

View File

@ -1,18 +1,13 @@
use std::fs::File; use std::fs::File;
use std::io::Write;
use crate::mappings::CodeMappings; use crate::mappings::CodeMappings;
use crate::transformer::transform_protocol;
use handlebars::*;
use heck::SnakeCase;
use serde::Serialize;
use serde_json::json;
use structopt::StructOpt; use structopt::StructOpt;
pub mod backend; pub mod backend;
pub mod frontend; pub mod frontend;
pub mod mappings; pub mod mappings;
pub mod transformer; pub mod templates;
pub mod transformers;
#[derive(StructOpt)] #[derive(StructOpt)]
#[structopt(name = "protocol-generator")] #[structopt(name = "protocol-generator")]
@ -23,7 +18,7 @@ struct Opt {
pub fn main() { pub fn main() {
let opt: Opt = Opt::from_args(); let opt: Opt = Opt::from_args();
let template_engine = create_template_engine(); let template_engine = templates::create_template_engine();
let protocol_data_file_name = format!( let protocol_data_file_name = format!(
"protocol-generator/minecraft-data/data/pc/{}/protocol.json", "protocol-generator/minecraft-data/data/pc/{}/protocol.json",
@ -40,7 +35,7 @@ pub fn main() {
let protocols = vec![ let protocols = vec![
( (
transform_protocol( transformers::transform_protocol(
&mappings, &mappings,
frontend::State::Handshake, frontend::State::Handshake,
&protocol_input.handshaking, &protocol_input.handshaking,
@ -48,15 +43,27 @@ pub fn main() {
frontend::State::Handshake, frontend::State::Handshake,
), ),
( (
transform_protocol(&mappings, frontend::State::Status, &protocol_input.status), transformers::transform_protocol(
&mappings,
frontend::State::Status,
&protocol_input.status,
),
frontend::State::Status, frontend::State::Status,
), ),
( (
transform_protocol(&mappings, frontend::State::Login, &protocol_input.login), transformers::transform_protocol(
&mappings,
frontend::State::Login,
&protocol_input.login,
),
frontend::State::Login, frontend::State::Login,
), ),
( (
transform_protocol(&mappings, frontend::State::Game, &protocol_input.game), transformers::transform_protocol(
&mappings,
frontend::State::Game,
&protocol_input.game,
),
frontend::State::Game, frontend::State::Game,
), ),
]; ];
@ -68,123 +75,7 @@ pub fn main() {
); );
let file = File::create(file_name).expect("Failed to create file"); let file = File::create(file_name).expect("Failed to create file");
generate_rust_file(&protocol, &template_engine, &file) frontend::generate_rust_file(&protocol, &template_engine, &file)
.expect("Failed to generate rust file"); .expect("Failed to generate rust file");
} }
} }
fn create_template_engine() -> Handlebars<'static> {
let mut template_engine = Handlebars::new();
template_engine.register_helper("snake_case", Box::new(format_snake_case));
template_engine.register_helper("packet_id", Box::new(format_packet_id));
template_engine.register_escape_fn(|s| s.to_owned());
template_engine
.register_template_file(
"packet_imports",
"protocol-generator/templates/packet_imports.hbs",
)
.expect("Failed to register template");
template_engine
.register_template_file(
"packet_enum",
"protocol-generator/templates/packet_enum.hbs",
)
.expect("Failed to register template");
template_engine
.register_template_file(
"packet_structs",
"protocol-generator/templates/packet_structs.hbs",
)
.expect("Failed to register template");
template_engine
}
#[derive(Serialize)]
struct GenerateContext<'a> {
packet_enum_name: String,
packets: &'a Vec<frontend::Packet>,
}
fn generate_rust_file<W: Write>(
protocol: &frontend::Protocol,
template_engine: &Handlebars,
mut writer: W,
) -> Result<(), TemplateRenderError> {
let server_bound_ctx = GenerateContext {
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, frontend::Bound::Server),
packets: &protocol.server_bound_packets,
};
let client_bound_ctx = GenerateContext {
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, frontend::Bound::Client),
packets: &protocol.client_bound_packets,
};
let mut imports = vec![
"crate::DecodeError",
"crate::Decoder",
"std::io::Read",
"minecraft_protocol_derive::Packet",
];
imports.extend(protocol.data_type_imports().iter());
template_engine.render_to_write(
"packet_imports",
&json!({ "imports": imports }),
&mut writer,
)?;
template_engine.render_to_write("packet_enum", &server_bound_ctx, &mut writer)?;
template_engine.render_to_write("packet_enum", &client_bound_ctx, &mut writer)?;
template_engine.render_to_write("packet_structs", &server_bound_ctx, &mut writer)?;
template_engine.render_to_write("packet_structs", &client_bound_ctx, &mut writer)?;
Ok(())
}
fn format_snake_case(
h: &Helper,
_: &Handlebars,
_: &Context,
_: &mut RenderContext,
out: &mut dyn Output,
) -> Result<(), RenderError> {
let str = h
.param(0)
.and_then(|v| v.value().as_str())
.ok_or(RenderError::new(
"Param 0 with str type is required for snake case helper.",
))? as &str;
let snake_case_str = str.to_snake_case();
out.write(snake_case_str.as_ref())?;
Ok(())
}
fn format_packet_id(
h: &Helper,
_: &Handlebars,
_: &Context,
_: &mut RenderContext,
out: &mut dyn Output,
) -> Result<(), RenderError> {
let id = h
.param(0)
.and_then(|v| v.value().as_u64())
.ok_or(RenderError::new(
"Param 0 with u64 type is required for packet id helper.",
))? as u64;
let packet_id_str = format!("{:#04X}", id);
out.write(packet_id_str.as_ref())?;
Ok(())
}

View File

@ -0,0 +1,73 @@
use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError};
use heck::SnakeCase;
pub fn create_template_engine() -> Handlebars<'static> {
let mut template_engine = Handlebars::new();
template_engine.register_helper("snake_case", Box::new(format_snake_case));
template_engine.register_helper("packet_id", Box::new(format_packet_id));
template_engine.register_escape_fn(|s| s.to_owned());
template_engine
.register_template_file(
"packet_imports",
"protocol-generator/templates/packet_imports.hbs",
)
.expect("Failed to register template");
template_engine
.register_template_file(
"packet_enum",
"protocol-generator/templates/packet_enum.hbs",
)
.expect("Failed to register template");
template_engine
.register_template_file(
"packet_structs",
"protocol-generator/templates/packet_structs.hbs",
)
.expect("Failed to register template");
template_engine
}
fn format_snake_case(
h: &Helper,
_: &Handlebars,
_: &Context,
_: &mut RenderContext,
out: &mut dyn Output,
) -> Result<(), RenderError> {
let str = h
.param(0)
.and_then(|v| v.value().as_str())
.ok_or(RenderError::new(
"Param 0 with str type is required for snake case helper.",
))? as &str;
let snake_case_str = str.to_snake_case();
out.write(snake_case_str.as_ref())?;
Ok(())
}
fn format_packet_id(
h: &Helper,
_: &Handlebars,
_: &Context,
_: &mut RenderContext,
out: &mut dyn Output,
) -> Result<(), RenderError> {
let id = h
.param(0)
.and_then(|v| v.value().as_u64())
.ok_or(RenderError::new(
"Param 0 with u64 type is required for packet id helper.",
))? as u64;
let packet_id_str = format!("{:#04X}", id);
out.write(packet_id_str.as_ref())?;
Ok(())
}