From cdb43f02506d1e6b195ef5e7297db0e9e4b2f1fb Mon Sep 17 00:00:00 2001 From: Nasr Date: Mon, 15 Jan 2024 12:37:15 -0500 Subject: [PATCH] feat: update to new dojo-bindgen version & handle enums --- crates/dojo-bindgen/src/plugins/unity/mod.rs | 183 +++++++++++++++++-- 1 file changed, 170 insertions(+), 13 deletions(-) diff --git a/crates/dojo-bindgen/src/plugins/unity/mod.rs b/crates/dojo-bindgen/src/plugins/unity/mod.rs index 1560e1c45f..3d629be29a 100644 --- a/crates/dojo-bindgen/src/plugins/unity/mod.rs +++ b/crates/dojo-bindgen/src/plugins/unity/mod.rs @@ -1,32 +1,189 @@ use async_trait::async_trait; +use cainome::parser::tokens::{Composite, Token}; -use crate::error::BindgenResult; +use crate::error::{BindgenResult, Error}; use crate::plugins::BuiltinPlugin; -use crate::DojoData; +use crate::{DojoData, DojoModel}; -pub struct UnityPlugin; + +#[derive(Debug)] +pub enum UnityPluginError { + InvalidType(String), +} + +impl std::fmt::Display for UnityPluginError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + UnityPluginError::InvalidType(type_path) => write!(f, "Invalid type: {}", type_path), + } + } +} + +impl std::error::Error for UnityPluginError {} + +pub struct UnityPlugin { +} impl UnityPlugin { pub fn new() -> Self { - Self {} + Self { + } + } + + // Maps cairo types to C#/Unity SDK defined types + fn map_type(type_name: &str) -> Result { + match type_name { + "u8" => Ok("byte".to_string()), + "u16" => Ok("ushort".to_string()), + "u32" => Ok("uint".to_string()), + "u64" => Ok("ulong".to_string()), + "u128" => Ok("Span".to_string()), + "u256" => Ok("Span".to_string()), + "usize" => Ok("uint".to_string()), + "felt252" => Ok("FieldElement".to_string()), + "ClassHash" => Ok("FieldElement".to_string()), + "ContractAddress" => Ok("FieldElement".to_string()), + + _ => Ok(type_name.to_string()), + } + } + + // Token should be a struct + // This will be formatted into a C# struct + // using C# and unity SDK types + fn format_struct(token: &Composite) -> Result { + let fields = token.inners.iter().map(|field| { + format!( + "public {} {};", + UnityPlugin::map_type(field.token.clone().type_name().as_str()).unwrap(), + field.name + ) + }).collect::>().join("\n "); + + return Ok(format!( + " +[Serializable] +public struct {} {{ + {} +}} +", + token.type_name(), fields + )); + } + + // Token should be an enum + // This will be formatted into a C# enum + // Enum is mapped using index of cairo enum + fn format_enum(token: &Composite) -> Result { + let fields = token.inners.iter().map(|field| { + format!( + "{},", + field.name, + ) + }).collect::>().join("\n "); + + return Ok(format!( + " +public enum {} {{ + {} +}} +", + token.type_name(), fields + )); + } + + fn format_model(model: &Composite) -> Result { + let fields = model.inners.iter().map(|field| {format!( + "[ModelField(\"{}\")]\n public {} {};", + field.name, + UnityPlugin::map_type(field.token.type_name().as_str()).unwrap(), + field.name, + )}).collect::>().join("\n\n "); + + return Ok(format!( + " +public class {} : ModelInstance {{ + {} + + // Start is called before the first frame update + void Start() {{ + }} + + // Update is called once per frame + void Update() {{ + }} +}} + ", + model.type_name(), fields + )); + } + + fn handle_model(&self, model: &DojoModel) -> Result { + let mut out = String::new(); + out += "using System;\n"; + out += "using Dojo;\n"; + out += "using Dojo.Starknet;\n"; + + if model.tokens.get("structs").is_none() { + return Err(UnityPluginError::InvalidType("structs".to_string())); + } + + let mut model_struct: Option<&Composite> = None; + let tokens = &model.tokens; + for (token_type, tokens) in tokens { + match token_type.as_str() { + "structs" => { + for token in tokens { + // first index is our model struct + if token.type_name() == model.name { + model_struct = Some(token.to_composite().unwrap()); + continue; + } + + out += UnityPlugin::format_struct(token.to_composite().unwrap())?.as_str(); + } + }, + "enums" => { + for token in tokens { + out += UnityPlugin::format_enum(token.to_composite().unwrap())?.as_str(); + } + }, + "functions" => {}, + _ => { + return Err(UnityPluginError::InvalidType(token_type.to_string())); + } + } + } + + out += "\n"; + + out += UnityPlugin::format_model(model_struct.expect("model struct not found"))?.as_str(); + + Ok(out) } } #[async_trait] impl BuiltinPlugin for UnityPlugin { - async fn generate_code(&self, data: &DojoData) -> BindgenResult<()> { - println!("-> Unity models bindings\n"); - + async fn generate_code( + &self, + data: &DojoData + ) -> BindgenResult<()> { + // Handle codegen for models for (name, model) in &data.models { - println!("## Model: {}", name); - println!("{:?}\n", model); + println!("Generating model: {}", name); + let code = self.handle_model(model).map_err(|e| { + Error::Format(format!("Failed to generate code for model: {}", e)) + })?; + // println!("{}", code); } - for (file_name, contract) in &data.contracts { - println!("## Contract: {}", file_name); - println!("{:?}\n", contract); + // Handle codegen for systems + for (name, system) in &data.contracts { + println!("Generating system: {}", name); + println!("{:?}", system); } Ok(()) } -} +} \ No newline at end of file