|
@@ -1,15 +1,15 @@
|
|
|
-use crate::VERSION;
|
|
|
-use anchor_cli::rust_template::{get_or_create_program_id, ProgramTemplate};
|
|
|
+use anchor_cli::rust_template::ProgramTemplate;
|
|
|
use anchor_cli::{create_files, Files};
|
|
|
-use anchor_lang_idl::types::{
|
|
|
- Idl, IdlArrayLen, IdlDefinedFields, IdlGenericArg, IdlType, IdlTypeDef, IdlTypeDefGeneric,
|
|
|
- IdlTypeDefTy,
|
|
|
-};
|
|
|
+use anchor_lang_idl::types::{IdlArrayLen, IdlGenericArg, IdlType};
|
|
|
use anyhow::Result;
|
|
|
-use heck::{ToSnakeCase, ToUpperCamelCase};
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
-pub const ANCHOR_VERSION: &str = anchor_cli::VERSION;
|
|
|
+use crate::templates::component::create_component_template_simple;
|
|
|
+use crate::templates::program::{create_program_template_multiple, create_program_template_single};
|
|
|
+use crate::templates::system::create_system_template_simple;
|
|
|
+use crate::templates::workspace::{
|
|
|
+ cargo_toml, cargo_toml_with_serde, workspace_manifest, xargo_toml,
|
|
|
+};
|
|
|
|
|
|
/// Create a component from the given name.
|
|
|
pub fn create_component(name: &str) -> Result<()> {
|
|
@@ -60,531 +60,6 @@ pub fn create_program(name: &str, template: ProgramTemplate) -> Result<()> {
|
|
|
create_files(&[common_files, template_files].concat())
|
|
|
}
|
|
|
|
|
|
-/// Create a component which holds position data.
|
|
|
-fn create_component_template_simple(name: &str, program_path: &Path) -> Files {
|
|
|
- vec![(
|
|
|
- program_path.join("src").join("lib.rs"),
|
|
|
- format!(
|
|
|
- r#"use bolt_lang::*;
|
|
|
-
|
|
|
-declare_id!("{}");
|
|
|
-
|
|
|
-#[component]
|
|
|
-#[derive(Default)]
|
|
|
-pub struct {} {{
|
|
|
- pub x: i64,
|
|
|
- pub y: i64,
|
|
|
- pub z: i64,
|
|
|
- #[max_len(20)]
|
|
|
- pub description: String,
|
|
|
-}}
|
|
|
-"#,
|
|
|
- anchor_cli::rust_template::get_or_create_program_id(name),
|
|
|
- name.to_upper_camel_case(),
|
|
|
- ),
|
|
|
- )]
|
|
|
-}
|
|
|
-
|
|
|
-/// Create a system which operates on a Position component.
|
|
|
-fn create_system_template_simple(name: &str, program_path: &Path) -> Files {
|
|
|
- vec![(
|
|
|
- program_path.join("src").join("lib.rs"),
|
|
|
- format!(
|
|
|
- r#"use bolt_lang::*;
|
|
|
-use position::Position;
|
|
|
-
|
|
|
-declare_id!("{}");
|
|
|
-
|
|
|
-#[system]
|
|
|
-pub mod {} {{
|
|
|
-
|
|
|
- pub fn execute(ctx: Context<Components>, _args_p: Vec<u8>) -> Result<Components> {{
|
|
|
- let position = &mut ctx.accounts.position;
|
|
|
- position.x += 1;
|
|
|
- position.y += 1;
|
|
|
- Ok(ctx.accounts)
|
|
|
- }}
|
|
|
-
|
|
|
- #[system_input]
|
|
|
- pub struct Components {{
|
|
|
- pub position: Position,
|
|
|
- }}
|
|
|
-
|
|
|
-}}
|
|
|
-"#,
|
|
|
- anchor_cli::rust_template::get_or_create_program_id(name),
|
|
|
- name.to_snake_case(),
|
|
|
- ),
|
|
|
- )]
|
|
|
-}
|
|
|
-
|
|
|
-fn create_program_template_single(name: &str, program_path: &Path) -> Files {
|
|
|
- vec![(
|
|
|
- program_path.join("src").join("lib.rs"),
|
|
|
- format!(
|
|
|
- r#"use anchor_lang::prelude::*;
|
|
|
-
|
|
|
-declare_id!("{}");
|
|
|
-
|
|
|
-#[program]
|
|
|
-pub mod {} {{
|
|
|
- use super::*;
|
|
|
-
|
|
|
- pub fn initialize(ctx: Context<Initialize>) -> Result<()> {{
|
|
|
- Ok(())
|
|
|
- }}
|
|
|
-}}
|
|
|
-
|
|
|
-#[derive(Accounts)]
|
|
|
-pub struct Initialize {{}}
|
|
|
-"#,
|
|
|
- get_or_create_program_id(name),
|
|
|
- name.to_snake_case(),
|
|
|
- ),
|
|
|
- )]
|
|
|
-}
|
|
|
-
|
|
|
-/// Create a program with multiple files for instructions, state...
|
|
|
-fn create_program_template_multiple(name: &str, program_path: &Path) -> Files {
|
|
|
- let src_path = program_path.join("src");
|
|
|
- vec![
|
|
|
- (
|
|
|
- src_path.join("lib.rs"),
|
|
|
- format!(
|
|
|
- r#"pub mod constants;
|
|
|
-pub mod error;
|
|
|
-pub mod instructions;
|
|
|
-pub mod state;
|
|
|
-
|
|
|
-use anchor_lang::prelude::*;
|
|
|
-
|
|
|
-pub use constants::*;
|
|
|
-pub use instructions::*;
|
|
|
-pub use state::*;
|
|
|
-
|
|
|
-declare_id!("{}");
|
|
|
-
|
|
|
-#[program]
|
|
|
-pub mod {} {{
|
|
|
- use super::*;
|
|
|
-
|
|
|
- pub fn initialize(ctx: Context<Initialize>) -> Result<()> {{
|
|
|
- initialize::handler(ctx)
|
|
|
- }}
|
|
|
-}}
|
|
|
-"#,
|
|
|
- get_or_create_program_id(name),
|
|
|
- name.to_snake_case(),
|
|
|
- ),
|
|
|
- ),
|
|
|
- (
|
|
|
- src_path.join("constants.rs"),
|
|
|
- r#"use anchor_lang::prelude::*;
|
|
|
-
|
|
|
-#[constant]
|
|
|
-pub const SEED: &str = "anchor";
|
|
|
-"#
|
|
|
- .into(),
|
|
|
- ),
|
|
|
- (
|
|
|
- src_path.join("error.rs"),
|
|
|
- r#"use anchor_lang::prelude::*;
|
|
|
-
|
|
|
-#[error_code]
|
|
|
-pub enum ErrorCode {
|
|
|
- #[msg("Custom error message")]
|
|
|
- CustomError,
|
|
|
-}
|
|
|
-"#
|
|
|
- .into(),
|
|
|
- ),
|
|
|
- (
|
|
|
- src_path.join("instructions").join("mod.rs"),
|
|
|
- r#"pub mod initialize;
|
|
|
-
|
|
|
-pub use initialize::*;
|
|
|
-"#
|
|
|
- .into(),
|
|
|
- ),
|
|
|
- (
|
|
|
- src_path.join("instructions").join("initialize.rs"),
|
|
|
- r#"use anchor_lang::prelude::*;
|
|
|
-
|
|
|
-#[derive(Accounts)]
|
|
|
-pub struct Initialize {}
|
|
|
-
|
|
|
-pub fn handler(ctx: Context<Initialize>) -> Result<()> {
|
|
|
- Ok(())
|
|
|
-}
|
|
|
-"#
|
|
|
- .into(),
|
|
|
- ),
|
|
|
- (src_path.join("state").join("mod.rs"), r#""#.into()),
|
|
|
- ]
|
|
|
-}
|
|
|
-
|
|
|
-const fn workspace_manifest() -> &'static str {
|
|
|
- r#"[workspace]
|
|
|
-members = [
|
|
|
- "programs/*",
|
|
|
- "programs-ecs/components/*",
|
|
|
- "programs-ecs/systems/*"
|
|
|
-]
|
|
|
-resolver = "2"
|
|
|
-
|
|
|
-[profile.release]
|
|
|
-overflow-checks = true
|
|
|
-lto = "fat"
|
|
|
-codegen-units = 1
|
|
|
-[profile.release.build-override]
|
|
|
-opt-level = 3
|
|
|
-incremental = false
|
|
|
-codegen-units = 1
|
|
|
-"#
|
|
|
-}
|
|
|
-
|
|
|
-pub fn package_json(jest: bool) -> String {
|
|
|
- if jest {
|
|
|
- format!(
|
|
|
- r#"{{
|
|
|
- "scripts": {{
|
|
|
- "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
- "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
- }},
|
|
|
- "dependencies": {{
|
|
|
- "@coral-xyz/anchor": "^{ANCHOR_VERSION}"
|
|
|
- }},
|
|
|
- "devDependencies": {{
|
|
|
- "jest": "^29.0.3",
|
|
|
- "prettier": "^2.6.2"
|
|
|
- }}
|
|
|
- }}
|
|
|
- "#
|
|
|
- )
|
|
|
- } else {
|
|
|
- format!(
|
|
|
- r#"{{
|
|
|
- "scripts": {{
|
|
|
- "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
- "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
- }},
|
|
|
- "dependencies": {{
|
|
|
- "@coral-xyz/anchor": "^{ANCHOR_VERSION}"
|
|
|
- }},
|
|
|
- "devDependencies": {{
|
|
|
- "chai": "^4.3.4",
|
|
|
- "mocha": "^9.0.3",
|
|
|
- "prettier": "^2.6.2",
|
|
|
- "@metaplex-foundation/beet": "^0.7.1",
|
|
|
- "@metaplex-foundation/beet-solana": "^0.4.0",
|
|
|
- "@magicblock-labs/bolt-sdk": "latest"
|
|
|
- }}
|
|
|
-}}
|
|
|
-"#
|
|
|
- )
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-pub fn ts_package_json(jest: bool) -> String {
|
|
|
- if jest {
|
|
|
- format!(
|
|
|
- r#"{{
|
|
|
- "scripts": {{
|
|
|
- "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
- "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
- }},
|
|
|
- "dependencies": {{
|
|
|
- "@coral-xyz/anchor": "^{ANCHOR_VERSION}"
|
|
|
- }},
|
|
|
- "devDependencies": {{
|
|
|
- "@types/bn.js": "^5.1.0",
|
|
|
- "@types/jest": "^29.0.3",
|
|
|
- "jest": "^29.0.3",
|
|
|
- "prettier": "^2.6.2",
|
|
|
- "ts-jest": "^29.0.2",
|
|
|
- "typescript": "^4.3.5",
|
|
|
- "@metaplex-foundation/beet": "^0.7.1",
|
|
|
- "@metaplex-foundation/beet-solana": "^0.4.0",
|
|
|
- "@magicblock-labs/bolt-sdk": "latest"
|
|
|
- }}
|
|
|
- }}
|
|
|
- "#
|
|
|
- )
|
|
|
- } else {
|
|
|
- format!(
|
|
|
- r#"{{
|
|
|
- "scripts": {{
|
|
|
- "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
|
|
- "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
|
|
- }},
|
|
|
- "dependencies": {{
|
|
|
- "@coral-xyz/anchor": "^{ANCHOR_VERSION}"
|
|
|
- }},
|
|
|
- "devDependencies": {{
|
|
|
- "chai": "^4.3.4",
|
|
|
- "mocha": "^9.0.3",
|
|
|
- "ts-mocha": "^10.0.0",
|
|
|
- "@types/bn.js": "^5.1.0",
|
|
|
- "@types/chai": "^4.3.0",
|
|
|
- "@types/mocha": "^9.0.0",
|
|
|
- "typescript": "^4.3.5",
|
|
|
- "prettier": "^2.6.2",
|
|
|
- "@metaplex-foundation/beet": "^0.7.1",
|
|
|
- "@metaplex-foundation/beet-solana": "^0.4.0",
|
|
|
- "@magicblock-labs/bolt-sdk": "latest"
|
|
|
- }}
|
|
|
-}}
|
|
|
-"#
|
|
|
- )
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-pub fn mocha(name: &str) -> String {
|
|
|
- format!(
|
|
|
- r#"const anchor = require("@coral-xyz/anchor");
|
|
|
-const boltSdk = require("@magicblock-labs/bolt-sdk");
|
|
|
-const {{
|
|
|
- InitializeNewWorld,
|
|
|
-}} = boltSdk;
|
|
|
-
|
|
|
-describe("{}", () => {{
|
|
|
- // Configure the client to use the local cluster.
|
|
|
- const provider = anchor.AnchorProvider.env();
|
|
|
- anchor.setProvider(provider);
|
|
|
-
|
|
|
- it("InitializeNewWorld", async () => {{
|
|
|
- const initNewWorld = await InitializeNewWorld({{
|
|
|
- payer: provider.wallet.publicKey,
|
|
|
- connection: provider.connection,
|
|
|
- }});
|
|
|
- const txSign = await provider.sendAndConfirm(initNewWorld.transaction);
|
|
|
- console.log(`Initialized a new world (ID=${{initNewWorld.worldPda}}). Initialization signature: ${{txSign}}`);
|
|
|
- }});
|
|
|
- }});
|
|
|
-}});
|
|
|
-"#,
|
|
|
- name,
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-pub fn jest(name: &str) -> String {
|
|
|
- format!(
|
|
|
- r#"const anchor = require("@coral-xyz/anchor");
|
|
|
-const boltSdk = require("@magicblock-labs/bolt-sdk");
|
|
|
-const {{
|
|
|
- InitializeNewWorld,
|
|
|
-}} = boltSdk;
|
|
|
-
|
|
|
-describe("{}", () => {{
|
|
|
- // Configure the client to use the local cluster.
|
|
|
- const provider = anchor.AnchorProvider.env();
|
|
|
- anchor.setProvider(provider);
|
|
|
-
|
|
|
- // Constants used to test the program.
|
|
|
- let worldPda: PublicKey;
|
|
|
-
|
|
|
- it("InitializeNewWorld", async () => {{
|
|
|
- const initNewWorld = await InitializeNewWorld({{
|
|
|
- payer: provider.wallet.publicKey,
|
|
|
- connection: provider.connection,
|
|
|
- }});
|
|
|
- const txSign = await provider.sendAndConfirm(initNewWorld.transaction);
|
|
|
- worldPda = initNewWorld.worldPda;
|
|
|
- console.log(`Initialized a new world (ID=${{worldPda}}). Initialization signature: ${{txSign}}`);
|
|
|
- }});
|
|
|
- }});
|
|
|
-"#,
|
|
|
- name,
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-pub fn ts_mocha(name: &str) -> String {
|
|
|
- format!(
|
|
|
- r#"import * as anchor from "@coral-xyz/anchor";
|
|
|
-import {{ Program }} from "@coral-xyz/anchor";
|
|
|
-import {{ PublicKey }} from "@solana/web3.js";
|
|
|
-import {{ Position }} from "../target/types/position";
|
|
|
-import {{ Movement }} from "../target/types/movement";
|
|
|
-import {{
|
|
|
- InitializeNewWorld,
|
|
|
- AddEntity,
|
|
|
- InitializeComponent,
|
|
|
- ApplySystem,
|
|
|
-}} from "@magicblock-labs/bolt-sdk"
|
|
|
-import {{expect}} from "chai";
|
|
|
-
|
|
|
-describe("{}", () => {{
|
|
|
- // Configure the client to use the local cluster.
|
|
|
- const provider = anchor.AnchorProvider.env();
|
|
|
- anchor.setProvider(provider);
|
|
|
-
|
|
|
- // Constants used to test the program.
|
|
|
- let worldPda: PublicKey;
|
|
|
- let entityPda: PublicKey;
|
|
|
- let componentPda: PublicKey;
|
|
|
-
|
|
|
- const positionComponent = anchor.workspace.Position as Program<Position>;
|
|
|
- const systemMovement = anchor.workspace.Movement as Program<Movement>;
|
|
|
-
|
|
|
- it("InitializeNewWorld", async () => {{
|
|
|
- const initNewWorld = await InitializeNewWorld({{
|
|
|
- payer: provider.wallet.publicKey,
|
|
|
- connection: provider.connection,
|
|
|
- }});
|
|
|
- const txSign = await provider.sendAndConfirm(initNewWorld.transaction);
|
|
|
- worldPda = initNewWorld.worldPda;
|
|
|
- console.log(`Initialized a new world (ID=${{worldPda}}). Initialization signature: ${{txSign}}`);
|
|
|
- }});
|
|
|
-
|
|
|
- it("Add an entity", async () => {{
|
|
|
- const addEntity = await AddEntity({{
|
|
|
- payer: provider.wallet.publicKey,
|
|
|
- world: worldPda,
|
|
|
- connection: provider.connection,
|
|
|
- }});
|
|
|
- const txSign = await provider.sendAndConfirm(addEntity.transaction);
|
|
|
- entityPda = addEntity.entityPda;
|
|
|
- console.log(`Initialized a new Entity (ID=${{addEntity.entityId}}). Initialization signature: ${{txSign}}`);
|
|
|
- }});
|
|
|
-
|
|
|
- it("Add a component", async () => {{
|
|
|
- const initializeComponent = await InitializeComponent({{
|
|
|
- payer: provider.wallet.publicKey,
|
|
|
- entity: entityPda,
|
|
|
- componentId: positionComponent.programId,
|
|
|
- }});
|
|
|
- const txSign = await provider.sendAndConfirm(initializeComponent.transaction);
|
|
|
- componentPda = initializeComponent.componentPda;
|
|
|
- console.log(`Initialized the grid component. Initialization signature: ${{txSign}}`);
|
|
|
- }});
|
|
|
-
|
|
|
- it("Apply a system", async () => {{
|
|
|
- // Check that the component has been initialized and x is 0
|
|
|
- const positionBefore = await positionComponent.account.position.fetch(
|
|
|
- componentPda
|
|
|
- );
|
|
|
- expect(positionBefore.x.toNumber()).to.equal(0);
|
|
|
-
|
|
|
- // Run the movement system
|
|
|
- const applySystem = await ApplySystem({{
|
|
|
- authority: provider.wallet.publicKey,
|
|
|
- systemId: systemMovement.programId,
|
|
|
- entities: [{{
|
|
|
- entity: entityPda,
|
|
|
- components: [{{ componentId: positionComponent.programId }}],
|
|
|
- }}]
|
|
|
- }});
|
|
|
- const txSign = await provider.sendAndConfirm(applySystem.transaction);
|
|
|
- console.log(`Applied a system. Signature: ${{txSign}}`);
|
|
|
-
|
|
|
- // Check that the system has been applied and x is > 0
|
|
|
- const positionAfter = await positionComponent.account.position.fetch(
|
|
|
- componentPda
|
|
|
- );
|
|
|
- expect(positionAfter.x.toNumber()).to.gt(0);
|
|
|
- }});
|
|
|
-
|
|
|
-}});
|
|
|
-"#,
|
|
|
- name.to_upper_camel_case(),
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-fn cargo_toml(name: &str) -> String {
|
|
|
- format!(
|
|
|
- r#"[package]
|
|
|
-name = "{0}"
|
|
|
-version = "{2}"
|
|
|
-description = "Created with Bolt"
|
|
|
-edition = "2021"
|
|
|
-
|
|
|
-[lib]
|
|
|
-crate-type = ["cdylib", "lib"]
|
|
|
-name = "{1}"
|
|
|
-
|
|
|
-[features]
|
|
|
-no-entrypoint = []
|
|
|
-no-idl = []
|
|
|
-no-log-ix-name = []
|
|
|
-cpi = ["no-entrypoint"]
|
|
|
-default = []
|
|
|
-idl-build = ["anchor-lang/idl-build"]
|
|
|
-
|
|
|
-[dependencies]
|
|
|
-bolt-lang = "{2}"
|
|
|
-anchor-lang = "{3}"
|
|
|
-"#,
|
|
|
- name,
|
|
|
- name.to_snake_case(),
|
|
|
- VERSION,
|
|
|
- ANCHOR_VERSION
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-/// TODO: Remove serde dependency
|
|
|
-fn cargo_toml_with_serde(name: &str) -> String {
|
|
|
- format!(
|
|
|
- r#"[package]
|
|
|
-name = "{0}"
|
|
|
-version = "{2}"
|
|
|
-description = "Created with Bolt"
|
|
|
-edition = "2021"
|
|
|
-
|
|
|
-[lib]
|
|
|
-crate-type = ["cdylib", "lib"]
|
|
|
-name = "{1}"
|
|
|
-
|
|
|
-[features]
|
|
|
-no-entrypoint = []
|
|
|
-no-idl = []
|
|
|
-no-log-ix-name = []
|
|
|
-cpi = ["no-entrypoint"]
|
|
|
-default = []
|
|
|
-idl-build = ["anchor-lang/idl-build"]
|
|
|
-
|
|
|
-[dependencies]
|
|
|
-bolt-lang = "{2}"
|
|
|
-anchor-lang = "{3}"
|
|
|
-serde = {{ version = "1.0", features = ["derive"] }}
|
|
|
-"#,
|
|
|
- name,
|
|
|
- name.to_snake_case(),
|
|
|
- VERSION,
|
|
|
- ANCHOR_VERSION
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-fn xargo_toml() -> &'static str {
|
|
|
- r#"[target.bpfel-unknown-unknown.dependencies.std]
|
|
|
-features = []
|
|
|
-"#
|
|
|
-}
|
|
|
-pub fn git_ignore() -> &'static str {
|
|
|
- r#"
|
|
|
-.anchor
|
|
|
-.bolt
|
|
|
-.DS_Store
|
|
|
-target
|
|
|
-**/*.rs.bk
|
|
|
-node_modules
|
|
|
-test-ledger
|
|
|
-.yarn
|
|
|
-"#
|
|
|
-}
|
|
|
-
|
|
|
-pub fn prettier_ignore() -> &'static str {
|
|
|
- r#"
|
|
|
-.anchor
|
|
|
-.bolt
|
|
|
-.DS_Store
|
|
|
-target
|
|
|
-node_modules
|
|
|
-dist
|
|
|
-build
|
|
|
-test-ledger
|
|
|
-"#
|
|
|
-}
|
|
|
-
|
|
|
pub fn registry_account() -> &'static str {
|
|
|
r#"
|
|
|
{
|
|
@@ -604,112 +79,6 @@ pub fn registry_account() -> &'static str {
|
|
|
"#
|
|
|
}
|
|
|
|
|
|
-/// Automatic generation of crates from the components idl
|
|
|
-
|
|
|
-pub fn component_type(idl: &Idl, component_id: &str) -> Result<String> {
|
|
|
- let component_account = idl
|
|
|
- .accounts
|
|
|
- .iter()
|
|
|
- .filter(|a| a.name.to_lowercase() != "Entity")
|
|
|
- .last();
|
|
|
- let component_account =
|
|
|
- component_account.ok_or_else(|| anyhow::anyhow!("Component account not found in IDL"))?;
|
|
|
-
|
|
|
- let type_def = &idl
|
|
|
- .types
|
|
|
- .iter()
|
|
|
- .rfind(|ty| ty.name == component_account.name);
|
|
|
- let type_def = match type_def {
|
|
|
- Some(ty) => ty,
|
|
|
- None => return Err(anyhow::anyhow!("Component type not found in IDL")),
|
|
|
- };
|
|
|
- let component_code = component_to_rust_code(type_def, component_id);
|
|
|
- let types_code = component_types_to_rust_code(&idl.types, &component_account.name);
|
|
|
- Ok(format!(
|
|
|
- r#"use bolt_lang::*;
|
|
|
-
|
|
|
-#[component_deserialize]
|
|
|
-#[derive(Clone, Copy)]
|
|
|
-{}
|
|
|
-
|
|
|
-{}
|
|
|
-"#,
|
|
|
- component_code, types_code
|
|
|
- ))
|
|
|
-}
|
|
|
-
|
|
|
-/// Convert the component type definition to rust code
|
|
|
-fn component_to_rust_code(component: &IdlTypeDef, component_id: &str) -> String {
|
|
|
- let mut code = String::new();
|
|
|
- // Add documentation comments, if any
|
|
|
- for doc in &component.docs {
|
|
|
- code += &format!("/// {}\n", doc);
|
|
|
- }
|
|
|
- // Handle generics
|
|
|
- let generics = {
|
|
|
- let generic_names: Vec<String> = component
|
|
|
- .generics
|
|
|
- .iter()
|
|
|
- .map(|gen| match gen {
|
|
|
- IdlTypeDefGeneric::Type { name } => name.clone(),
|
|
|
- IdlTypeDefGeneric::Const { name, .. } => name.clone(),
|
|
|
- })
|
|
|
- .collect();
|
|
|
- if generic_names.is_empty() {
|
|
|
- "".to_string()
|
|
|
- } else {
|
|
|
- format!("<{}>", generic_names.join(", "))
|
|
|
- }
|
|
|
- };
|
|
|
- let composite_name = format!("Component{}", component_id);
|
|
|
- if let IdlTypeDefTy::Struct { fields } = &component.ty {
|
|
|
- code += &format!("pub struct {}{} {{\n", composite_name, generics);
|
|
|
- code += &*component_fields_to_rust_code(fields);
|
|
|
- code += "}\n\n";
|
|
|
- code += &format!("pub use {} as {};", composite_name, component.name);
|
|
|
- }
|
|
|
- code
|
|
|
-}
|
|
|
-
|
|
|
-/// Code to expose the generated type, to be added to lib.rs
|
|
|
-pub fn component_type_import(component_id: &str) -> String {
|
|
|
- format!(
|
|
|
- r#"#[allow(non_snake_case)]
|
|
|
-mod component_{0};
|
|
|
-pub use component_{0}::*;
|
|
|
-"#,
|
|
|
- component_id,
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-/// Convert fields to rust code
|
|
|
-fn component_fields_to_rust_code(fields: &Option<IdlDefinedFields>) -> String {
|
|
|
- let mut code = String::new();
|
|
|
- if let Some(fields) = fields {
|
|
|
- match fields {
|
|
|
- IdlDefinedFields::Named(named_fields) => {
|
|
|
- for field in named_fields {
|
|
|
- if field.name.to_lowercase() == "bolt_metadata" {
|
|
|
- continue;
|
|
|
- }
|
|
|
- for doc in &field.docs {
|
|
|
- code += &format!(" /// {}\n", doc);
|
|
|
- }
|
|
|
- let field_type = convert_idl_type_to_str(&field.ty);
|
|
|
- code += &format!(" pub {}: {},\n", field.name, field_type);
|
|
|
- }
|
|
|
- }
|
|
|
- IdlDefinedFields::Tuple(tuple_types) => {
|
|
|
- for (index, ty) in tuple_types.iter().enumerate() {
|
|
|
- let field_type = convert_idl_type_to_str(ty);
|
|
|
- code += &format!(" pub field_{}: {},\n", index, field_type);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- code
|
|
|
-}
|
|
|
-
|
|
|
/// Map Idl type to rust type
|
|
|
pub fn convert_idl_type_to_str(ty: &IdlType) -> String {
|
|
|
match ty {
|
|
@@ -760,71 +129,3 @@ pub fn convert_idl_type_to_str(ty: &IdlType) -> String {
|
|
|
_ => unimplemented!("{ty:?}"),
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-/// Convert the component types definition to rust code
|
|
|
-fn component_types_to_rust_code(types: &[IdlTypeDef], component_name: &str) -> String {
|
|
|
- types
|
|
|
- .iter()
|
|
|
- .filter(|ty| ty.name.to_lowercase() != "boltmetadata" && ty.name != component_name)
|
|
|
- .map(component_type_to_rust_code)
|
|
|
- .collect::<Vec<_>>()
|
|
|
- .join("\n")
|
|
|
-}
|
|
|
-
|
|
|
-/// Convert the component type definition to rust code
|
|
|
-fn component_type_to_rust_code(component_type: &IdlTypeDef) -> String {
|
|
|
- let mut code = String::new();
|
|
|
- // Add documentation comments, if any
|
|
|
- for doc in &component_type.docs {
|
|
|
- code += &format!("/// {}\n", doc);
|
|
|
- }
|
|
|
- // Handle generics
|
|
|
- let gen = &component_type.generics;
|
|
|
- let generics = {
|
|
|
- let generic_names: Vec<String> = gen
|
|
|
- .iter()
|
|
|
- .map(|gen| match gen {
|
|
|
- IdlTypeDefGeneric::Type { name } => name.clone(),
|
|
|
- IdlTypeDefGeneric::Const { name, .. } => name.clone(),
|
|
|
- })
|
|
|
- .collect();
|
|
|
- if generic_names.is_empty() {
|
|
|
- "".to_string()
|
|
|
- } else {
|
|
|
- format!("<{}>", generic_names.join(", "))
|
|
|
- }
|
|
|
- };
|
|
|
- if let IdlTypeDefTy::Struct { fields } = &component_type.ty {
|
|
|
- code += &format!(
|
|
|
- "#[component_deserialize]\n#[derive(Clone, Copy)]\npub struct {}{} {{\n",
|
|
|
- component_type.name, generics
|
|
|
- );
|
|
|
- code += &*component_fields_to_rust_code(fields);
|
|
|
- code += "}\n\n";
|
|
|
- }
|
|
|
- code
|
|
|
-}
|
|
|
-
|
|
|
-pub(crate) fn types_cargo_toml() -> String {
|
|
|
- let name = "bolt-types";
|
|
|
- format!(
|
|
|
- r#"[package]
|
|
|
-name = "{0}"
|
|
|
-version = "{2}"
|
|
|
-description = "Autogenerate types for the bolt language"
|
|
|
-edition = "2021"
|
|
|
-
|
|
|
-[lib]
|
|
|
-crate-type = ["cdylib", "lib"]
|
|
|
-name = "{1}"
|
|
|
-
|
|
|
-[dependencies]
|
|
|
-bolt-lang = "{2}"
|
|
|
-anchor-lang = "{3}"
|
|
|
-"#,
|
|
|
- name,
|
|
|
- name.to_snake_case(),
|
|
|
- VERSION,
|
|
|
- ANCHOR_VERSION
|
|
|
- )
|
|
|
-}
|