Skip to content

Commit

Permalink
[Rust]: Codegen tools (#3566)
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan authored Nov 22, 2023
1 parent ea4e1c1 commit b8beaec
Show file tree
Hide file tree
Showing 57 changed files with 2,086 additions and 493 deletions.
82 changes: 80 additions & 2 deletions codegen-v2/Cargo.lock

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

3 changes: 3 additions & 0 deletions codegen-v2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ name = "parser"
path = "src/main.rs"

[dependencies]
convert_case = "0.6.0"
pathdiff = "0.2.1"
serde = { version = "1.0.159", features = ["derive"] }
serde_json = "1.0.95"
serde_yaml = "0.9.21"
toml_edit = "0.21.0"
handlebars = "4.3.6"
heck = "0.4.1"
1 change: 1 addition & 0 deletions codegen-v2/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

pub mod rust;
pub mod swift;
133 changes: 133 additions & 0 deletions codegen-v2/src/codegen/rust/blockchain_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright © 2017-2023 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use crate::codegen::rust::toml_editor::Dependencies;
use crate::codegen::rust::{rust_source_directory, CoinItem};
use crate::utils::FileContent;
use crate::Result;
use std::path::{Path, PathBuf};

const BLOCKCHAIN_TYPE_START: &str = "start_of_blockchain_type";
const BLOCKCHAIN_TYPE_END: &str = "end_of_blockchain_type";
const BLOCKCHAIN_ENTRIES_START: &str = "start_of_blockchain_entries";
const BLOCKCHAIN_ENTRIES_END: &str = "end_of_blockchain_entries";
const BLOCKCHAIN_DISPATCHER_START: &str = "start_of_blockchain_dispatcher";
const BLOCKCHAIN_DISPATCHER_END: &str = "end_of_blockchain_dispatcher";

pub fn coin_registry_directory() -> PathBuf {
rust_source_directory().join("tw_coin_registry")
}

pub fn blockchain_type_path() -> PathBuf {
coin_registry_directory()
.join("src")
.join("blockchain_type.rs")
}

pub fn dispatcher_path() -> PathBuf {
coin_registry_directory().join("src").join("dispatcher.rs")
}

pub struct CoinRegistry {
coin: CoinItem,
}

impl CoinRegistry {
pub fn new(coin: CoinItem) -> CoinRegistry {
CoinRegistry { coin }
}

pub fn add(self, path_to_new_blockchain_crate: &Path) -> Result<()> {
self.add_blockchain_crate_to_manifest_file(path_to_new_blockchain_crate)?;
self.add_blockchain_variant()?;
self.add_use_of_blockchain_entry()?;
self.add_blockchain_entry()?;
self.add_blockchain_dispatcher()
}

fn add_blockchain_crate_to_manifest_file(
&self,
path_to_new_blockchain_crate: &Path,
) -> Result<()> {
let path_to_cargo_manifest = coin_registry_directory().join("Cargo.toml");
Dependencies::new(path_to_cargo_manifest).insert_dependency(
&self.coin.id.to_tw_crate_name(),
path_to_new_blockchain_crate,
)
}

fn add_blockchain_variant(&self) -> Result<()> {
let blockchain_type_rs_path = blockchain_type_path();
let blockchain_type = self.coin.blockchain_type();

let mut blockchain_type_rs = FileContent::read(blockchain_type_rs_path)?;

{
let mut enum_region = blockchain_type_rs
.find_region_with_comments(BLOCKCHAIN_TYPE_START, BLOCKCHAIN_TYPE_END)?;
enum_region.push_line(format!(" {blockchain_type},"));
enum_region.sort();
}

blockchain_type_rs.write()
}

fn add_use_of_blockchain_entry(&self) -> Result<()> {
let dispatcher_rs_path = dispatcher_path();
let blockchain_entry = self.coin.blockchain_entry();
let tw_crate_name = self.coin.id.to_tw_crate_name();

let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?;

{
let import_pattern = "use ";
let mut last_entry = dispatcher_rs.rfind_line(|line| line.contains(import_pattern))?;
last_entry.push_line_after(format!("use {tw_crate_name}::entry::{blockchain_entry};"));
}

dispatcher_rs.write()
}

fn add_blockchain_entry(&self) -> Result<()> {
let dispatcher_rs_path = dispatcher_path();
let blockchain_entry = self.coin.blockchain_entry();
let blockchain_entry_const = self.coin.blockchain_entry_upper_snake();

let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?;

{
let mut entries_region = dispatcher_rs
.find_region_with_comments(BLOCKCHAIN_ENTRIES_START, BLOCKCHAIN_ENTRIES_END)?;
entries_region.push_line(format!(
"const {blockchain_entry_const}: {blockchain_entry} = {blockchain_entry};"
));
entries_region.sort();
}

dispatcher_rs.write()
}

fn add_blockchain_dispatcher(&self) -> Result<()> {
let dispatcher_rs_path = dispatcher_path();
let blockchain_type = self.coin.blockchain_type();
let blockchain_entry_const = self.coin.blockchain_entry_upper_snake();

let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?;

{
let mut dispatcher_region = dispatcher_rs.find_region_with_comments(
BLOCKCHAIN_DISPATCHER_START,
BLOCKCHAIN_DISPATCHER_END,
)?;
dispatcher_region.push_line(format!(
" BlockchainType::{blockchain_type} => Ok(&{blockchain_entry_const}),"
));
dispatcher_region.sort();
}

dispatcher_rs.write()
}
}
88 changes: 88 additions & 0 deletions codegen-v2/src/codegen/rust/coin_crate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright © 2017-2023 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use crate::codegen::rust::coin_id::CoinId;
use crate::codegen::rust::{chains_directory, rs_header, CoinItem};
use crate::{Error, Result};
use std::path::PathBuf;
use std::{fs, io};

pub fn coin_source_directory(id: &CoinId) -> PathBuf {
chains_directory().join(id.to_tw_crate_name())
}

pub struct CoinCrate {
coin: CoinItem,
}

impl CoinCrate {
pub fn new(coin: CoinItem) -> CoinCrate {
CoinCrate { coin }
}

/// Creates a Cargo crate with `entry.rs` file.
/// Returns the path to the create crate.
pub fn create(self) -> Result<PathBuf> {
let header = rs_header();

let blockchain_path = coin_source_directory(&self.coin.id);
let blockchain_toml_path = blockchain_path.join("Cargo.toml");

let blockchain_src_path = blockchain_path.join("src");
let blockchain_lib_rs_path = blockchain_src_path.join("lib.rs");
let blockchain_entry_path = blockchain_src_path.join("entry.rs");

let tw_crate_name = self.coin.id.to_tw_crate_name();
let blockchain_name = self.coin.blockchain_type();

if blockchain_path.exists() {
return Err(Error::IoError(io::Error::new(
io::ErrorKind::AlreadyExists,
"blockchain already exists",
)));
}

fs::create_dir(&blockchain_path)?;
fs::create_dir(&blockchain_src_path)?;

let blockchain_toml = format!(
r#"[package]
name = "{tw_crate_name}"
version = "0.1.0"
edition = "2021"
[dependencies]
tw_coin_entry = {{ path = "../../tw_coin_entry" }}
tw_proto = {{ path = "../../tw_proto" }}
"#
);
fs::write(blockchain_toml_path, blockchain_toml)?;

let blockchain_lib_rs = format!(
r#"{header}
pub mod entry;
"#
);
fs::write(blockchain_lib_rs_path, blockchain_lib_rs)?;

let blockchain_entry = format!(
r#"{header}
use tw_coin_entry::coin_entry::CoinEntry;
pub struct {blockchain_name}Entry;
impl CoinEntry for {blockchain_name}Entry {{
// TODO declare associated types and implement methods from the 'CoinEntry' trait.
}}
"#
);
fs::write(blockchain_entry_path, blockchain_entry)?;

Ok(blockchain_path)
}
}
Loading

0 comments on commit b8beaec

Please sign in to comment.