Skip to content

Commit

Permalink
Add schema generation functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
kpob committed Jan 19, 2024
1 parent d249b6d commit 9cb2426
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 68 deletions.
2 changes: 2 additions & 0 deletions src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ pub mod build;
pub mod clean;
pub mod generate;
pub mod init;
pub mod schema;
pub mod test;
pub mod update;
mod utils;
86 changes: 18 additions & 68 deletions src/actions/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Module for managing and building backends.
use crate::{command, errors::Error, log, odra_toml::Contract, paths, project::Project};
use super::utils;
use crate::{command, log, paths, project::Project};

/// BuildAction configuration.
pub struct BuildAction<'a> {
Expand All @@ -22,57 +23,23 @@ impl<'a> BuildAction<'a> {
impl BuildAction<'_> {
/// Main function that runs the whole workflow for a backend.
pub fn build(&self) {
self.check_target_requirements();
self.validate_contract_name_argument();
utils::check_target_requirements();
utils::validate_contract_name_argument(self.project, self.contracts_names());
self.build_wasm_files();
self.optimize_wasm_files();
}

/// Returns list of contract to process.
fn contracts(&self) -> Vec<Contract> {
let names = self.parse_contracts_names();
let odra_toml = self.project.odra_toml();
match names.is_empty() {
true => odra_toml.contracts,
false => odra_toml
.contracts
.into_iter()
.filter(|c| names.contains(&c.struct_name()))
.collect(),
}
}

/// Check if wasm32-unknown-unknown target is installed.
fn check_target_requirements(&self) {
if !command::command_output("rustup target list --installed")
.contains("wasm32-unknown-unknown")
{
Error::WasmTargetNotInstalled.print_and_die();
}
}

/// Check if contract name argument is valid if set.
fn validate_contract_name_argument(&self) {
let names = self.parse_contracts_names();
names.iter().for_each(|contract_name| {
if !self
.project
.odra_toml()
.contracts
.iter()
.any(|c| c.struct_name() == *contract_name)
{
Error::ContractNotFound(contract_name.clone()).print_and_die();
}
});
}

/// Build .wasm files.
fn build_wasm_files(&self) {
log::info("Generating wasm files...");
command::mkdir(paths::wasm_dir(self.project.project_root()));

for contract in self.contracts() {
let contracts =
utils::contracts(self.project, self.contracts_names()).unwrap_or_else(|_| {
Error::FailedToParseArgument("contracts_names".to_string()).print_and_die()
});

for contract in contracts {
let build_contract = format!("{}_build_contract", &contract.module_name());
command::cargo_build_wasm_files(
self.project.project_root(),
Expand Down Expand Up @@ -103,7 +70,12 @@ impl BuildAction<'_> {
/// Run wasm-strip on *.wasm files in wasm directory.
fn optimize_wasm_files(&self) {
log::info("Optimizing wasm files...");
for contract in self.contracts() {
let contracts =
utils::contracts(self.project, self.contracts_names()).unwrap_or_else(|_| {
Error::FailedToParseArgument("contracts_names".to_string()).print_and_die()
});

for contract in contracts {
command::wasm_strip(&contract.struct_name(), self.project.project_root());
if self.project.is_workspace() {
command::wasm_strip(
Expand All @@ -114,29 +86,7 @@ impl BuildAction<'_> {
}
}

fn parse_contracts_names(&self) -> Vec<String> {
match &self.contracts_names {
Some(string) => remove_extra_spaces(string)
.map(|string| {
string
.split(' ')
.map(ToString::to_string)
.collect::<Vec<_>>()
})
.unwrap_or_else(|_| {
Error::FailedToParseArgument("contracts_names".to_string()).print_and_die()
}),
None => vec![],
}
}
}

fn remove_extra_spaces(input: &str) -> Result<String, &'static str> {
// Ensure there are no other separators
if input.chars().any(|c| c.is_whitespace() && c != ' ') {
return Err("Input contains non-space whitespace characters");
fn contracts_names(&self) -> String {
self.contracts_names.clone().unwrap_or_default()
}

let trimmed = input.split_whitespace().collect::<Vec<&str>>().join(" ");
Ok(trimmed)
}
49 changes: 49 additions & 0 deletions src/actions/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! Module for generating contracts schema.
use super::utils;
use crate::{command, log, project::Project};

/// SchemaAction configuration.
pub struct SchemaAction<'a> {
project: &'a Project,
contracts_names: Option<String>,
}

impl<'a> SchemaAction<'a> {
/// Crate a new SchemaAction for a given configuration.
pub fn new(project: &'a Project, contracts_names: Option<String>) -> Self {
SchemaAction {
project,
contracts_names,
}
}
}

impl SchemaAction<'_> {
/// Main function that runs the whole workflow.
pub fn build(&self) {
utils::check_target_requirements();
utils::validate_contract_name_argument(self.project, self.contracts_names());
self.generate_schema_files();
}

/// Generates *_schema.json files.
fn generate_schema_files(&self) {
log::info("Generating schema files...");
let contracts =
utils::contracts(self.project, self.contracts_names()).unwrap_or_else(|_| {
Error::FailedToParseArgument("contracts_names".to_string()).print_and_die()
});
for contract in contracts {
command::cargo_generate_schema_files(
self.project.project_root(),
&contract.struct_name(),
&contract.module_name(),
);
}
}

fn contracts_names(&self) -> String {
self.contracts_names.clone().unwrap_or_default()
}
}
60 changes: 60 additions & 0 deletions src/actions/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crate::{command, errors::Error, odra_toml::Contract, project::Project};

/// Check if wasm32-unknown-unknown target is installed.
pub fn check_target_requirements() {
if !command::command_output("rustup target list --installed").contains("wasm32-unknown-unknown")
{
Error::WasmTargetNotInstalled.print_and_die();
}
}

/// Returns list of contract to process.
pub fn contracts(project: &Project, names_string: String) -> Result<Vec<Contract>, &'static str> {
let names = parse_contracts_names(names_string)?;
let odra_toml = project.odra_toml();
Ok(match names.is_empty() {
true => odra_toml.contracts,
false => odra_toml
.contracts
.into_iter()
.filter(|c| names.contains(&c.struct_name()))
.collect(),
})
}

/// Check if contract name argument is valid if set.
pub fn validate_contract_name_argument(project: &Project, names_string: String) {
let names = parse_contracts_names(names_string);
names.iter().for_each(|contract_name| {
if !project
.odra_toml()
.contracts
.iter()
.any(|c| c.struct_name() == *contract_name)
{
Error::ContractNotFound(contract_name.clone()).print_and_die();
}
});
}

fn remove_extra_spaces(input: &str) -> Result<String, &'static str> {
// Ensure there are no other separators
if input.chars().any(|c| c.is_whitespace() && c != ' ') {
return Err("Input contains non-space whitespace characters");
}

let trimmed = input.split_whitespace().collect::<Vec<&str>>().join(" ");
Ok(trimmed)
}

fn parse_contracts_names(names_string: String) -> Result<Vec<String>, &'static str> {
match names_string.is_empty() {
true => Ok(vec![]),
false => remove_extra_spaces(&names_string).map(|string| {
string
.split(' ')
.map(ToString::to_string)
.collect::<Vec<_>>()
}),
}
}
15 changes: 15 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
clean::clean_action,
generate::GenerateAction,
init::InitAction,
schema::SchemaAction,
test::TestAction,
update::update_action,
},
Expand Down Expand Up @@ -51,6 +52,8 @@ pub enum OdraSubcommand {
Init(InitCommand),
/// Builds the project, including backend and producing wasm files.
Build(BuildCommand),
/// Generates schema for a given contract.
Schema(SchemaCommand),
/// Runs test. Without the backend parameter, the tests will be run against Mock VM.
Test(TestCommand),
/// Generates boilerplate code for contracts.
Expand Down Expand Up @@ -94,6 +97,14 @@ pub struct BuildCommand {
pub contracts_names: Option<String>,
}

#[derive(clap::Args)]
/// `cargo odra schema`
pub struct SchemaCommand {
/// Contracts names separated by a space that matches the names in Odra.toml.
#[clap(value_parser, long, short)]
pub contracts_names: Option<String>,
}

#[derive(clap::Args, Debug)]
/// `cargo odra test`
pub struct TestCommand {
Expand Down Expand Up @@ -167,5 +178,9 @@ pub fn make_action() {
OdraSubcommand::Completions { shell } => {
shell.generate(&mut Cargo::command(), &mut std::io::stdout());
}
OdraSubcommand::Schema(schema) => {
let project = Project::detect(current_dir);
SchemaAction::new(&project, schema.contracts_names).build();
}
}
}
7 changes: 7 additions & 0 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ pub fn cargo_build_wasm_files(current_dir: PathBuf, contract_name: &str, module_
);
}

/// Build schema files.
pub fn cargo_generate_schema_files(current_dir: PathBuf, contract_name: &str, module_name: &str) {
env::set_var(ODRA_MODULE_ENV_KEY, contract_name);
let gen_schema = format!("{}_build_schema", module_name);
cargo(current_dir, "run", vec!["--bin", &gen_schema, "--release"]);
}

/// Update a cargo module.
pub fn cargo_update(current_dir: PathBuf) {
cargo(current_dir, "update", vec![]);
Expand Down

0 comments on commit 9cb2426

Please sign in to comment.