Skip to content

Commit

Permalink
- add: start of code generator for rust
Browse files Browse the repository at this point in the history
- fix: unable to add new generator due to generator not being boxed
  • Loading branch information
RavenX8 committed Nov 27, 2024
1 parent 33258e5 commit 12f8e16
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion generator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "packet_generator"
version = "0.2.0"
version = "0.2.2"
authors = ["L3nn0x <[email protected]>"]

[dependencies]
Expand Down
5 changes: 4 additions & 1 deletion generator/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
139 changes: 139 additions & 0 deletions generator/src/codegen/rust/codegen_source.rs
Original file line number Diff line number Diff line change
@@ -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<W>,
version: String
}

impl<'a, W: Write> CodeSourceGenerator<'a, W> {
pub fn new(writer: &'a mut ::writer::Writer<W>, 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<str>) -> 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<String>>();

// 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<String>) -> 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()
}
}
84 changes: 84 additions & 0 deletions generator/src/codegen/rust/mod.rs
Original file line number Diff line number Diff line change
@@ -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<usize> {
self.output += std::str::from_utf8(buf).unwrap();
Ok(buf.len())
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

impl Into<String> for StringWriter {
fn into(self) -> String {
self.output
}
}

fn call_header(packet: &Packet) -> std::io::Result<String> {
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());
}
}
9 changes: 5 additions & 4 deletions generator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -24,7 +24,7 @@ struct Args {
#[arg(short, long)]
inputs: Vec<String>,
#[command(subcommand)]
command: codegen::CodegenCommands,
command: CodegenCommands,
#[arg(short, long, action = clap::ArgAction::Count)]
verbose: u8

Expand Down Expand Up @@ -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<dyn Codegen> = 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_());
Expand Down

0 comments on commit 12f8e16

Please sign in to comment.