Skip to content

Commit

Permalink
feat(dojo-bindgen): fs & unity plugin for codegen (#1498)
Browse files Browse the repository at this point in the history
* fix: ensure correct model detection via interfaces

* fix: clippy + attempt to fix test on windows

* fix: clippy

* fix: comment test until windows stack overflow is solved

* fix: fmt

* fix: fmt again

* refactor: update to new bindgen version

(cherry picked from commit 3310612)

* feat: contract class gen + system format

(cherry picked from commit 87d7e4b)

* feat: correctly handle native types in codegen

(cherry picked from commit ab0c5ba)

* feat: codegen metadata & cleaner code

(cherry picked from commit 68bb60b)

* feat: write code to fs

* fix: writing to fs and contract file name

* refactor: generated code

* chore: fmt

* refactor: generated folder and .,gitignore

* refactor: path structure for generated files

* chore: added code comments

* feat(bindgen): dynamic output directory

* feat(bindgen): prepend plugin name to path

* refactor(bindgen): remove option for bindgen result

---------

Co-authored-by: glihm <[email protected]>
  • Loading branch information
Larkooo and glihm authored Feb 1, 2024
1 parent b6a4333 commit 8224b91
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 100 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

8 changes: 7 additions & 1 deletion bin/sozo/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub struct BuildArgs {
#[arg(long)]
#[arg(help = "Generate Unity bindings.")]
pub unity: bool,

#[arg(long)]
#[arg(help = "Output directory.", default_value = "bindings")]
pub bindings_output: String,
}

impl BuildArgs {
Expand All @@ -34,6 +38,7 @@ impl BuildArgs {

// Custom plugins are always empty for now.
let bindgen = PluginManager {
output_path: self.bindings_output.into(),
artifacts_path: compile_info.target_dir,
plugins: vec![],
builtin_plugins,
Expand All @@ -58,7 +63,8 @@ mod tests {
fn build_example_with_typescript_and_unity_bindings() {
let config = build_test_config("../../examples/spawn-and-move/Scarb.toml").unwrap();

let build_args = BuildArgs { typescript: true, unity: true };
let build_args =
BuildArgs { bindings_output: "generated".to_string(), typescript: true, unity: true };
let result = build_args.run(&config);
assert!(result.is_ok());
}
Expand Down
7 changes: 4 additions & 3 deletions crates/dojo-bindgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ starknet.workspace = true
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
chrono.workspace = true

# Some issue with CI on windows, need to be investigated.
# https://github.com/dojoengine/dojo/actions/runs/7548423990/job/20550444492?pr=1425#step:6:1644
#dojo-test-utils = { path = "../dojo-test-utils", features = [ "build-examples" ] }
# https://github.com/dojoengine/dojo/actions/runs/7736050751/job/21092743552?pr=1501#step:6:249
# dojo-test-utils = { path = "../dojo-test-utils", features = [ "build-examples" ] }

cainome = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.2.2" }
cainome = { git = "https://github.com/cartridge-gg/cainome", tag = "v0.2.2" }
179 changes: 96 additions & 83 deletions crates/dojo-bindgen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;

use cainome::parser::tokens::Token;
use cainome::parser::{AbiParser, TokenizedAbi};
Expand Down Expand Up @@ -48,6 +49,8 @@ pub struct DojoData {
// TODO: include the manifest to have more metadata when new manifest is available.
#[derive(Debug)]
pub struct PluginManager {
/// Path of generated files.
pub output_path: PathBuf,
/// Path of contracts artifacts.
pub artifacts_path: Utf8PathBuf,
/// A list of builtin plugins to invoke.
Expand All @@ -72,7 +75,14 @@ impl PluginManager {
BuiltinPlugins::Unity => Box::new(UnityPlugin::new()),
};

builder.generate_code(&data).await?;
let files = builder.generate_code(&data).await?;
for (path, content) in files {
// Prepends the output directory and plugin name to the path.
let path = self.output_path.join(plugin.to_string()).join(path);
fs::create_dir_all(path.parent().unwrap()).unwrap();

fs::write(path, content)?;
}
}
Ok(())
}
Expand Down Expand Up @@ -250,97 +260,100 @@ fn is_model_contract(tokens: &TokenizedAbi) -> bool {

let mut funcs_counts = 0;

// This hashmap is not that good at devex level.. one must check the
// code to know the keys.
for f in &tokens.functions {
if expected_funcs.contains(&f.to_function().expect("Function expected").name.as_str()) {
funcs_counts += 1;
for functions in tokens.interfaces.values() {
for f in functions {
if expected_funcs.contains(&f.to_function().expect("Function expected").name.as_str()) {
funcs_counts += 1;
}
}
}

funcs_counts == expected_funcs.len()
}

// Uncomment tests once windows issue is solved.
// #[cfg(test)]
// mod tests {
// use super::*;
//
// #[test]
// fn is_system_contract_ok() {
// let file_name = "dojo_examples::actions::actions.json";
// let file_content = include_str!(
// "test_data/spawn-and-move/target/dev/dojo_examples::actions::actions.json"
// );
// use super::*;

// #[test]
// fn is_system_contract_ok() {
// let file_name = "dojo_examples::actions::actions.json";
// let file_content = include_str!(
// "test_data/spawn-and-move/target/dev/dojo_examples::actions::actions.json"
// );

// assert!(is_systems_contract(file_name, file_content));
// }

// #[test]
// fn is_system_contract_ignore_dojo_files() {
// let file_name = "dojo::world::world.json";
// let file_content = "";
// assert!(!is_systems_contract(file_name, file_content));

// let file_name = "manifest.json";
// assert!(!is_systems_contract(file_name, file_content));
// }

// #[test]
// fn test_is_system_contract_ignore_models() {
// let file_name = "dojo_examples::models::position.json";
// let file_content = include_str!(
// "test_data/spawn-and-move/target/dev/dojo_examples::models::position.json"
// );
// assert!(!is_systems_contract(file_name, file_content));
// }

// #[test]
// fn model_name_from_artifact_filename_ok() {
// let file_name = "dojo_examples::models::position.json";
// assert_eq!(model_name_from_artifact_filename(file_name), Some("position".to_string()));
// }

// #[test]
// fn is_model_contract_ok() {
// let file_content =
//
// assert!(is_systems_contract(file_name, file_content));
// }
//
// #[test]
// fn is_system_contract_ignore_dojo_files() {
// let file_name = "dojo::world::world.json";
// let file_content = "";
// assert!(!is_systems_contract(file_name, file_content));
//
// let file_name = "manifest.json";
// assert!(!is_systems_contract(file_name, file_content));
// }
//
// #[test]
// fn test_is_system_contract_ignore_models() {
// let file_name = "dojo_examples::models::position.json";
// let file_content = include_str!(
// "test_data/spawn-and-move/target/dev/dojo_examples::models::position.json"
// );
// assert!(!is_systems_contract(file_name, file_content));
// }
//
// #[test]
// fn model_name_from_artifact_filename_ok() {
// let file_name = "dojo_examples::models::position.json";
// assert_eq!(model_name_from_artifact_filename(file_name), Some("position".to_string()));
// }
//
// #[test]
// fn is_model_contract_ok() {
// let file_content =
// include_str!("test_data/spawn-and-move/target/dev/dojo_examples::models::moves.json");
// let tokens = AbiParser::tokens_from_abi_string(file_content, &HashMap::new()).unwrap();
//
// assert!(is_model_contract(&tokens));
// }
//
// #[test]
// fn is_model_contract_ignore_systems() {
// let file_content = include_str!(
// "test_data/spawn-and-move/target/dev/dojo_examples::actions::actions.json"
// );
// let tokens = AbiParser::tokens_from_abi_string(file_content, &HashMap::new()).unwrap();
//
// assert!(!is_model_contract(&tokens));
// }
//
// #[test]
// fn is_model_contract_ignore_dojo_files() {
// let file_content =
// include_str!("test_data/spawn-and-move/target/dev/dojo::world::world.json");
// let tokens = AbiParser::tokens_from_abi_string(file_content, &HashMap::new()).unwrap();
//
// assert!(!is_model_contract(&tokens));
// }
//
// #[test]
// fn gather_data_ok() {
// let data = gather_dojo_data(&Utf8PathBuf::from("src/test_data/spawn-and-move/target/dev"))
// let tokens = AbiParser::tokens_from_abi_string(file_content, &HashMap::new()).unwrap();

// assert!(is_model_contract(&tokens));
// }

// #[test]
// fn is_model_contract_ignore_systems() {
// let file_content = include_str!(
// "test_data/spawn-and-move/target/dev/dojo_examples::actions::actions.json"
// );
// let tokens = AbiParser::tokens_from_abi_string(file_content, &HashMap::new()).unwrap();

// assert!(!is_model_contract(&tokens));
// }

// #[test]
// fn is_model_contract_ignore_dojo_files() {
// let file_content =
// include_str!("test_data/spawn-and-move/target/dev/dojo::world::world.json");
// let tokens = AbiParser::tokens_from_abi_string(file_content, &HashMap::new()).unwrap();

// assert!(!is_model_contract(&tokens));
// }

// #[test]
// fn gather_data_ok() {
// let data =
// gather_dojo_data(&Utf8PathBuf::from("src/test_data/spawn-and-move/target/dev"))
// .unwrap();
//
// assert_eq!(data.models.len(), 2);
//
// let pos = data.models.get("Position").unwrap();
// assert_eq!(pos.name, "Position");
// assert_eq!(pos.qualified_path, "dojo_examples::models::Position");
//
// let moves = data.models.get("Moves").unwrap();
// assert_eq!(moves.name, "Moves");
// assert_eq!(moves.qualified_path, "dojo_examples::models::Moves");
// }

// assert_eq!(data.models.len(), 2);

// let pos = data.models.get("Position").unwrap();
// assert_eq!(pos.name, "Position");
// assert_eq!(pos.qualified_path, "dojo_examples::models::Position");

// let moves = data.models.get("Moves").unwrap();
// assert_eq!(moves.name, "Moves");
// assert_eq!(moves.qualified_path, "dojo_examples::models::Moves");
// }
// }
15 changes: 14 additions & 1 deletion crates/dojo-bindgen/src/plugins/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::collections::HashMap;
use std::fmt;
use std::path::PathBuf;

use async_trait::async_trait;

use crate::error::BindgenResult;
Expand All @@ -12,12 +16,21 @@ pub enum BuiltinPlugins {
Unity,
}

impl fmt::Display for BuiltinPlugins {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BuiltinPlugins::Typescript => write!(f, "typescript"),
BuiltinPlugins::Unity => write!(f, "unity"),
}
}
}

#[async_trait]
pub trait BuiltinPlugin {
/// Generates code by executing the plugin.
///
/// # Arguments
///
/// * `data` - Dojo data gathered from the compiled project.
async fn generate_code(&self, data: &DojoData) -> BindgenResult<()>;
async fn generate_code(&self, data: &DojoData) -> BindgenResult<HashMap<PathBuf, Vec<u8>>>;
}
7 changes: 5 additions & 2 deletions crates/dojo-bindgen/src/plugins/typescript/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::collections::HashMap;
use std::path::PathBuf;

use async_trait::async_trait;

use crate::error::BindgenResult;
Expand All @@ -14,7 +17,7 @@ impl TypescriptPlugin {

#[async_trait]
impl BuiltinPlugin for TypescriptPlugin {
async fn generate_code(&self, data: &DojoData) -> BindgenResult<()> {
async fn generate_code(&self, data: &DojoData) -> BindgenResult<HashMap<PathBuf, Vec<u8>>> {
println!("-> Typescript models bindings\n");

for (name, model) in &data.models {
Expand All @@ -27,6 +30,6 @@ impl BuiltinPlugin for TypescriptPlugin {
println!("{:?}\n", contract);
}

Ok(())
Ok(HashMap::new())
}
}
Loading

0 comments on commit 8224b91

Please sign in to comment.