From 12f8e16f5c783033b8606466e40479987f3852ba Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:09:54 -0500 Subject: [PATCH] - add: start of code generator for rust - fix: unable to add new generator due to generator not being boxed --- Cargo.lock | 2 +- generator/Cargo.toml | 2 +- generator/src/codegen/mod.rs | 5 +- generator/src/codegen/rust/codegen_source.rs | 139 +++++++++++++++++++ generator/src/codegen/rust/mod.rs | 84 +++++++++++ generator/src/main.rs | 9 +- 6 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 generator/src/codegen/rust/codegen_source.rs create mode 100644 generator/src/codegen/rust/mod.rs diff --git a/Cargo.lock b/Cargo.lock index dd3dadb..a7695d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "packet_generator" -version = "0.2.0" +version = "0.2.2" dependencies = [ "clap", "failure", diff --git a/generator/Cargo.toml b/generator/Cargo.toml index 6772f60..eee54c8 100644 --- a/generator/Cargo.toml +++ b/generator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "packet_generator" -version = "0.2.0" +version = "0.2.2" authors = ["L3nn0x "] [dependencies] diff --git a/generator/src/codegen/mod.rs b/generator/src/codegen/mod.rs index 8958777..31fd64a 100644 --- a/generator/src/codegen/mod.rs +++ b/generator/src/codegen/mod.rs @@ -16,11 +16,14 @@ pub(crate) trait Codegen { } pub mod cpp; +pub mod rust; use clap::Subcommand; #[derive(Subcommand, Debug)] pub enum CodegenCommands { #[command(name = "cpp")] - CppCommand(cpp::CppArgs) + CppCommand(cpp::CppArgs), + #[command(name = "rust")] + RustCommand(rust::RustArgs) } \ No newline at end of file diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs new file mode 100644 index 0000000..22b5cd7 --- /dev/null +++ b/generator/src/codegen/rust/codegen_source.rs @@ -0,0 +1,139 @@ +use ::flat_ast::*; +use std::io::{Result, Write}; +use ::heck::*; +use std::collections::HashSet; + +pub (crate) struct CodeSourceGenerator<'a, W: Write + 'a> { + writer: &'a mut ::writer::Writer, + version: String +} + +impl<'a, W: Write> CodeSourceGenerator<'a, W> { + pub fn new(writer: &'a mut ::writer::Writer, version: String) -> Self { + Self { + writer, + version + } + } + + fn indent(&mut self) { + self.writer.indent(); + } + + fn dedent(&mut self) { + self.writer.dedent(); + } + + fn write(&mut self, val: impl AsRef) -> Result<&mut Self> { + self.writer.write(val)?; + Ok(self) + } + + pub fn generate(&mut self, packet: &Packet) -> Result<()> { + let version = self.version.clone(); + cg!(self, "/* Generated with IDL v{} */\n", version); + cg!(self); + cg!(self, r#"use bincode::{{Encode, Decode}};"#); + cg!(self); + + + cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); + + let iserialize = packet.contents().iter().filter_map(|elem| { + if PacketContent::is_type(elem) { + PacketContent::type_from_name(elem) + } else { + match elem { + PacketContent::Element(ref e) => { + match e.type_().as_ref() { + "int8_t" => Some("i8".to_string()), + "uint8_t" => Some("u8".to_string()), + "int16_t" => Some("i16".to_string()), + "uint16_t" => Some("u16".to_string()), + "int32_t" => Some("i32".to_string()), + "uint32_t" => Some("u32".to_string()), + "int64_t" => Some("i64".to_string()), + "uint64_t" => Some("u64".to_string()), + "char" => Some("u8".to_string()), + "float" => Some("f32".to_string()), + "double" => Some("f64".to_string()), + "std::string" => Some("String".to_string()), + _ => Some(e.type_().to_string()) + } + }, + _ => None + } + } + }).collect::<::std::collections::HashSet>(); + + // Need to drop out the struct + cg!(self, "pub struct {} {{", packet.class_name()); + self.indent(); + for content in packet.contents() { + use self::PacketContent::*; + match content { + Element(ref elem) => self.element(elem)?, + _ => {} + }; + } + self.dedent(); + cg!(self, "}}"); + + + Ok(()) + } + + fn doc(&mut self, doc: &Option) -> Result<()> { + match doc { + None => (), + Some(doc) => { + for line in doc.lines() { + match line.trim() { + "" => (), + line => { + cg!(self, "// {}", line); + } + } + } + } + }; + Ok(()) + } + + fn element(&mut self, elem: &Element) -> Result<()> { + self.doc(elem.doc())?; + + if let Some(bitset) = elem.bitset() { + if bitset.start == 0 { + cg!(self, "{}: [bool; {}],", bitset.name, bitset.size); + } + return Ok(()); + } + + let (type_, bits) = if let Some(ref o) = elem.occurs() { + use ::flat_ast::Occurs::*; + let type_ = match o { + Unbounded => format!("Vec<{}>", elem.type_()), + Num(n) => format!("[{}; {}]", elem.type_(), n) + }; + (type_, "".to_string()) + } else { + let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!(" : {}", b)); + (elem.type_().to_owned(), bits) + }; + let default = match elem.init() { + self::ElementInitValue::Default(d) => " = ".to_string() + d, + _ => "".to_string() + }; + cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); + Ok(()) + } +} + +fn clean_base(base: &str) -> String { + if base.contains("::") { + base.split("::").skip(1).collect() + } else { + base.to_string() + } +} diff --git a/generator/src/codegen/rust/mod.rs b/generator/src/codegen/rust/mod.rs new file mode 100644 index 0000000..32e0301 --- /dev/null +++ b/generator/src/codegen/rust/mod.rs @@ -0,0 +1,84 @@ +use std::fs::File; +use std::path::PathBuf; +use codegen::Codegen; +use ::{flat_ast, writer}; + +mod codegen_source; + +pub struct Generator { + output: PathBuf +} + +impl Generator { + pub fn new(args: &RustArgs) -> Self { + Self{ + output: args.output_folder.clone().into() + } + } +} + +impl Codegen for Generator { + fn generate(&mut self, version: &str, packet: &flat_ast::Packet) -> Result<(), failure::Error> { + let source_output = File::create(self.output.to_str().unwrap().to_owned() + &format!("/{}.rs", packet.filename()))?; + debug!("source {:?}", source_output); + let mut writer = writer::Writer::new(source_output); + let mut codegen = codegen_source::CodeSourceGenerator::new(&mut writer, version.to_string()); + codegen.generate(&packet)?; + Ok(()) + } +} + +#[derive(clap::Args, Debug)] +#[command(name="rust")] +pub struct RustArgs { + #[arg(long)] + output_folder: String +} + +#[cfg(test)] +mod tests { + use crate::{flat_ast::Packet, writer::Writer}; + use super::{codegen_source}; + + struct StringWriter { + output: String + } + + impl StringWriter { + fn new() -> Self { + Self { output: String::new() } + } + } + + impl std::io::Write for StringWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.output += std::str::from_utf8(buf).unwrap(); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + } + + impl Into for StringWriter { + fn into(self) -> String { + self.output + } + } + + fn call_header(packet: &Packet) -> std::io::Result { + let writer = StringWriter::new(); + let mut writer = Writer::new(writer); + let mut codegen = codegen_source::CodeSourceGenerator::new(&mut writer, "0".to_string()); + codegen.generate(packet)?; + Ok(writer.into().into()) + } + + #[test] + fn empty_packet() { + let packet = Packet::new("PAKCS_PACKET".to_owned(), None); + let result = call_header(&packet); + assert!(result.is_ok()); + } +} \ No newline at end of file diff --git a/generator/src/main.rs b/generator/src/main.rs index ab9b384..15416c1 100644 --- a/generator/src/main.rs +++ b/generator/src/main.rs @@ -11,7 +11,7 @@ mod writer; mod codegen; mod graph_passes; -use codegen::{cpp, Codegen, CodegenCommands}; +use codegen::{cpp, rust, Codegen, CodegenCommands}; use log::Level; @@ -24,7 +24,7 @@ struct Args { #[arg(short, long)] inputs: Vec, #[command(subcommand)] - command: codegen::CodegenCommands, + command: CodegenCommands, #[arg(short, long, action = clap::ArgAction::Count)] verbose: u8 @@ -53,8 +53,9 @@ fn main() -> Result<(), failure::Error> { trace!("packet {:?}", packet); let packet = graph_passes::run(packet)?; debug!("packet {:#?}", packet); - let mut generator = match &args.command { - CodegenCommands::CppCommand(args) => cpp::Generator::new(args) + let mut generator: Box = match &args.command { + CodegenCommands::CppCommand(args) => Box::new(cpp::Generator::new(args)), + CodegenCommands::RustCommand(args) => Box::new(rust::Generator::new(args)), }; generator.generate(VERSION, &packet)?; info!("Generated packet {}", packet.type_());