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 01/27] - 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_()); From 0dff44ce4e53abd84b302ca26fb02203efd894d5 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:47:49 -0500 Subject: [PATCH 02/27] - fix: mapping C++ types to rust types - remove: unused code --- generator/src/codegen/rust/codegen_source.rs | 66 +++++++++----------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 22b5cd7..7874d6d 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -1,7 +1,8 @@ use ::flat_ast::*; use std::io::{Result, Write}; use ::heck::*; -use std::collections::HashSet; +use std::collections::HashMap; +use schema::ast::Occurs::Unbounded; pub (crate) struct CodeSourceGenerator<'a, W: Write + 'a> { writer: &'a mut ::writer::Writer, @@ -40,31 +41,28 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { 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 + match elem { + PacketContent::Element(ref e) => { + let rust_type = match e.type_().as_ref() { + "int8_t" => "i8", + "uint8_t" => "u8", + "int16_t" => "i16", + "uint16_t" => "u16", + "int32_t" => "i32", + "uint32_t" => "u32", + "int64_t" => "i64", + "uint64_t" => "u64", + "char" => "u8", + "float" => "f32", + "double" => "f64", + "std::string" => "String", + _ => e.type_().as_str(), + }; + Some((e.type_().to_string(), rust_type.to_string())) // Map key and value } + _ => None, } - }).collect::<::std::collections::HashSet>(); + }).collect::>(); // Collect into a HashMap // Need to drop out the struct cg!(self, "pub struct {} {{", packet.class_name()); @@ -72,7 +70,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { for content in packet.contents() { use self::PacketContent::*; match content { - Element(ref elem) => self.element(elem)?, + Element(ref elem) => self.element(elem, &iserialize)?, _ => {} }; } @@ -100,7 +98,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Ok(()) } - fn element(&mut self, elem: &Element) -> Result<()> { + fn element(&mut self, elem: &Element, iserialize: &HashMap) -> Result<()> { self.doc(elem.doc())?; if let Some(bitset) = elem.bitset() { @@ -110,16 +108,18 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { return Ok(()); } + let Some(rust_type) = iserialize.get(elem.type_()) else { warn!(r#"Type "{}" not found"#, elem.type_()); 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) + Unbounded => format!("Vec<{}>", rust_type), + Num(n) => format!("[{}; {}]", rust_type, n) }; (type_, "".to_string()) } else { let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!(" : {}", b)); - (elem.type_().to_owned(), bits) + (rust_type.to_owned(), bits) }; let default = match elem.init() { self::ElementInitValue::Default(d) => " = ".to_string() + d, @@ -129,11 +129,3 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Ok(()) } } - -fn clean_base(base: &str) -> String { - if base.contains("::") { - base.split("::").skip(1).collect() - } else { - base.to_string() - } -} From f1adf365831ea6cfac93e96029d5974145790092 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:25:26 -0500 Subject: [PATCH 03/27] - add: simple and complex type conversion --- generator/src/codegen/rust/codegen_source.rs | 139 ++++++++++++++++++- 1 file changed, 133 insertions(+), 6 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 7874d6d..75e85ee 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -33,13 +33,10 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { 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, r#"use crate::packet::PacketPayload;"#); cg!(self); - - cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); - let iserialize = packet.contents().iter().filter_map(|elem| { match elem { PacketContent::Element(ref e) => { @@ -60,11 +57,56 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { }; Some((e.type_().to_string(), rust_type.to_string())) // Map key and value } + PacketContent::Simple(ref e) => { + let mut rust_type = ("".to_string(), "".to_string()); + for content in e.contents() { + rust_type = match content { + SimpleTypeContent::Restriction(res) => { + let rust_type = match res.base().as_ref() { + "int8_t" => "i8", + "uint8_t" => "u8", + "int16_t" => "i16", + "uint16_t" => "u16", + "int32_t" => "i32", + "uint32_t" => "u32", + "int64_t" => "i64", + "uint64_t" => "u64", + "char" => "u8", + "float" => "f32", + "double" => "f64", + "std::string" => "String", + _ => res.base().as_str(), + }; + (res.base().clone(), rust_type.to_string()) + }, + }; + } + Some(rust_type) + } + // TODO Map complex type _ => None, } }).collect::>(); // Collect into a HashMap - // Need to drop out the struct + for content in packet.contents() { + use self::PacketContent::*; + match content { + Simple(simple) => self.simple_type(simple, &iserialize)?, + _ => {} + } + } + cg!(self); + for content in packet.contents() { + use self::PacketContent::*; + match content { + Complex(ref complex) => self.complex_type(complex, &iserialize)?, + _ => {} + }; + } + + cg!(self); + + cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); cg!(self, "pub struct {} {{", packet.class_name()); self.indent(); for content in packet.contents() { @@ -77,6 +119,9 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { self.dedent(); cg!(self, "}}"); + cg!(self); + cg!(self, "impl PacketPayload for {} {{}}", packet.class_name()); + Ok(()) } @@ -98,6 +143,57 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Ok(()) } + fn complex_type(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { + use ::flat_ast::ComplexTypeContent::*; + if complex.inline() == false { + cg!(self, "struct {} {{", complex.name()); + self.indent(); + match complex.content() { + Seq(ref s) => { + for elem in s.elements() { + self.element(elem, &iserialize)?; + } + }, + Choice(ref c) => { + cg!(self, "union Data {{"); + self.indent(); + for elem in c.elements() { + if let Some(ref seq) = c.inline_seqs().get(elem.name()) { + cg!(self, "struct {{"); + self.indent(); + for e in seq.elements() { + self.element(e, &iserialize)?; + } + self.dedent(); + cg!(self, "}});"); + } else { + self.element(elem, &iserialize)?; + } + } + self.dedent(); + cg!(self, "}}"); + self.dedent(); + }, + Empty => {} + } + self.dedent(); + cg!(self, "}};"); + cg!(self); + } + Ok(()) + } + + fn simple_type(&mut self, simple: &SimpleType, iserialize: &HashMap) -> Result<()> { + cg!(self); + self.doc(simple.doc())?; + for content in simple.contents() { + match content { + SimpleTypeContent::Restriction(res) => self.restrict(res, simple.name(), &iserialize)? + } + } + Ok(()) + } + fn element(&mut self, elem: &Element, iserialize: &HashMap) -> Result<()> { self.doc(elem.doc())?; @@ -108,7 +204,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { return Ok(()); } - let Some(rust_type) = iserialize.get(elem.type_()) else { warn!(r#"Type "{}" not found"#, elem.type_()); return Ok(()) }; + let Some(rust_type) = iserialize.get(elem.type_().trim()) else { warn!(r#"Type "{}" not found"#, elem.type_()); return Ok(()) }; let (type_, bits) = if let Some(ref o) = elem.occurs() { use ::flat_ast::Occurs::*; @@ -128,4 +224,35 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); Ok(()) } + + fn restrict(&mut self, restrict: &Restriction, name: &str, iserialize: &HashMap) -> Result<()> { + use self::RestrictionContent::*; + let is_enum = restrict.contents().iter().find(|content| match content { + Enumeration(_) => true, + _ => false + }).is_some(); + self.doc(restrict.doc())?; + let base = restrict.base().trim(); + let Some(rust_type) = iserialize.get(base) else { warn!(r#"Type "{}" not found"#, base); return Ok(()) }; + + if is_enum { + cg!(self, r#"#[repr({})]"#, rust_type); + cg!(self, "enum {} {{", name.to_upper_camel_case()); + self.indent(); + for content in restrict.contents() { + if let Enumeration(en) = content { + self.doc(en.doc())?; + cg!(self, "{} = {},", en.value(), en.id()); + } + } + } else { + cg!(self, "struct {} {{", name.to_upper_camel_case()); + self.indent(); + cg!(self, "{}: {},", name.to_string().to_snake_case(), rust_type); + } + + self.dedent(); + cg!(self, "}}"); + Ok(()) + } } From 20cc5d441319fc6a8713079e8027596560cfe8cb Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:30:24 -0500 Subject: [PATCH 04/27] - update: github actions/download-artifact to v4 - update: github actions/upload-artifact to v4 --- .github/workflows/rust.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fa2db9d..166fad7 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,7 +20,7 @@ jobs: - name: Prepare Release run: tar --transform 's/.*\///g' -zcvf ${{github.workspace}}/${{ runner.os }}-${{ env.PROCESSOR_ARCH }}-packet_generator.tar.gz target/*/packet_generator - name: Upload linux build - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: linux_build path: ${{github.workspace}}/${{ runner.os }}-${{ env.PROCESSOR_ARCH }}-packet_generator.tar.gz @@ -37,7 +37,7 @@ jobs: copy-item ${{github.workspace}}\target\release\packet_generator.exe -destination ${{github.workspace}}\packet_generator.exe 7z a ${{github.workspace}}/windows-amd64-packet_generator.zip ${{github.workspace}}\packet_generator.exe - name: Upload windows build - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: windows_build path: ${{github.workspace}}/windows-amd64-packet_generator.zip @@ -48,11 +48,11 @@ jobs: if: github.ref == 'refs/heads/master' steps: - name: Download linux build - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: linux_build - name: Download windows build - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: windows_build - name: Release From abd005357a7aaf264a97387203f544ed66ffb693 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:32:38 -0500 Subject: [PATCH 05/27] - remove: Unused import --- generator/src/codegen/rust/codegen_source.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 75e85ee..501c689 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -2,7 +2,6 @@ use ::flat_ast::*; use std::io::{Result, Write}; use ::heck::*; use std::collections::HashMap; -use schema::ast::Occurs::Unbounded; pub (crate) struct CodeSourceGenerator<'a, W: Write + 'a> { writer: &'a mut ::writer::Writer, From eafc8b69478f6a53a303abaa58844ba0b9746a53 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:24:51 -0500 Subject: [PATCH 06/27] - remove: default values can not be set in rust --- generator/src/codegen/rust/codegen_source.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 501c689..7f2e173 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -154,11 +154,12 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } }, Choice(ref c) => { + cg!(self, "#[repr(C)]"); cg!(self, "union Data {{"); self.indent(); for elem in c.elements() { if let Some(ref seq) = c.inline_seqs().get(elem.name()) { - cg!(self, "struct {{"); + cg!(self, "struct {} {{", elem.name()); self.indent(); for e in seq.elements() { self.element(e, &iserialize)?; @@ -220,7 +221,8 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { self::ElementInitValue::Default(d) => " = ".to_string() + d, _ => "".to_string() }; - cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); + // cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); + cg!(self, "{}: {}{},", elem.name(), type_, bits); Ok(()) } From 3b960b60cca275bc41038b0e1551b8a97a0e8519 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:34:15 -0500 Subject: [PATCH 07/27] - fix: file name was being incorrectly parsed --- generator/src/flat_ast.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/generator/src/flat_ast.rs b/generator/src/flat_ast.rs index fd5e0ab..34ea636 100644 --- a/generator/src/flat_ast.rs +++ b/generator/src/flat_ast.rs @@ -146,16 +146,16 @@ impl Packet { , doc: Option) -> Self { use ::heck::*; let name = type_.clone().to_lower_camel_case(); - let (class_name, filename) = if name.starts_with("Isc") { + let (class_name, filename) = if name.starts_with("isc") { (name.clone(), name.clone().to_snake_case()) } else { - if name.starts_with("Pakcs") { + if name.starts_with("pakcs") { let name = "Cli".to_string() + &name[5..]; (name.clone(), name.clone().to_snake_case()) } else { - let name = "Srv".to_string() + &name[5..]; + let name = "srv".to_string() + &name[5..]; (name.clone(), name.clone().to_snake_case()) } From 995e3c44734f3f1a5da9ca8a737672fcf179e465 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:35:01 -0500 Subject: [PATCH 08/27] - update: enum element names should be in camel case for rust --- generator/src/codegen/rust/codegen_source.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 7f2e173..62936b7 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -243,7 +243,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { for content in restrict.contents() { if let Enumeration(en) = content { self.doc(en.doc())?; - cg!(self, "{} = {},", en.value(), en.id()); + cg!(self, "{} = {},", en.value().to_upper_camel_case(), en.id()); } } } else { From 8382ca6e1d5dcb25f570d678b2648465141a8910 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:35:32 -0500 Subject: [PATCH 09/27] - remove: unused default variable --- generator/src/codegen/rust/codegen_source.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 62936b7..aa8a74b 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -217,10 +217,10 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!(" : {}", b)); (rust_type.to_owned(), bits) }; - let default = match elem.init() { - self::ElementInitValue::Default(d) => " = ".to_string() + d, - _ => "".to_string() - }; + // let default = match elem.init() { + // self::ElementInitValue::Default(d) => " = ".to_string() + d, + // _ => "".to_string() + // }; // cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); cg!(self, "{}: {}{},", elem.name(), type_, bits); Ok(()) From 65ee27038daf8bbaad51a7838c176f2b4a5546f4 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:43:50 -0500 Subject: [PATCH 10/27] - fix: field names should not be a reserved rust keyword --- generator/src/codegen/rust/codegen_source.rs | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index aa8a74b..98b0ef0 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -221,8 +221,9 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { // self::ElementInitValue::Default(d) => " = ".to_string() + d, // _ => "".to_string() // }; + let name = rename_if_reserved(elem.name()); // cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); - cg!(self, "{}: {}{},", elem.name(), type_, bits); + cg!(self, "{}: {}{},", name, type_, bits); Ok(()) } @@ -257,3 +258,21 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Ok(()) } } + +fn rename_if_reserved(name: &str) -> String { + let reserved_keywords = [ + "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", + "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", + "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait", + "true", "type", "unsafe", "use", "where", "while", "async", "await", "dyn", + "abstract", "become", "box", "do", "final", "macro", "override", "priv", + "try", "typeof", "unsized", "virtual", "yield", + ]; + + if reserved_keywords.contains(&name) { + format!("{}_", name) // Append a suffix to avoid conflicts + } else { + name.to_string() + } +} + From c26e650cb224ebff60ab838b0e981d1c282c9f44 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Wed, 27 Nov 2024 23:21:01 -0500 Subject: [PATCH 11/27] - fix: more naming issues when outputting rust --- generator/src/codegen/rust/codegen_source.rs | 23 +++++++++++++++----- generator/src/flat_ast.rs | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 98b0ef0..eb8ec4a 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -34,6 +34,16 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "/* Generated with IDL v{} */\n", version); cg!(self, r#"use bincode::{{Encode, Decode}};"#); cg!(self, r#"use crate::packet::PacketPayload;"#); + + for content in packet.contents() { + use self::PacketContent::*; + match content { + Include(ref inc, system) => { + cg!(self, r#"use {};"#, inc); + }, + _ => {} + }; + } cg!(self); let iserialize = packet.contents().iter().filter_map(|elem| { @@ -82,7 +92,10 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } Some(rust_type) } - // TODO Map complex type + PacketContent::Complex(ref e) => { + let rust_type = (e.name().clone(), e.name().clone()); + Some(rust_type) + } _ => None, } }).collect::>(); // Collect into a HashMap @@ -106,7 +119,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self); cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); - cg!(self, "pub struct {} {{", packet.class_name()); + cg!(self, "pub struct {} {{", packet.class_name().to_upper_camel_case()); self.indent(); for content in packet.contents() { use self::PacketContent::*; @@ -119,7 +132,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "}}"); cg!(self); - cg!(self, "impl PacketPayload for {} {{}}", packet.class_name()); + cg!(self, "impl PacketPayload for {} {{}}", packet.class_name().to_upper_camel_case()); Ok(()) @@ -270,9 +283,9 @@ fn rename_if_reserved(name: &str) -> String { ]; if reserved_keywords.contains(&name) { - format!("{}_", name) // Append a suffix to avoid conflicts + format!("{}_", name.to_snake_case()) // Append a suffix to avoid conflicts } else { - name.to_string() + name.to_string().to_snake_case() } } diff --git a/generator/src/flat_ast.rs b/generator/src/flat_ast.rs index 34ea636..72f2c97 100644 --- a/generator/src/flat_ast.rs +++ b/generator/src/flat_ast.rs @@ -155,7 +155,7 @@ impl Packet { (name.clone(), name.clone().to_snake_case()) } else { - let name = "srv".to_string() + &name[5..]; + let name = "Srv".to_string() + &name[5..]; (name.clone(), name.clone().to_snake_case()) } From c04ce0b5fe52ca955e703cea10a07dd82d6d6d17 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Thu, 28 Nov 2024 00:43:07 -0500 Subject: [PATCH 12/27] - add: missing derive directives --- generator/src/codegen/rust/codegen_source.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index eb8ec4a..1147a47 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -252,6 +252,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { if is_enum { cg!(self, r#"#[repr({})]"#, rust_type); + cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); cg!(self, "enum {} {{", name.to_upper_camel_case()); self.indent(); for content in restrict.contents() { @@ -261,6 +262,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } } } else { + cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); cg!(self, "struct {} {{", name.to_upper_camel_case()); self.indent(); cg!(self, "{}: {},", name.to_string().to_snake_case(), rust_type); From 3e396506c1f1e5c076d027554e3a98a7a3de5345 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Thu, 28 Nov 2024 10:17:47 -0500 Subject: [PATCH 13/27] - fix: array elements that use a const not being referred to as an usize - remove: C++ include directives in generated rust --- generator/src/codegen/rust/codegen_source.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 1147a47..e878f3a 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -35,16 +35,6 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, r#"use bincode::{{Encode, Decode}};"#); cg!(self, r#"use crate::packet::PacketPayload;"#); - for content in packet.contents() { - use self::PacketContent::*; - match content { - Include(ref inc, system) => { - cg!(self, r#"use {};"#, inc); - }, - _ => {} - }; - } - cg!(self); let iserialize = packet.contents().iter().filter_map(|elem| { match elem { @@ -223,7 +213,13 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { use ::flat_ast::Occurs::*; let type_ = match o { Unbounded => format!("Vec<{}>", rust_type), - Num(n) => format!("[{}; {}]", rust_type, n) + Num(n) => { + if n.parse::().is_ok() { + format!("[{}; {}]", rust_type, n) + } else { + format!("[{}; ({} as usize)]", rust_type, n) + } + } }; (type_, "".to_string()) } else { From 1095eea55722c7a13d8a63986cd4b49bf8cc4887 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:14:56 -0500 Subject: [PATCH 14/27] - fix: includeXml directives were being pruned incorrectly --- generator/src/flat_ast.rs | 9 +++++---- generator/src/graph_passes.rs | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/generator/src/flat_ast.rs b/generator/src/flat_ast.rs index 72f2c97..afd229a 100644 --- a/generator/src/flat_ast.rs +++ b/generator/src/flat_ast.rs @@ -31,6 +31,7 @@ pub enum ComplexTypeContent { Empty } +use heck::ToUpperCamelCase; pub use ::schema::ast::Occurs; #[derive(Debug, Clone)] @@ -145,12 +146,12 @@ impl Packet { pub fn new(type_: String , doc: Option) -> Self { use ::heck::*; - let name = type_.clone().to_lower_camel_case(); - let (class_name, filename) = if name.starts_with("isc") { + let name = type_.clone().to_upper_camel_case(); + let (class_name, filename) = if name.starts_with("Isc") { (name.clone(), name.clone().to_snake_case()) } else { - if name.starts_with("pakcs") { + if name.starts_with("Pakcs") { let name = "Cli".to_string() + &name[5..]; (name.clone(), name.clone().to_snake_case()) @@ -439,7 +440,7 @@ impl Bitset { impl SimpleType { pub fn new(name: String, doc: Option) -> Self { use heck::ToLowerCamelCase; - SimpleType{ name: name.to_lower_camel_case(), contents: Vec::new(), doc } + SimpleType{ name: name.to_upper_camel_case(), contents: Vec::new(), doc } } pub fn add_content(&mut self, content: SimpleTypeContent) { diff --git a/generator/src/graph_passes.rs b/generator/src/graph_passes.rs index 4289b90..4c99d4a 100644 --- a/generator/src/graph_passes.rs +++ b/generator/src/graph_passes.rs @@ -1,6 +1,6 @@ use ::flat_ast::*; use std::collections::{BTreeMap, HashSet}; -use heck::ToLowerCamelCase; +use heck::{ToLowerCamelCase, ToUpperCamelCase}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] struct NodeId(usize); @@ -250,7 +250,7 @@ pub fn run(mut packet: Packet) -> Result { for content in s.contents() { match content { Restriction(ref r) => { - if let Ok(node) = graph.get_node(&r.base().to_owned().to_lower_camel_case()) { + if let Ok(node) = graph.get_node(&r.base().to_owned().to_upper_camel_case()) { let to = graph.get_node(s.name()).unwrap(); graph.add_edge(to, node); } @@ -260,7 +260,7 @@ pub fn run(mut packet: Packet) -> Result { }, PacketContent::Element(ref e) => { trace!("adding start node {}", e.type_()); - graph.add_start_node(&e.type_().to_owned().to_lower_camel_case()); + graph.add_start_node(&e.type_().to_owned().to_upper_camel_case()); match e.occurs() { Some(self::Occurs::Unbounded) => vector = true, Some(self::Occurs::Num(_)) => array = true, From 1163f0960ae21768689a14143407bec2777a449d Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:15:47 -0500 Subject: [PATCH 15/27] - fix: hashmap not actually mapping datatypes correctly since it filters out anything that isn't used by the main packet --- generator/src/codegen/rust/codegen_source.rs | 127 +++++++++---------- 1 file changed, 57 insertions(+), 70 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index e878f3a..915f59b 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -35,60 +35,22 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, r#"use bincode::{{Encode, Decode}};"#); cg!(self, r#"use crate::packet::PacketPayload;"#); + let mut iserialize: HashMap = HashMap::new(); + iserialize.insert("int8_t".to_string(), "i8".to_string()); + iserialize.insert("uint8_t".to_string(), "u8".to_string()); + iserialize.insert("int16_t".to_string(), "i16".to_string()); + iserialize.insert("uint16_t".to_string(), "u16".to_string()); + iserialize.insert("int32_t".to_string(), "i32".to_string()); + iserialize.insert("uint32_t".to_string(), "u32".to_string()); + iserialize.insert("int64_t".to_string(), "i64".to_string()); + iserialize.insert("uint64_t".to_string(), "u64".to_string()); + iserialize.insert("char".to_string(), "u8".to_string()); + iserialize.insert("int".to_string(), "i32".to_string()); + iserialize.insert("unsigned int".to_string(), "u32".to_string()); + iserialize.insert("float".to_string(), "f32".to_string()); + iserialize.insert("double".to_string(), "f64".to_string()); + iserialize.insert("std::string".to_string(), "String".to_string()); - let iserialize = packet.contents().iter().filter_map(|elem| { - match elem { - PacketContent::Element(ref e) => { - let rust_type = match e.type_().as_ref() { - "int8_t" => "i8", - "uint8_t" => "u8", - "int16_t" => "i16", - "uint16_t" => "u16", - "int32_t" => "i32", - "uint32_t" => "u32", - "int64_t" => "i64", - "uint64_t" => "u64", - "char" => "u8", - "float" => "f32", - "double" => "f64", - "std::string" => "String", - _ => e.type_().as_str(), - }; - Some((e.type_().to_string(), rust_type.to_string())) // Map key and value - } - PacketContent::Simple(ref e) => { - let mut rust_type = ("".to_string(), "".to_string()); - for content in e.contents() { - rust_type = match content { - SimpleTypeContent::Restriction(res) => { - let rust_type = match res.base().as_ref() { - "int8_t" => "i8", - "uint8_t" => "u8", - "int16_t" => "i16", - "uint16_t" => "u16", - "int32_t" => "i32", - "uint32_t" => "u32", - "int64_t" => "i64", - "uint64_t" => "u64", - "char" => "u8", - "float" => "f32", - "double" => "f64", - "std::string" => "String", - _ => res.base().as_str(), - }; - (res.base().clone(), rust_type.to_string()) - }, - }; - } - Some(rust_type) - } - PacketContent::Complex(ref e) => { - let rust_type = (e.name().clone(), e.name().clone()); - Some(rust_type) - } - _ => None, - } - }).collect::>(); // Collect into a HashMap for content in packet.contents() { use self::PacketContent::*; @@ -148,18 +110,10 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { fn complex_type(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { use ::flat_ast::ComplexTypeContent::*; if complex.inline() == false { - cg!(self, "struct {} {{", complex.name()); - self.indent(); + + // All unions need to be outside the struct match complex.content() { - Seq(ref s) => { - for elem in s.elements() { - self.element(elem, &iserialize)?; - } - }, Choice(ref c) => { - cg!(self, "#[repr(C)]"); - cg!(self, "union Data {{"); - self.indent(); for elem in c.elements() { if let Some(ref seq) = c.inline_seqs().get(elem.name()) { cg!(self, "struct {} {{", elem.name()); @@ -168,19 +122,45 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { self.element(e, &iserialize)?; } self.dedent(); - cg!(self, "}});"); + cg!(self, "}}"); + cg!(self); + } + } + + cg!(self, "#[repr(C)]"); + cg!(self, r#"#[derive(Debug)]"#); + cg!(self, "union {}InternalData {{", complex.name()); + self.indent(); + for elem in c.elements() { + if let Some(ref seq) = c.inline_seqs().get(elem.name()) { + cg!(self, "{}: {},", elem.name().to_snake_case(), elem.name()); } else { self.element(elem, &iserialize)?; } } self.dedent(); cg!(self, "}}"); - self.dedent(); + }, + _ => {} + } + + cg!(self); + cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); + cg!(self, "struct {} {{", complex.name()); + self.indent(); + match complex.content() { + Seq(ref s) => { + for elem in s.elements() { + self.element(elem, &iserialize)?; + } + }, + Choice(ref c) => { + cg!(self, "data: {}InternalData,", complex.name()); }, Empty => {} } self.dedent(); - cg!(self, "}};"); + cg!(self, "}}"); cg!(self); } Ok(()) @@ -207,7 +187,11 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { return Ok(()); } - let Some(rust_type) = iserialize.get(elem.type_().trim()) else { warn!(r#"Type "{}" not found"#, elem.type_()); return Ok(()) }; + let trimmed_type = elem.type_().trim().to_string(); + let rust_type = iserialize.get(elem.type_().trim()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + &trimmed_type + }); let (type_, bits) = if let Some(ref o) = elem.occurs() { use ::flat_ast::Occurs::*; @@ -224,7 +208,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { (type_, "".to_string()) } else { let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!(" : {}", b)); - (rust_type.to_owned(), bits) + (rust_type.to_owned().to_string(), bits) }; // let default = match elem.init() { // self::ElementInitValue::Default(d) => " = ".to_string() + d, @@ -243,8 +227,11 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { _ => false }).is_some(); self.doc(restrict.doc())?; - let base = restrict.base().trim(); - let Some(rust_type) = iserialize.get(base) else { warn!(r#"Type "{}" not found"#, base); return Ok(()) }; + let base = restrict.base().trim().to_string(); + let rust_type = iserialize.get(restrict.base().trim()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, base); + &base + }); if is_enum { cg!(self, r#"#[repr({})]"#, rust_type); From 5e252df7c845e3ab6f997e589255ca933ce0fe11 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:16:19 -0500 Subject: [PATCH 16/27] - remove: unused import --- generator/src/flat_ast.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/generator/src/flat_ast.rs b/generator/src/flat_ast.rs index afd229a..e547671 100644 --- a/generator/src/flat_ast.rs +++ b/generator/src/flat_ast.rs @@ -439,7 +439,6 @@ impl Bitset { impl SimpleType { pub fn new(name: String, doc: Option) -> Self { - use heck::ToLowerCamelCase; SimpleType{ name: name.to_upper_camel_case(), contents: Vec::new(), doc } } From c99c4c2a4da49db0b758d214a1d22110bc1c39d8 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:07:40 -0500 Subject: [PATCH 17/27] - add: custom encoder and decoder for main struct and restricted types --- generator/src/codegen/rust/codegen_source.rs | 204 ++++++++++++++++++- 1 file changed, 195 insertions(+), 9 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 915f59b..70118ed 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -2,6 +2,7 @@ use ::flat_ast::*; use std::io::{Result, Write}; use ::heck::*; use std::collections::HashMap; +use flat_ast::RestrictionContent::{Enumeration, Length, MaxValue, MinValue}; pub (crate) struct CodeSourceGenerator<'a, W: Write + 'a> { writer: &'a mut ::writer::Writer, @@ -32,7 +33,9 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { pub fn generate(&mut self, packet: &Packet) -> Result<()> { let version = self.version.clone(); cg!(self, "/* Generated with IDL v{} */\n", version); - cg!(self, r#"use bincode::{{Encode, Decode}};"#); + cg!(self, r#"use bincode::{{Encode, Decode, enc::Encoder, de::Decoder, error::DecodeError}};"#); + cg!(self, r#"use bincode::de::read::Reader;"#); + cg!(self, r#"use bincode::enc::write::Writer;"#); cg!(self, r#"use crate::packet::PacketPayload;"#); let mut iserialize: HashMap = HashMap::new(); @@ -70,7 +73,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self); - cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); + cg!(self, r#"#[derive(Debug)]"#); cg!(self, "pub struct {} {{", packet.class_name().to_upper_camel_case()); self.indent(); for content in packet.contents() { @@ -85,6 +88,62 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self); cg!(self, "impl PacketPayload for {} {{}}", packet.class_name().to_upper_camel_case()); + cg!(self); + cg!(self, "impl Encode for {} {{", packet.class_name().to_upper_camel_case()); + self.indent(); + cg!(self, "fn encode(&self, encoder: &mut E) -> std::result::Result<(), bincode::error::EncodeError> {{"); + self.indent(); + for content in packet.contents() { + use self::PacketContent::*; + match content { + Element(ref elem) => { + let name = rename_if_reserved(elem.name()); + cg!(self, "self.{}.encode(encoder)?;", name); + }, + _ => {} + }; + } + cg!(self, "Ok(())"); + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); + + cg!(self); + cg!(self, "impl Decode for {} {{", packet.class_name().to_upper_camel_case()); + self.indent(); + cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); + self.indent(); + for content in packet.contents() { + use self::PacketContent::*; + match content { + Element(ref elem) => { + let name = rename_if_reserved(elem.name()); + let trimmed_type = elem.type_().trim().to_string(); + let rust_type = iserialize.get(elem.type_().trim()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + &trimmed_type + }); + cg!(self, "let {} = {}::decode(decoder)?;", name, rust_type); + }, + _ => {} + }; + } + cg!(self, "Ok({} {{", packet.class_name().to_upper_camel_case()); + for content in packet.contents() { + use self::PacketContent::*; + match content { + Element(ref elem) => { + cg!(self, "{},", rename_if_reserved(elem.name())); + }, + _ => {} + }; + } + cg!(self, "}})"); + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); Ok(()) @@ -216,7 +275,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { // }; let name = rename_if_reserved(elem.name()); // cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); - cg!(self, "{}: {}{},", name, type_, bits); + cg!(self, "pub(crate) {}: {}{},", name, type_, bits); Ok(()) } @@ -235,8 +294,8 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { if is_enum { cg!(self, r#"#[repr({})]"#, rust_type); - cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); - cg!(self, "enum {} {{", name.to_upper_camel_case()); + cg!(self, r#"#[derive(Debug, Clone)]"#); + cg!(self, "pub(crate) enum {} {{", name.to_upper_camel_case()); self.indent(); for content in restrict.contents() { if let Enumeration(en) = content { @@ -245,14 +304,142 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } } } else { - cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); - cg!(self, "struct {} {{", name.to_upper_camel_case()); + cg!(self, r#"#[derive(Debug)]"#); + cg!(self, "pub struct {} {{", name.to_upper_camel_case()); self.indent(); - cg!(self, "{}: {},", name.to_string().to_snake_case(), rust_type); + cg!(self, "pub(crate) {}: {},", name.to_string().to_snake_case(), rust_type); } self.dedent(); cg!(self, "}}"); + + cg!(self); + self.restrict_encode(&restrict, name, iserialize)?; + cg!(self); + self.restrict_decode(&restrict, name, iserialize)?; + Ok(()) + } + + fn restrict_encode(&mut self, restrict: &Restriction, name: &str, iserialize: &HashMap) -> Result<()> { + let is_enum = restrict.contents().iter().find(|content| match content { + Enumeration(_) => true, + _ => false + }).is_some(); + let trimmed_type = restrict.base().trim().to_string(); + let rust_type = iserialize.get(restrict.base().trim()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, restrict.base()); + &trimmed_type + }); + + cg!(self, "impl Encode for {} {{", name.to_upper_camel_case()); + self.indent(); + cg!(self, "fn encode(&self, encoder: &mut E) -> std::result::Result<(), bincode::error::EncodeError> {{"); + self.indent(); + if is_enum { + cg!(self, "encoder.writer().write(&[self.clone() as u8]).map_err(Into::into)"); + } else { + let data = name.to_string().to_snake_case(); + cg!(self, "let bytes = self.{}.as_bytes();", data); + for content in restrict.contents() { + match content { + Length(l) => { + cg!(self, "let fixed_length = {};", l); + cg!(self, "if bytes.len() > fixed_length {{"); + self.indent(); + cg!(self, "return Err(bincode::error::EncodeError::OtherString(format!("); + cg!(self, "\"{} length exceeds fixed size: {{}} > {{}}\", bytes.len(), fixed_length)));", data); + self.dedent(); + cg!(self, "}}"); + cg!(self, "encoder.writer().write(bytes)?;"); + cg!(self, "encoder.writer().write(&vec![0; fixed_length - bytes.len()])?;"); + cg!(self, "Ok(())"); + }, + MinValue(v) => { + + }, + MaxValue(v) => { + + }, + _ => panic!("enumeration in restrict when there shouldn't be one") + } + } + } + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); + + Ok(()) + } + + fn restrict_decode(&mut self, restrict: &Restriction, name: &str, iserialize: &HashMap) -> Result<()> { + let is_enum = restrict.contents().iter().find(|content| match content { + Enumeration(_) => true, + _ => false + }).is_some(); + let trimmed_type = restrict.base().trim().to_string(); + let rust_type = iserialize.get(restrict.base().trim()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, restrict.base()); + &trimmed_type + }); + + cg!(self, "impl Decode for {} {{", name.to_upper_camel_case()); + self.indent(); + cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); + self.indent(); + if is_enum { + cg!(self, "let value = {}::decode(decoder)?;", rust_type); + cg!(self, "match value {{"); + self.indent(); + for content in restrict.contents() { + if let Enumeration(en) = content { + cg!(self, "{} => Ok({}::{}),", en.id(), name.to_upper_camel_case(), en.value().to_upper_camel_case()); + } + } + cg!(self, "_ => Err(bincode::error::DecodeError::OtherString(format!(\"Invalid value for {}: {{}}\", value))),", name.to_upper_camel_case()); + self.dedent(); + cg!(self, "}}"); + } else { + let data = name.to_string().to_snake_case(); + let mut fixed_length = 64; + let mut minValueCheck = String::new(); + let mut maxValueCheck = String::new(); + for content in restrict.contents() { + match content { + Length(l) => { + fixed_length = *l; + }, + MinValue(v) => { + minValueCheck = format!("if {} < {} {{Err(bincode::error::DecodeError::OtherString(format!(\"Invalid value for {}: {{}} < {{}}\", {}, {})))}}", data, v, data, data, v).into(); + }, + MaxValue(v) => { + maxValueCheck = format!("if {} > {} {{Err(bincode::error::DecodeError::OtherString(format!(\"Invalid value for {}: {{}} > {{}}\", {}, {})))}}", data, v, data, data, v).into(); + }, + _ => panic!("enumeration in restrict when there shouldn't be one") + } + } + + if rust_type == "String" { + cg!(self, "let mut buffer = vec![0u8; {}];", fixed_length); + cg!(self, "decoder.reader().read(&mut buffer)?;"); + cg!(self, "let {} = {}::from_utf8(buffer)", data, rust_type); + cg!(self, ".map_err(|e| DecodeError::OtherString(format!(\"Invalid UTF-8: {{}}\", e)))?"); + cg!(self, ".trim_end_matches('\\0')"); + cg!(self, ".to_string();"); + } else { + cg!(self, "let {} = {}::decode(buffer)?;", data, rust_type); + + cg!(self, "{}", minValueCheck); + + cg!(self, "{}", maxValueCheck); + } + cg!(self, "Ok({} {{ {} }})", name.to_upper_camel_case(), data); + } + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); + Ok(()) } } @@ -273,4 +460,3 @@ fn rename_if_reserved(name: &str) -> String { name.to_string().to_snake_case() } } - From c13019e899b1fa7e886b54ec2d0ea42205990e4d Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:49:39 -0500 Subject: [PATCH 18/27] - update: generated rust code formatting --- generator/src/codegen/rust/codegen_source.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 70118ed..c10104d 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -129,7 +129,8 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { _ => {} }; } - cg!(self, "Ok({} {{", packet.class_name().to_upper_camel_case()); + cg!(self, "Ok(Self {{"); + self.indent(); for content in packet.contents() { use self::PacketContent::*; match content { @@ -139,6 +140,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { _ => {} }; } + self.dedent(); cg!(self, "}})"); self.dedent(); cg!(self, "}}"); @@ -433,7 +435,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "{}", maxValueCheck); } - cg!(self, "Ok({} {{ {} }})", name.to_upper_camel_case(), data); + cg!(self, "Ok(Self {{ {} }})", data); } self.dedent(); cg!(self, "}}"); From 31432ecd7ca5fc63c35b5270a6f9ce74ee1a6733 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:55:35 -0500 Subject: [PATCH 19/27] - add: NullTerminatedString as a type for std::string unless it's a length restricted string --- generator/src/codegen/rust/codegen_source.rs | 27 +++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index c10104d..be0e4ef 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -37,6 +37,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, r#"use bincode::de::read::Reader;"#); cg!(self, r#"use bincode::enc::write::Writer;"#); cg!(self, r#"use crate::packet::PacketPayload;"#); + cg!(self, r#"use crate::handlers::null_string::NullTerminatedString;"#); let mut iserialize: HashMap = HashMap::new(); iserialize.insert("int8_t".to_string(), "i8".to_string()); @@ -52,7 +53,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { iserialize.insert("unsigned int".to_string(), "u32".to_string()); iserialize.insert("float".to_string(), "f32".to_string()); iserialize.insert("double".to_string(), "f64".to_string()); - iserialize.insert("std::string".to_string(), "String".to_string()); + iserialize.insert("std::string".to_string(), "NullTerminatedString".to_string()); for content in packet.contents() { @@ -289,11 +290,15 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { }).is_some(); self.doc(restrict.doc())?; let base = restrict.base().trim().to_string(); - let rust_type = iserialize.get(restrict.base().trim()).unwrap_or_else(|| { + let mut rust_type = iserialize.get(restrict.base().trim()).map(|s| s.to_string()).unwrap_or_else(|| { debug!(r#"Type "{}" not found, outputting anyway"#, base); - &base + base.clone() }); + if "NullTerminatedString" == rust_type { + rust_type = "String".to_string(); + } + if is_enum { cg!(self, r#"#[repr({})]"#, rust_type); cg!(self, r#"#[derive(Debug, Clone)]"#); @@ -328,11 +333,15 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { _ => false }).is_some(); let trimmed_type = restrict.base().trim().to_string(); - let rust_type = iserialize.get(restrict.base().trim()).unwrap_or_else(|| { + let mut rust_type = iserialize.get(restrict.base().trim()).map(|s| s.to_string()).unwrap_or_else(|| { debug!(r#"Type "{}" not found, outputting anyway"#, restrict.base()); - &trimmed_type + trimmed_type.clone() }); + if "NullTerminatedString" == rust_type { + rust_type = "String".to_string(); + } + cg!(self, "impl Encode for {} {{", name.to_upper_camel_case()); self.indent(); cg!(self, "fn encode(&self, encoder: &mut E) -> std::result::Result<(), bincode::error::EncodeError> {{"); @@ -380,11 +389,15 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { _ => false }).is_some(); let trimmed_type = restrict.base().trim().to_string(); - let rust_type = iserialize.get(restrict.base().trim()).unwrap_or_else(|| { + let mut rust_type = iserialize.get(restrict.base().trim()).map(|s| s.to_string()).unwrap_or_else(|| { debug!(r#"Type "{}" not found, outputting anyway"#, restrict.base()); - &trimmed_type + trimmed_type.clone() }); + if "NullTerminatedString" == rust_type { + rust_type = "String".to_string(); + } + cg!(self, "impl Decode for {} {{", name.to_upper_camel_case()); self.indent(); cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); From d384b3cc99e89a1e3462f69a6264d43213daae09 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:17:26 -0500 Subject: [PATCH 20/27] - add: complex type encode and decode methods --- generator/src/codegen/rust/codegen_source.rs | 99 +++++++++++++++++++- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index be0e4ef..eddb700 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -125,7 +125,25 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); &trimmed_type }); - cg!(self, "let {} = {}::decode(decoder)?;", name, rust_type); + let (type_, bits) = if let Some(ref o) = elem.occurs() { + use ::flat_ast::Occurs::*; + let type_ = match o { + Unbounded => format!("Vec"), + Num(n) => { + // TODO: Check to see if this is actually needed? + if n.parse::().is_ok() { + format!("[{}; {}]", rust_type, n) + } else { + format!("[{}; ({} as usize)]", rust_type, n) + } + } + }; + (type_, "".to_string()) + } else { + let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!(" : {}", b)); + (rust_type.to_owned().to_string(), bits) + }; + cg!(self, "let {} = {}::decode(decoder)?;", name, type_); }, _ => {} }; @@ -172,7 +190,6 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { fn complex_type(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { use ::flat_ast::ComplexTypeContent::*; if complex.inline() == false { - // All unions need to be outside the struct match complex.content() { Choice(ref c) => { @@ -207,8 +224,8 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } cg!(self); - cg!(self, r#"#[derive(Debug, Encode, Decode)]"#); - cg!(self, "struct {} {{", complex.name()); + cg!(self, r#"#[derive(Debug)]"#); + cg!(self, "pub struct {} {{", complex.name()); self.indent(); match complex.content() { Seq(ref s) => { @@ -224,10 +241,84 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { self.dedent(); cg!(self, "}}"); cg!(self); + self.complex_encode(complex, iserialize); + cg!(self); + self.complex_decode(complex, iserialize); + } + Ok(()) + } + + fn complex_encode(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { + use ::flat_ast::ComplexTypeContent::*; + cg!(self, "impl Encode for {} {{", complex.name()); + self.indent(); + cg!(self, "fn encode(&self, encoder: &mut E) -> std::result::Result<(), bincode::error::EncodeError> {{"); + self.indent(); + + match complex.content() { + Seq(ref s) => { + for elem in s.elements() { + let data = elem.name().to_string().to_snake_case(); + cg!(self, "self.{}.encode(encoder)?;", data); + } + }, + Choice(ref c) => { + // TODO: Figure out how to make this work + }, + Empty => {} + } + cg!(self, "Ok(())"); + + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); + Ok(()) + } + + fn complex_decode(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { + use ::flat_ast::ComplexTypeContent::*; + cg!(self, "impl Decode for {} {{", complex.name().to_upper_camel_case()); + self.indent(); + cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); + self.indent(); + + let mut output_list = Vec::new(); + match complex.content() { + Seq(ref s) => { + for elem in s.elements() { + let name = elem.name().to_string().to_snake_case(); + let trimmed_type = elem.type_().trim().to_string(); + let rust_type = iserialize.get(elem.type_().trim()).map(|s| s.to_string()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + trimmed_type.clone() + }); + + cg!(self, "let {} = {}::decode(decoder)?;", name, rust_type); + output_list.push(name); + } + }, + Choice(ref c) => { + // TODO: Figure out how to make this work + }, + Empty => {} + } + cg!(self, "Ok(Self {{"); + self.indent(); + for name in &output_list { + cg!(self, "{},", name); } + self.dedent(); + cg!(self, "}})"); + + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); Ok(()) } + fn simple_type(&mut self, simple: &SimpleType, iserialize: &HashMap) -> Result<()> { cg!(self); self.doc(simple.doc())?; From 6ca7c5e1e2ff2fa8c1e140f3559b65ebe9c0e8fe Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:20:28 -0500 Subject: [PATCH 21/27] - fix: some warnings about unused variables --- generator/src/codegen/rust/codegen_source.rs | 52 ++++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index eddb700..efd8b5d 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -125,7 +125,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); &trimmed_type }); - let (type_, bits) = if let Some(ref o) = elem.occurs() { + let (type_, _bits) = if let Some(ref o) = elem.occurs() { use ::flat_ast::Occurs::*; let type_ = match o { Unbounded => format!("Vec"), @@ -211,7 +211,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "union {}InternalData {{", complex.name()); self.indent(); for elem in c.elements() { - if let Some(ref seq) = c.inline_seqs().get(elem.name()) { + if let Some(ref _seq) = c.inline_seqs().get(elem.name()) { cg!(self, "{}: {},", elem.name().to_snake_case(), elem.name()); } else { self.element(elem, &iserialize)?; @@ -233,7 +233,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { self.element(elem, &iserialize)?; } }, - Choice(ref c) => { + Choice(ref _c) => { cg!(self, "data: {}InternalData,", complex.name()); }, Empty => {} @@ -241,14 +241,14 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { self.dedent(); cg!(self, "}}"); cg!(self); - self.complex_encode(complex, iserialize); + let _ = self.complex_encode(complex, iserialize); cg!(self); - self.complex_decode(complex, iserialize); + let _ = self.complex_decode(complex, iserialize); } Ok(()) } - fn complex_encode(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { + fn complex_encode(&mut self, complex: &ComplexType, _iserialize: &HashMap) -> Result<()> { use ::flat_ast::ComplexTypeContent::*; cg!(self, "impl Encode for {} {{", complex.name()); self.indent(); @@ -262,7 +262,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "self.{}.encode(encoder)?;", data); } }, - Choice(ref c) => { + Choice(ref _c) => { // TODO: Figure out how to make this work }, Empty => {} @@ -298,7 +298,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { output_list.push(name); } }, - Choice(ref c) => { + Choice(ref _c) => { // TODO: Figure out how to make this work }, Empty => {} @@ -418,20 +418,20 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Ok(()) } - fn restrict_encode(&mut self, restrict: &Restriction, name: &str, iserialize: &HashMap) -> Result<()> { + fn restrict_encode(&mut self, restrict: &Restriction, name: &str, _iserialize: &HashMap) -> Result<()> { let is_enum = restrict.contents().iter().find(|content| match content { Enumeration(_) => true, _ => false }).is_some(); - let trimmed_type = restrict.base().trim().to_string(); - let mut rust_type = iserialize.get(restrict.base().trim()).map(|s| s.to_string()).unwrap_or_else(|| { - debug!(r#"Type "{}" not found, outputting anyway"#, restrict.base()); - trimmed_type.clone() - }); - - if "NullTerminatedString" == rust_type { - rust_type = "String".to_string(); - } + // let trimmed_type = restrict.base().trim().to_string(); + // let mut rust_type = iserialize.get(restrict.base().trim()).map(|s| s.to_string()).unwrap_or_else(|| { + // debug!(r#"Type "{}" not found, outputting anyway"#, restrict.base()); + // trimmed_type.clone() + // }); + // + // if "NullTerminatedString" == rust_type { + // rust_type = "String".to_string(); + // } cg!(self, "impl Encode for {} {{", name.to_upper_camel_case()); self.indent(); @@ -456,10 +456,10 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "encoder.writer().write(&vec![0; fixed_length - bytes.len()])?;"); cg!(self, "Ok(())"); }, - MinValue(v) => { + MinValue(_v) => { }, - MaxValue(v) => { + MaxValue(_v) => { }, _ => panic!("enumeration in restrict when there shouldn't be one") @@ -508,18 +508,18 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } else { let data = name.to_string().to_snake_case(); let mut fixed_length = 64; - let mut minValueCheck = String::new(); - let mut maxValueCheck = String::new(); + let mut min_value_check = String::new(); + let mut max_value_check = String::new(); for content in restrict.contents() { match content { Length(l) => { fixed_length = *l; }, MinValue(v) => { - minValueCheck = format!("if {} < {} {{Err(bincode::error::DecodeError::OtherString(format!(\"Invalid value for {}: {{}} < {{}}\", {}, {})))}}", data, v, data, data, v).into(); + min_value_check = format!("if {} < {} {{Err(bincode::error::DecodeError::OtherString(format!(\"Invalid value for {}: {{}} < {{}}\", {}, {})))}}", data, v, data, data, v).into(); }, MaxValue(v) => { - maxValueCheck = format!("if {} > {} {{Err(bincode::error::DecodeError::OtherString(format!(\"Invalid value for {}: {{}} > {{}}\", {}, {})))}}", data, v, data, data, v).into(); + max_value_check = format!("if {} > {} {{Err(bincode::error::DecodeError::OtherString(format!(\"Invalid value for {}: {{}} > {{}}\", {}, {})))}}", data, v, data, data, v).into(); }, _ => panic!("enumeration in restrict when there shouldn't be one") } @@ -535,9 +535,9 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } else { cg!(self, "let {} = {}::decode(buffer)?;", data, rust_type); - cg!(self, "{}", minValueCheck); + cg!(self, "{}", min_value_check); - cg!(self, "{}", maxValueCheck); + cg!(self, "{}", max_value_check); } cg!(self, "Ok(Self {{ {} }})", data); } From ddc2975d82eabbaa5c5d189d6f5e08afe5e915b5 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Tue, 10 Dec 2024 21:03:15 -0500 Subject: [PATCH 22/27] - add: support for client unions in rust without using unions directly - update: complex type generation now creates encode and decode trait functions correctly - update: elements that are not native rust types which are in arrays are now defined, encoded and decoded correctly --- generator/src/codegen/rust/codegen_source.rs | 298 +++++++++++++++---- 1 file changed, 240 insertions(+), 58 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index efd8b5d..b8dd66d 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -36,6 +36,9 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, r#"use bincode::{{Encode, Decode, enc::Encoder, de::Decoder, error::DecodeError}};"#); cg!(self, r#"use bincode::de::read::Reader;"#); cg!(self, r#"use bincode::enc::write::Writer;"#); + cg!(self, r#"use crate::enums::*;"#); + cg!(self, r#"use crate::types::*;"#); + cg!(self, r#"use crate::dataconsts::*;"#); cg!(self, r#"use crate::packet::PacketPayload;"#); cg!(self, r#"use crate::handlers::null_string::NullTerminatedString;"#); @@ -99,7 +102,31 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { match content { Element(ref elem) => { let name = rename_if_reserved(elem.name()); - cg!(self, "self.{}.encode(encoder)?;", name); + let trimmed_type = elem.type_().trim().to_string(); + let mut is_rust_native = true; + let rust_type = iserialize.get(elem.type_().trim()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + is_rust_native = false; + &trimmed_type + }); + + if let Some(ref o) = elem.occurs() { + use ::flat_ast::Occurs::*; + match o { + Unbounded => { + cg!(self, "self.{}.encode(encoder)?;", name); + } + Num(n) => { + cg!(self, "for value in &self.{} {{", name); + self.indent(); + cg!(self, "value.encode(encoder)?;"); + self.dedent(); + cg!(self, "}}"); + } + }; + } else { + cg!(self, "self.{}.encode(encoder)?;", name); + } }, _ => {} }; @@ -121,46 +148,64 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Element(ref elem) => { let name = rename_if_reserved(elem.name()); let trimmed_type = elem.type_().trim().to_string(); + let mut is_rust_native = true; let rust_type = iserialize.get(elem.type_().trim()).unwrap_or_else(|| { debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + is_rust_native = false; &trimmed_type }); - let (type_, _bits) = if let Some(ref o) = elem.occurs() { + if let Some(ref o) = elem.occurs() { use ::flat_ast::Occurs::*; - let type_ = match o { - Unbounded => format!("Vec"), + match o { + Unbounded => { + cg!(self, "let {} = Vec::decode(decoder)?;", name); + } Num(n) => { - // TODO: Check to see if this is actually needed? - if n.parse::().is_ok() { - format!("[{}; {}]", rust_type, n) + let mut type_prefix = "0"; + if "String" == rust_type { + type_prefix = ""; + } + + if false == is_rust_native { + cg!(self, "let mut {} = Vec::with_capacity({} as usize);", name, n); + cg!(self, "for _ in 0..{} as usize {{", n); + self.indent(); + cg!(self, "{}.push({}::decode(decoder)?);", name, rust_type); + self.dedent(); + cg!(self, "}}"); } else { - format!("[{}; ({} as usize)]", rust_type, n) + if n.parse::().is_ok() { + cg!(self, "let mut {} = [{}{}; {}];", name, type_prefix, rust_type, n); + } else { + cg!(self, "let mut {} = [{}{}; ({} as usize)];", name, type_prefix, rust_type, n); + } + cg!(self, "for value in &mut {} {{", name); + self.indent(); + cg!(self, "*value = {}::decode(decoder)?;", rust_type); + self.dedent(); + cg!(self, "}}"); } } }; - (type_, "".to_string()) } else { - let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!(" : {}", b)); - (rust_type.to_owned().to_string(), bits) + cg!(self, "let {} = {}::decode(decoder)?;", name, rust_type.to_owned().to_string()); }; - cg!(self, "let {} = {}::decode(decoder)?;", name, type_); }, _ => {} }; } - cg!(self, "Ok(Self {{"); - self.indent(); + let mut output_list = Vec::new(); for content in packet.contents() { use self::PacketContent::*; match content { Element(ref elem) => { - cg!(self, "{},", rename_if_reserved(elem.name())); + output_list.push(rename_if_reserved(elem.name())); }, _ => {} }; } - self.dedent(); - cg!(self, "}})"); + + cg!(self, "Ok(Self {{ {} }})", output_list.join(", ")); self.dedent(); cg!(self, "}}"); self.dedent(); @@ -187,6 +232,26 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Ok(()) } + fn get_bitfield_type(bit_count: usize) -> &'static str { + match bit_count { + 1..=8 => "u8", + 9..=16 => "u16", + 17..=32 => "u32", + 33..=64 => "u64", + _ => panic!("Unsupported bit count: {}", bit_count), + } + } + + fn get_size_of_type(type_name: &str) -> usize { + match type_name { + "u8" => std::mem::size_of::(), + "u16" => std::mem::size_of::(), + "u32" => std::mem::size_of::(), + "u64" => std::mem::size_of::(), + _ => panic!("Unsupported type: {}", type_name), + } + } + fn complex_type(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { use ::flat_ast::ComplexTypeContent::*; if complex.inline() == false { @@ -195,6 +260,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Choice(ref c) => { for elem in c.elements() { if let Some(ref seq) = c.inline_seqs().get(elem.name()) { + cg!(self, r#"#[derive(Debug)]"#); cg!(self, "struct {} {{", elem.name()); self.indent(); for e in seq.elements() { @@ -202,23 +268,92 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } self.dedent(); cg!(self, "}}"); + + // Get the max size of the union + let mut max_bit_size = 0; + + for elem2 in c.elements() { + if let Some(ref seq2) = c.inline_seqs().get(elem2.name()) { + // Do nothing + } else { + let trimmed_type = elem2.type_().trim().to_string(); + let rust_type = iserialize.get(elem2.type_().trim()).map(|s| s.to_string()).unwrap_or_else(|| { + warn!(r#"Type "{}" not found, outputting anyway"#, elem2.type_()); + trimmed_type.clone() + }); + max_bit_size = Self::get_size_of_type(&rust_type) as u32 * 8; + } + } + let rust_type = Self::get_bitfield_type(max_bit_size as usize); + + cg!(self); + cg!(self, "impl {} {{", elem.name()); + self.indent(); + cg!(self, "fn encode_bitfield(value: {}, size: {}, offset: &mut {}) -> {} {{", rust_type, rust_type, rust_type, rust_type); + self.indent(); + cg!(self, "let encoded = (value & ((1 << size) - 1)) << *offset;"); + cg!(self, "*offset += size; // Update offset for the next field"); + cg!(self, "encoded"); + self.dedent(); + cg!(self, "}}"); + cg!(self); + cg!(self, "fn decode_bitfield(encoded: {}, size: {}, offset: &mut {}) -> {} {{", rust_type, rust_type, rust_type, rust_type); + self.indent(); + cg!(self, "let value = (encoded >> *offset) & ((1 << size) - 1);"); + cg!(self, "*offset += size; // Update offset for the next field"); + cg!(self, "value"); + self.dedent(); + cg!(self, "}}"); + cg!(self); + cg!(self, "pub fn encode_data(&self) -> {} {{", rust_type); + self.indent(); + cg!(self, "let mut offset = 0;"); + let mut variable_names = Vec::new(); + for e in seq.elements() { + let name = rename_if_reserved(e.name()); + let bits = e.bits().map_or_else(|| "".to_string(), |b| format!("{}", b)); + cg!(self, "let {}_bits = Self::encode_bitfield(self.{}, {}, &mut offset);", e.name().to_snake_case(), name, bits); + variable_names.push(format!("{}_bits", e.name().to_snake_case())); + } + cg!(self, "{}", variable_names.join(" | ")); + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); cg!(self); - } - } - cg!(self, "#[repr(C)]"); - cg!(self, r#"#[derive(Debug)]"#); - cg!(self, "union {}InternalData {{", complex.name()); - self.indent(); - for elem in c.elements() { - if let Some(ref _seq) = c.inline_seqs().get(elem.name()) { - cg!(self, "{}: {},", elem.name().to_snake_case(), elem.name()); - } else { - self.element(elem, &iserialize)?; + cg!(self, "impl Encode for {} {{", elem.name()); + self.indent(); + cg!(self, "fn encode(&self, encoder: &mut E) -> std::result::Result<(), bincode::error::EncodeError> {{"); + self.indent(); + cg!(self, "self.encode_data().encode(encoder)?;"); + cg!(self, "Ok(())"); + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); + + cg!(self); + cg!(self, "impl Decode for {} {{", elem.name()); + self.indent(); + cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); + self.indent(); + cg!(self, "let bitfield = {}::decode(decoder)?;", rust_type); + cg!(self, "let mut offset = 0;"); + let mut variable_names = Vec::new(); + for e in seq.elements() { + let name = rename_if_reserved(e.name()); + let bits = e.bits().map_or_else(|| "".to_string(), |b| format!("{}", b)); + cg!(self, "let {} = Self::decode_bitfield(bitfield, {}, &mut offset);", name, bits); + variable_names.push(format!("{}", name)); + } + cg!(self, "Ok(Self {{ {} }})", variable_names.join(", ")); + self.dedent(); + cg!(self, "}}"); + self.dedent(); + cg!(self, "}}"); } } - self.dedent(); - cg!(self, "}}"); }, _ => {} } @@ -233,8 +368,14 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { self.element(elem, &iserialize)?; } }, - Choice(ref _c) => { - cg!(self, "data: {}InternalData,", complex.name()); + Choice(ref c) => { + for elem in c.elements() { + if let Some(ref _seq) = c.inline_seqs().get(elem.name()) { + cg!(self, "{}: {},", elem.name().to_snake_case(), elem.name()); + } else { + self.element(elem, &iserialize)?; + } + } }, Empty => {} } @@ -262,8 +403,11 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "self.{}.encode(encoder)?;", data); } }, - Choice(ref _c) => { - // TODO: Figure out how to make this work + Choice(ref c) => { + for elem in c.elements() { + let data = elem.name().to_string().to_snake_case(); + cg!(self, "self.{}.encode(encoder)?;", data); + } }, Empty => {} } @@ -289,27 +433,68 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { for elem in s.elements() { let name = elem.name().to_string().to_snake_case(); let trimmed_type = elem.type_().trim().to_string(); + let mut is_rust_native = true; let rust_type = iserialize.get(elem.type_().trim()).map(|s| s.to_string()).unwrap_or_else(|| { debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + is_rust_native = false; trimmed_type.clone() }); - cg!(self, "let {} = {}::decode(decoder)?;", name, rust_type); + if let Some(ref o) = elem.occurs() { + use ::flat_ast::Occurs::*; + match o { + Unbounded => { + cg!(self, "let {} = Vec::decode(decoder)?;", name); + } + Num(n) => { + let mut type_prefix = "0"; + if "String" == rust_type { + type_prefix = ""; + } + + if false == is_rust_native { + cg!(self, "let mut {} = Vec::with_capacity({} as usize);", name, n); + cg!(self, "for _ in 0..{} as usize {{", n); + self.indent(); + cg!(self, "{}.push({}::decode(decoder)?);", name, rust_type); + self.dedent(); + cg!(self, "}}"); + } else { + if n.parse::().is_ok() { + cg!(self, "let mut {} = [{}{}; {}];", name, type_prefix, rust_type, n); + } else { + cg!(self, "let mut {} = [{}{}; ({} as usize)];", name, type_prefix, rust_type, n); + } + cg!(self, "for value in &mut {} {{", name); + self.indent(); + cg!(self, "*value = {}::decode(decoder)?;", rust_type); + self.dedent(); + cg!(self, "}}"); + } + } + }; + } else { + cg!(self, "let {} = {}::decode(decoder)?;", name, rust_type); + } output_list.push(name); } }, - Choice(ref _c) => { - // TODO: Figure out how to make this work + Choice(ref c) => { + for elem in c.elements() { + let name = elem.name().to_string().to_snake_case(); + let trimmed_type = elem.type_().trim().to_string(); + let rust_type = iserialize.get(elem.type_().trim()).map(|s| s.to_string()).unwrap_or_else(|| { + debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + trimmed_type.clone() + }); + + cg!(self, "let {} = {}::decode(decoder)?;", name, rust_type); + output_list.push(name); + } }, Empty => {} } - cg!(self, "Ok(Self {{"); - self.indent(); - for name in &output_list { - cg!(self, "{},", name); - } - self.dedent(); - cg!(self, "}})"); + cg!(self, "Ok(Self {{ {} }})", output_list.join(", ")); self.dedent(); cg!(self, "}}"); @@ -341,8 +526,10 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } let trimmed_type = elem.type_().trim().to_string(); + let mut is_rust_native = true; let rust_type = iserialize.get(elem.type_().trim()).unwrap_or_else(|| { debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); + is_rust_native = false; &trimmed_type }); @@ -351,16 +538,20 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { let type_ = match o { Unbounded => format!("Vec<{}>", rust_type), Num(n) => { - if n.parse::().is_ok() { - format!("[{}; {}]", rust_type, n) + if false == is_rust_native { + format!("Vec<{}>", rust_type) } else { - format!("[{}; ({} as usize)]", rust_type, n) + if n.parse::().is_ok() { + format!("[{}; {}]", rust_type, n) + } else { + format!("[{}; ({} as usize)]", rust_type, n) + } } } }; (type_, "".to_string()) } else { - let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!(" : {}", b)); + let bits = elem.bits().map_or_else(|| "".to_string(), |b| format!("// {} bits", b)); (rust_type.to_owned().to_string(), bits) }; // let default = match elem.init() { @@ -369,7 +560,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { // }; let name = rename_if_reserved(elem.name()); // cg!(self, "{}: {}{}{},", elem.name(), type_, bits, default); - cg!(self, "pub(crate) {}: {}{},", name, type_, bits); + cg!(self, "pub(crate) {}: {}, {}", name, type_, bits); Ok(()) } @@ -423,15 +614,6 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Enumeration(_) => true, _ => false }).is_some(); - // let trimmed_type = restrict.base().trim().to_string(); - // let mut rust_type = iserialize.get(restrict.base().trim()).map(|s| s.to_string()).unwrap_or_else(|| { - // debug!(r#"Type "{}" not found, outputting anyway"#, restrict.base()); - // trimmed_type.clone() - // }); - // - // if "NullTerminatedString" == rust_type { - // rust_type = "String".to_string(); - // } cg!(self, "impl Encode for {} {{", name.to_upper_camel_case()); self.indent(); From 953d64eeeeb4355e18a5bd52bfdfaeedfede792e Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:28:52 -0500 Subject: [PATCH 23/27] - update: NullTerminatedString to utils crate - update: README.md --- README.md | 6 +----- generator/src/codegen/rust/codegen_source.rs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0e748e5..101bb9c 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,5 @@ packet generator for osiROSE-new ##### Build status -[![lin-badge]][lin-link] [![win-badge]][win-link] -[lin-badge]: https://travis-ci.com/dev-osrose/IDL.svg?branch=master "Linux build status" -[lin-link]: https://travis-ci.com/dev-osrose/IDL "Linux build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/vok7xs5wr1ajqpbc?svg=true "Windows build status" -[win-link]: https://ci.appveyor.com/project/RavenX8/idl "Windows build status" +Check the actions tab for build status. \ No newline at end of file diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index b8dd66d..dfc70da 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -36,11 +36,11 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, r#"use bincode::{{Encode, Decode, enc::Encoder, de::Decoder, error::DecodeError}};"#); cg!(self, r#"use bincode::de::read::Reader;"#); cg!(self, r#"use bincode::enc::write::Writer;"#); + cg!(self, r#"use utils::null_string::NullTerminatedString;"#); cg!(self, r#"use crate::enums::*;"#); cg!(self, r#"use crate::types::*;"#); cg!(self, r#"use crate::dataconsts::*;"#); cg!(self, r#"use crate::packet::PacketPayload;"#); - cg!(self, r#"use crate::handlers::null_string::NullTerminatedString;"#); let mut iserialize: HashMap = HashMap::new(); iserialize.insert("int8_t".to_string(), "i8".to_string()); From efaf8c3d99e82022359c958ee5f54bd1487911fc Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Thu, 19 Dec 2024 06:27:54 -0500 Subject: [PATCH 24/27] - update: generated commented now uses the @generated tag --- generator/src/codegen/rust/codegen_source.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index dfc70da..96e4413 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -32,7 +32,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { pub fn generate(&mut self, packet: &Packet) -> Result<()> { let version = self.version.clone(); - cg!(self, "/* Generated with IDL v{} */\n", version); + cg!(self, "/* This file is @generated with IDL v{} */\n", version); cg!(self, r#"use bincode::{{Encode, Decode, enc::Encoder, de::Decoder, error::DecodeError}};"#); cg!(self, r#"use bincode::de::read::Reader;"#); cg!(self, r#"use bincode::enc::write::Writer;"#); From 789b42b95c1287df08e89fe7d506e53290d0938b Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Sun, 5 Jan 2025 22:10:34 -0500 Subject: [PATCH 25/27] - fix: no longer use Vec for a fixed array - fix: issue where complex types could potentially output a rust reserved name --- generator/src/codegen/rust/codegen_source.rs | 40 +++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 96e4413..3b9a9c9 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -167,10 +167,16 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } if false == is_rust_native { - cg!(self, "let mut {} = Vec::with_capacity({} as usize);", name, n); - cg!(self, "for _ in 0..{} as usize {{", n); + type_prefix = ""; + if n.parse::().is_ok() { + cg!(self, "let mut {}: [{}{}; {}] = core::array::from_fn(|i| {}{}::default());", name, type_prefix, rust_type, n, type_prefix, rust_type); + } else { + cg!(self, "let mut {}: [{}{}; ({} as usize)] = core::array::from_fn(|i| {}{}::default());", name, type_prefix, rust_type, n, type_prefix, rust_type); + } + + cg!(self, "for index in 0..{} as usize {{", n); self.indent(); - cg!(self, "{}.push({}::decode(decoder)?);", name, rust_type); + cg!(self, "{}[index] = {}::decode(decoder)?;", name, rust_type); self.dedent(); cg!(self, "}}"); } else { @@ -359,7 +365,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } cg!(self); - cg!(self, r#"#[derive(Debug)]"#); + cg!(self, r#"#[derive(Debug, Clone, Default)]"#); cg!(self, "pub struct {} {{", complex.name()); self.indent(); match complex.content() { @@ -399,14 +405,14 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { match complex.content() { Seq(ref s) => { for elem in s.elements() { - let data = elem.name().to_string().to_snake_case(); - cg!(self, "self.{}.encode(encoder)?;", data); + let name = rename_if_reserved(elem.name()); + cg!(self, "self.{}.encode(encoder)?;", name); } }, Choice(ref c) => { for elem in c.elements() { - let data = elem.name().to_string().to_snake_case(); - cg!(self, "self.{}.encode(encoder)?;", data); + let name = rename_if_reserved(elem.name()); + cg!(self, "self.{}.encode(encoder)?;", name); } }, Empty => {} @@ -431,7 +437,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { match complex.content() { Seq(ref s) => { for elem in s.elements() { - let name = elem.name().to_string().to_snake_case(); + let name = rename_if_reserved(elem.name()); let trimmed_type = elem.type_().trim().to_string(); let mut is_rust_native = true; let rust_type = iserialize.get(elem.type_().trim()).map(|s| s.to_string()).unwrap_or_else(|| { @@ -453,10 +459,16 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { } if false == is_rust_native { - cg!(self, "let mut {} = Vec::with_capacity({} as usize);", name, n); - cg!(self, "for _ in 0..{} as usize {{", n); + type_prefix = ""; + if n.parse::().is_ok() { + cg!(self, "let mut {}: [{}{}; {}] = core::array::from_fn(|i| {}{}::default());", name, type_prefix, rust_type, n, type_prefix, rust_type); + } else { + cg!(self, "let mut {}: [{}{}; ({} as usize)] = core::array::from_fn(|i| {}{}::default());", name, type_prefix, rust_type, n, type_prefix, rust_type); + } + + cg!(self, "for index in 0..{} as usize {{", n); self.indent(); - cg!(self, "{}.push({}::decode(decoder)?);", name, rust_type); + cg!(self, "{}[index] = {}::decode(decoder)?;", name, rust_type); self.dedent(); cg!(self, "}}"); } else { @@ -481,7 +493,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { }, Choice(ref c) => { for elem in c.elements() { - let name = elem.name().to_string().to_snake_case(); + let name = rename_if_reserved(elem.name()); let trimmed_type = elem.type_().trim().to_string(); let rust_type = iserialize.get(elem.type_().trim()).map(|s| s.to_string()).unwrap_or_else(|| { debug!(r#"Type "{}" not found, outputting anyway"#, elem.type_()); @@ -539,7 +551,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { Unbounded => format!("Vec<{}>", rust_type), Num(n) => { if false == is_rust_native { - format!("Vec<{}>", rust_type) + format!("[{}; ({} as usize)]", rust_type, n) } else { if n.parse::().is_ok() { format!("[{}; {}]", rust_type, n) From 6355321739fc95a652d3557123690c511cafcf59 Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Fri, 7 Mar 2025 14:37:49 -0500 Subject: [PATCH 26/27] - update: generated bincode::Decode impl functions to match bincode 2.0.0 release --- generator/src/codegen/rust/codegen_source.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generator/src/codegen/rust/codegen_source.rs b/generator/src/codegen/rust/codegen_source.rs index 3b9a9c9..bc78f05 100644 --- a/generator/src/codegen/rust/codegen_source.rs +++ b/generator/src/codegen/rust/codegen_source.rs @@ -138,7 +138,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "}}"); cg!(self); - cg!(self, "impl Decode for {} {{", packet.class_name().to_upper_camel_case()); + cg!(self, "impl Decode for {} {{", packet.class_name().to_upper_camel_case()); self.indent(); cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); self.indent(); @@ -340,7 +340,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { cg!(self, "}}"); cg!(self); - cg!(self, "impl Decode for {} {{", elem.name()); + cg!(self, "impl Decode for {} {{", elem.name()); self.indent(); cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); self.indent(); @@ -428,7 +428,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { fn complex_decode(&mut self, complex: &ComplexType, iserialize: &HashMap) -> Result<()> { use ::flat_ast::ComplexTypeContent::*; - cg!(self, "impl Decode for {} {{", complex.name().to_upper_camel_case()); + cg!(self, "impl Decode for {} {{", complex.name().to_upper_camel_case()); self.indent(); cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); self.indent(); @@ -683,7 +683,7 @@ impl<'a, W: Write> CodeSourceGenerator<'a, W> { rust_type = "String".to_string(); } - cg!(self, "impl Decode for {} {{", name.to_upper_camel_case()); + cg!(self, "impl Decode for {} {{", name.to_upper_camel_case()); self.indent(); cg!(self, "fn decode(decoder: &mut D) -> std::result::Result {{"); self.indent(); From 0dba330c6abab33397526a1ef31ad3e3b5fe4b6f Mon Sep 17 00:00:00 2001 From: raven <7156279+RavenX8@users.noreply.github.com> Date: Sun, 8 Feb 2026 19:18:11 -0500 Subject: [PATCH 27/27] update: bump GitHub Actions dependencies to latest versions --- .github/workflows/rust.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 166fad7..ca56610 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,7 +10,7 @@ jobs: linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Get processor arch run: echo "PROCESSOR_ARCH=`uname -p`" >> $GITHUB_ENV - name: Build @@ -20,14 +20,14 @@ jobs: - name: Prepare Release run: tar --transform 's/.*\///g' -zcvf ${{github.workspace}}/${{ runner.os }}-${{ env.PROCESSOR_ARCH }}-packet_generator.tar.gz target/*/packet_generator - name: Upload linux build - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: linux_build path: ${{github.workspace}}/${{ runner.os }}-${{ env.PROCESSOR_ARCH }}-packet_generator.tar.gz windows: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Build run: cargo build --release --verbose - name: Run tests @@ -37,7 +37,7 @@ jobs: copy-item ${{github.workspace}}\target\release\packet_generator.exe -destination ${{github.workspace}}\packet_generator.exe 7z a ${{github.workspace}}/windows-amd64-packet_generator.zip ${{github.workspace}}\packet_generator.exe - name: Upload windows build - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: windows_build path: ${{github.workspace}}/windows-amd64-packet_generator.zip @@ -48,11 +48,11 @@ jobs: if: github.ref == 'refs/heads/master' steps: - name: Download linux build - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: linux_build - name: Download windows build - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: windows_build - name: Release