Skip to content

Commit

Permalink
[aptos-cli] Standardize CLI commands
Browse files Browse the repository at this point in the history
By using a trait now all commands are forced to have
the same format, and naming, leaving it easy to have
a single command to do all the serialization and
sending of metrics.
  • Loading branch information
gregnazario authored and aptos-bot committed Apr 28, 2022
1 parent bc8029e commit cf68bb9
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 65 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.

1 change: 1 addition & 0 deletions crates/aptos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ build = "build.rs"

[dependencies]
anyhow = "1.0.52"
async-trait = "0.1.42"
base64 = "0.13.0"
clap = "3.1.8"
hex = "0.4.3"
Expand Down
16 changes: 12 additions & 4 deletions crates/aptos/src/account/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
use crate::common::{
init::DEFAULT_FAUCET_URL,
types::{
account_address_from_public_key, CliConfig, CliError, CliTypedResult, EncodingOptions,
ProfileOptions, WriteTransactionOptions,
account_address_from_public_key, CliCommand, CliConfig, CliError, CliTypedResult,
EncodingOptions, ProfileOptions, WriteTransactionOptions,
},
};
use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey};
use aptos_rest_client::{Client as RestClient, Response, Transaction};
use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount};
use aptos_transaction_builder::aptos_stdlib;
use aptos_types::account_address::AccountAddress;
use async_trait::async_trait;
use clap::Parser;
use reqwest::Url;

Expand Down Expand Up @@ -40,8 +41,13 @@ pub struct CreateAccount {
initial_coins: u64,
}

impl CreateAccount {
pub async fn execute(self) -> CliTypedResult<String> {
#[async_trait]
impl CliCommand<String> for CreateAccount {
fn command_name(&self) -> &'static str {
"CreateAccount"
}

async fn execute(self) -> CliTypedResult<String> {
let address = self.account;
if self.use_faucet {
let faucet_url = if let Some(faucet_url) = self.faucet_url {
Expand All @@ -63,7 +69,9 @@ impl CreateAccount {
}
.map(|_| format!("Account Created at {}", address))
}
}

impl CreateAccount {
async fn post_account(
&self,
address: AccountAddress,
Expand Down
14 changes: 11 additions & 3 deletions crates/aptos/src/account/list.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::common::types::{CliConfig, CliError, CliTypedResult, ProfileOptions, RestOptions};
use crate::common::types::{
CliCommand, CliConfig, CliError, CliTypedResult, ProfileOptions, RestOptions,
};
use aptos_rest_client::{types::Resource, Client};
use aptos_types::account_address::AccountAddress;
use async_trait::async_trait;
use clap::Parser;

/// Command to list resources owned by an address
Expand All @@ -21,10 +24,15 @@ pub struct ListResources {
account: Option<AccountAddress>,
}

impl ListResources {
#[async_trait]
impl CliCommand<Vec<serde_json::Value>> for ListResources {
fn command_name(&self) -> &'static str {
"ListResources"
}

// TODO: Format this in a reasonable way while providing all information
// add options like --tokens --nfts etc
pub(crate) async fn execute(self) -> CliTypedResult<Vec<serde_json::Value>> {
async fn execute(self) -> CliTypedResult<Vec<serde_json::Value>> {
let account = if let Some(account) = self.account {
account
} else if let Some(Some(account)) =
Expand Down
16 changes: 6 additions & 10 deletions crates/aptos/src/account/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::common::{types::CliResult, utils::to_common_result};
use crate::common::{
types::{CliCommand, CliResult},
};
use clap::Subcommand;

pub mod create;
Expand All @@ -20,15 +22,9 @@ pub enum AccountTool {
impl AccountTool {
pub async fn execute(self) -> CliResult {
match self {
AccountTool::Create(tool) => {
to_common_result("CreateAccount", tool.execute().await).await
}
AccountTool::List(tool) => {
to_common_result("ListResources", tool.execute().await).await
}
AccountTool::Transfer(tool) => {
to_common_result("TransferCoins", tool.execute().await).await
}
AccountTool::Create(tool) => tool.execute_serialized().await,
AccountTool::List(tool) => tool.execute_serialized().await,
AccountTool::Transfer(tool) => tool.execute_serialized().await,
}
}
}
14 changes: 10 additions & 4 deletions crates/aptos/src/account/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@

use crate::common::{
types::{
account_address_from_public_key, CliError, CliTypedResult, EncodingOptions, ProfileOptions,
WriteTransactionOptions,
account_address_from_public_key, CliCommand, CliError, CliTypedResult, EncodingOptions,
ProfileOptions, WriteTransactionOptions,
},
utils::get_sequence_number,
};
use aptos_crypto::PrivateKey;
use aptos_rest_client::Transaction;
use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount};
use aptos_types::account_address::AccountAddress;
use async_trait::async_trait;
use cached_framework_packages::aptos_stdlib;
use clap::Parser;

Expand All @@ -37,8 +38,13 @@ pub struct TransferCoins {
amount: u64,
}

impl TransferCoins {
pub(crate) async fn execute(self) -> CliTypedResult<Transaction> {
#[async_trait]
impl CliCommand<Transaction> for TransferCoins {
fn command_name(&self) -> &'static str {
"TransferCoins"
}

async fn execute(self) -> CliTypedResult<Transaction> {
let client = aptos_rest_client::Client::new(reqwest::Url::clone(
&self
.write_options
Expand Down
14 changes: 10 additions & 4 deletions crates/aptos/src/common/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ use crate::{
account::create::CreateAccount,
common::{
types::{
account_address_from_public_key, CliConfig, CliError, CliTypedResult, ProfileConfig,
ProfileOptions,
account_address_from_public_key, CliCommand, CliConfig, CliError, CliTypedResult,
ProfileConfig, ProfileOptions,
},
utils::prompt_yes,
},
op::key::GenerateKey,
};
use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, ValidCryptoMaterialStringExt};
use async_trait::async_trait;
use clap::Parser;
use std::collections::HashMap;

Expand All @@ -27,8 +28,13 @@ pub struct InitTool {
profile_options: ProfileOptions,
}

impl InitTool {
pub async fn execute(self) -> CliTypedResult<()> {
#[async_trait]
impl CliCommand<()> for InitTool {
fn command_name(&self) -> &'static str {
"AptosInit"
}

async fn execute(self) -> CliTypedResult<()> {
let mut config = if CliConfig::config_exists()? {
CliConfig::load()?
} else {
Expand Down
26 changes: 25 additions & 1 deletion crates/aptos/src/common/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use crate::common::{
init::DEFAULT_REST_URL,
utils::{check_if_file_exists, write_to_file},
utils::{check_if_file_exists, to_common_result, write_to_file},
};
use aptos_crypto::{
ed25519::{Ed25519PrivateKey, Ed25519PublicKey},
Expand All @@ -12,6 +12,7 @@ use aptos_crypto::{
use aptos_logger::debug;
use aptos_rest_client::Client;
use aptos_types::{chain_id::ChainId, transaction::authenticator::AuthenticationKey};
use async_trait::async_trait;
use clap::{ArgEnum, Parser};
use move_core_types::account_address::AccountAddress;
use serde::{Deserialize, Serialize};
Expand All @@ -22,6 +23,7 @@ use std::{
str::FromStr,
};
use thiserror::Error;
use crate::common::utils::to_common_success_result;

/// A common result to be returned to users
pub type CliResult = Result<String, String>;
Expand Down Expand Up @@ -533,3 +535,25 @@ pub fn load_account_arg(str: &str) -> Result<AccountAddress, CliError> {
))
}
}

/// A common trait for all CLI commands to have consistent outputs
#[async_trait]
pub trait CliCommand<T: Serialize + Send>: Sized + Send {
/// Returns a name for logging purposes
fn command_name(&self) -> &'static str;

/// Executes the command, returning a command specific type
async fn execute(self) -> CliTypedResult<T>;

/// Executes the command, and serializes it to the common JSON output type
async fn execute_serialized(self) -> CliResult {
let command_name = self.command_name();
to_common_result(command_name, self.execute().await).await
}

/// Executes the command, and throws away Ok(result) for the string Success
async fn execute_serialized_success(self) -> CliResult {
let command_name = self.command_name();
to_common_success_result(command_name, self.execute().await).await
}
}
6 changes: 3 additions & 3 deletions crates/aptos/src/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ pub fn prompt_yes(prompt: &str) -> bool {
result.unwrap()
}

/// Convert an empty response to Success
pub async fn to_common_success_result(command: &str, result: CliTypedResult<()>) -> CliResult {
to_common_result(command, result.map(|()| "Success")).await
/// Convert any successful response to Success
pub async fn to_common_success_result<T>(command: &str, result: CliTypedResult<T>) -> CliResult {
to_common_result(command, result.map(|_| "Success")).await
}

/// For pretty printing outputs in JSON
Expand Down
6 changes: 4 additions & 2 deletions crates/aptos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ pub mod common;
pub mod move_tool;
pub mod op;

use crate::common::{types::CliResult, utils::to_common_success_result};
use crate::common::{
types::{CliCommand, CliResult},
};
use clap::Parser;

/// CLI tool for interacting with the Aptos blockchain and nodes
Expand All @@ -29,7 +31,7 @@ impl Tool {
pub async fn execute(self) -> CliResult {
match self {
Tool::Account(tool) => tool.execute().await,
Tool::Init(tool) => to_common_success_result("AptosInit", tool.execute().await).await,
Tool::Init(tool) => tool.execute_serialized_success().await,
Tool::Move(tool) => tool.execute().await,
Tool::Key(tool) => tool.execute().await,
}
Expand Down
52 changes: 34 additions & 18 deletions crates/aptos/src/move_tool/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use async_trait::async_trait;
use crate::{
common::{
types::{
load_account_arg, CliError, CliTypedResult, EncodingOptions, MovePackageDir,
ProfileOptions, WriteTransactionOptions,
},
utils::to_common_result,
},
CliResult,
CliCommand, CliResult,
};
use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey};
use aptos_rest_client::{aptos_api_types::MoveType, Client, Transaction};
Expand Down Expand Up @@ -46,14 +46,10 @@ pub enum MoveTool {
impl MoveTool {
pub async fn execute(self) -> CliResult {
match self {
MoveTool::Compile(tool) => {
to_common_result("CompilePackage", tool.execute().await).await
}
MoveTool::Publish(tool) => {
to_common_result("PublishPackage", tool.execute().await).await
}
MoveTool::Run(tool) => to_common_result("RunFunction", tool.execute().await).await,
MoveTool::Test(tool) => to_common_result("TestPackage", tool.execute().await).await,
MoveTool::Compile(tool) => tool.execute_serialized().await,
MoveTool::Publish(tool) => tool.execute_serialized().await,
MoveTool::Run(tool) => tool.execute_serialized().await,
MoveTool::Test(tool) => tool.execute_serialized().await,
}
}
}
Expand All @@ -65,8 +61,13 @@ pub struct CompilePackage {
move_options: MovePackageDir,
}

impl CompilePackage {
pub async fn execute(self) -> CliTypedResult<Vec<String>> {
#[async_trait]
impl CliCommand<Vec<String>> for CompilePackage {
fn command_name(&self) -> &'static str {
"CompilePackage"
}

async fn execute(self) -> CliTypedResult<Vec<String>> {
let build_config = BuildConfig {
additional_named_addresses: self.move_options.named_addresses(),
generate_docs: true,
Expand All @@ -91,8 +92,13 @@ pub struct TestPackage {
move_options: MovePackageDir,
}

impl TestPackage {
pub async fn execute(self) -> CliTypedResult<&'static str> {
#[async_trait]
impl CliCommand<&'static str> for TestPackage {
fn command_name(&self) -> &'static str {
"TestPackage"
}

async fn execute(self) -> CliTypedResult<&'static str> {
let config = BuildConfig {
additional_named_addresses: self.move_options.named_addresses(),
test_mode: true,
Expand Down Expand Up @@ -137,8 +143,13 @@ pub struct PublishPackage {
profile_options: ProfileOptions,
}

impl PublishPackage {
pub async fn execute(self) -> CliTypedResult<aptos_rest_client::Transaction> {
#[async_trait]
impl CliCommand<aptos_rest_client::Transaction> for PublishPackage {
fn command_name(&self) -> &'static str {
"PublishPackage"
}

async fn execute(self) -> CliTypedResult<aptos_rest_client::Transaction> {
let build_config = BuildConfig {
additional_named_addresses: self.move_options.named_addresses(),
generate_abis: false,
Expand Down Expand Up @@ -237,8 +248,13 @@ pub struct RunFunction {
type_args: Vec<MoveType>,
}

impl RunFunction {
pub async fn execute(self) -> Result<Transaction, CliError> {
#[async_trait]
impl CliCommand<Transaction> for RunFunction {
fn command_name(&self) -> &'static str {
"RunFunction"
}

async fn execute(self) -> CliTypedResult<Transaction> {
let args: Vec<Vec<u8>> = self
.args
.iter()
Expand Down
Loading

0 comments on commit cf68bb9

Please sign in to comment.