diff --git a/.gitignore b/.gitignore index bd8e40101..875665674 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +/test* .DS_Store near-cli *.wasm diff --git a/Cargo.lock b/Cargo.lock index 82f92bc4d..53460d1ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1442,6 +1442,30 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "interactive_clap" +version = "0.1.0" +source = "git+https://github.com/FroVolod/interactive-clap?rev=89b5bee74035b0ba6d8603fbd9fc3da0ad31eaeb#89b5bee74035b0ba6d8603fbd9fc3da0ad31eaeb" + +[[package]] +name = "interactive_clap" +version = "0.1.0" +source = "git+https://github.com/FroVolod/interactive-clap#89b5bee74035b0ba6d8603fbd9fc3da0ad31eaeb" + +[[package]] +name = "interactive_clap_derive" +version = "0.1.0" +source = "git+https://github.com/FroVolod/interactive-clap-derive?rev=3531e842bdf53ccb82ac261f0eb7535bbd7126ed#3531e842bdf53ccb82ac261f0eb7535bbd7126ed" +dependencies = [ + "clap", + "clap_generate", + "interactive_clap 0.1.0 (git+https://github.com/FroVolod/interactive-clap)", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "itoa" version = "0.4.8" @@ -1688,6 +1712,8 @@ dependencies = [ "dirs", "ed25519-dalek", "hex 0.4.3", + "interactive_clap 0.1.0 (git+https://github.com/FroVolod/interactive-clap?rev=89b5bee74035b0ba6d8603fbd9fc3da0ad31eaeb)", + "interactive_clap_derive", "near-crypto", "near-jsonrpc-client", "near-jsonrpc-primitives", diff --git a/Cargo.toml b/Cargo.toml index 969f28440..95de30dfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,9 @@ near-primitives = { git = "https://github.com/near/nearcore", rev="6f2a290227f4f near-jsonrpc-client = { git = "https://github.com/near/nearcore", rev="6f2a290227f4f3b41c543b615231e769e438e28c" } near-jsonrpc-primitives = { git = "https://github.com/near/nearcore", rev="6f2a290227f4f3b41c543b615231e769e438e28c" } +interactive_clap = { git = "https://github.com/FroVolod/interactive-clap", rev="89b5bee74035b0ba6d8603fbd9fc3da0ad31eaeb" } +interactive_clap_derive = { git = "https://github.com/FroVolod/interactive-clap-derive", rev="3531e842bdf53ccb82ac261f0eb7535bbd7126ed" } + [features] default = ["ledger"] ledger = ["near-ledger"] diff --git a/src/commands/add_command/access_key/operation_mode/mod.rs b/src/commands/add_command/access_key/operation_mode/mod.rs index 2a3b9a12f..93cfe21e9 100644 --- a/src/commands/add_command/access_key/operation_mode/mod.rs +++ b/src/commands/add_command/access_key/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { #[strum_discriminants(strum(message = "Yes, I keep it simple"))] + /// Prepare and, optionally, submit a new transaction with online mode Network(self::online_mode::NetworkArgs), #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] + /// Prepare and, optionally, submit a new transaction with offline mode Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct AddAccessKeyCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/add_command/access_key/operation_mode/offline_mode/mod.rs b/src/commands/add_command/access_key/operation_mode/offline_mode/mod.rs index 775982073..50d59c48b 100644 --- a/src/commands/add_command/access_key/operation_mode/offline_mode/mod.rs +++ b/src/commands/add_command/access_key/operation_mode/offline_mode/mod.rs @@ -1,56 +1,38 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + ///Specify a sender + account: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::AddAccessKeyCommandNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.account .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/add_command/access_key/operation_mode/online_mode/mod.rs b/src/commands/add_command/access_key/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/add_command/access_key/operation_mode/online_mode/mod.rs +++ b/src/commands/add_command/access_key/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/add_command/access_key/operation_mode/online_mode/select_server/mod.rs b/src/commands/add_command/access_key/operation_mode/online_mode/select_server/mod.rs index 838909edd..2db25114e 100644 --- a/src/commands/add_command/access_key/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/add_command/access_key/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,86 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { #[strum_discriminants(strum(message = "Testnet"))] + /// предоставление данных для сервера https://rpc.testnet.near.org Testnet(self::server::Server), #[strum_discriminants(strum(message = "Mainnet"))] + /// предоставление данных для сервера https://rpc.mainnet.near.org Mainnet(self::server::Server), #[strum_discriminants(strum(message = "Betanet"))] + /// предоставление данных для сервера https://rpc.betanet.near.org Betanet(self::server::Server), #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + /// предоставление данных для сервера, указанного вручную + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::AddAccessKeyCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/add_command/access_key/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/add_command/access_key/operation_mode/online_mode/select_server/server/mod.rs index 469d4253b..969982fc3 100644 --- a/src/commands/add_command/access_key/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/add_command/access_key/operation_mode/online_mode/select_server/server/mod.rs @@ -1,121 +1,53 @@ -use std::str::FromStr; - use dialoguer::Input; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a sender + pub account: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::AddAccessKeyCommandNetworkContext)] +pub struct CustomServer { + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a sender + pub account: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::AddAccessKeyCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { - url: url.inner.clone(), - }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone())?, - None => SendFrom::choose_send_from(connection_config.clone())?, - }; - Ok(Server { - connection_config, - send_from, - }) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } } @@ -123,75 +55,22 @@ impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.account + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a sender - Account(super::super::super::super::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Account(super::super::super::super::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Account(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Account(sender) => Self::Account(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Account(cli_sender) => Ok(Self::Account( - super::super::super::super::sender::Sender::from(cli_sender, connection_config)?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Self::from(CliSendFrom::Account(Default::default()), connection_config) - } - +impl CustomServer { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::Account(sender) => { - sender - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.account + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/add_command/access_key/public_key_mode/add_access_key/full_access_type/mod.rs b/src/commands/add_command/access_key/public_key_mode/add_access_key/full_access_type/mod.rs index 9e6209a63..b830bb3d5 100644 --- a/src/commands/add_command/access_key/public_key_mode/add_access_key/full_access_type/mod.rs +++ b/src/commands/add_command/access_key/public_key_mode/add_access_key/full_access_type/mod.rs @@ -1,64 +1,20 @@ -/// данные для определения ключа с полным доступом -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliFullAccessType { - #[clap(subcommand)] - sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct FullAccessType { + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliFullAccessType { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliFullAccessType { - fn from(full_access_type: FullAccessType) -> Self { - Self { - sign_option: Some(full_access_type.sign_option.into()), - } - } -} - -impl FullAccessType { - pub fn from( - item: CliFullAccessType, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config,sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config,sender_account_id)?, - }; - Ok(Self { sign_option }) - } -} - impl FullAccessType { pub async fn process( self, - nonce: near_primitives::types::Nonce, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, public_key: near_crypto::PublicKey, ) -> crate::CliResult { let access_key: near_primitives::account::AccessKey = near_primitives::account::AccessKey { - nonce, + nonce: 0, permission: near_primitives::account::AccessKeyPermission::FullAccess, }; let action = near_primitives::transaction::Action::AddKey( diff --git a/src/commands/add_command/access_key/public_key_mode/add_access_key/function_call_type/mod.rs b/src/commands/add_command/access_key/public_key_mode/add_access_key/function_call_type/mod.rs index 3853bf482..9c1c8b71a 100644 --- a/src/commands/add_command/access_key/public_key_mode/add_access_key/function_call_type/mod.rs +++ b/src/commands/add_command/access_key/public_key_mode/add_access_key/function_call_type/mod.rs @@ -1,104 +1,74 @@ -use dialoguer::{console::Term, theme::ColorfulTheme, Input, Select}; +use std::str::FromStr; -/// данные для определения ключа с function call -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliFunctionCallType { - #[clap(long)] - allowance: Option, - #[clap(long)] - receiver_id: Option, - #[clap(long)] - method_names: Option, - #[clap(subcommand)] - sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} +use dialoguer::{console::Term, theme::ColorfulTheme, Input, Select}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] +#[interactive_clap(skip_default_from_cli)] pub struct FunctionCallType { - pub allowance: Option, - pub receiver_id: near_primitives::types::AccountId, - pub method_names: Vec, + #[interactive_clap(long)] + pub allowance: Option, + #[interactive_clap(long)] + pub receiver_account_id: crate::types::account_id::AccountId, + #[interactive_clap(long)] + pub method_names: crate::types::vec_string::VecString, + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliFunctionCallType { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(method_names) = &self.method_names { - args.push_front(method_names.to_string()); - args.push_front("--method-names".to_owned()) - }; - if let Some(allowance) = &self.allowance { - args.push_front(allowance.to_string()); - args.push_front("--allowance".to_owned()) - }; - if let Some(receiver_id) = &self.receiver_id { - args.push_front(receiver_id.to_string()); - args.push_front("--receiver-id".to_owned()) - }; - args - } -} - -impl From for CliFunctionCallType { - fn from(function_call_type: FunctionCallType) -> Self { - Self { - allowance: Some(crate::common::NearBalance::from_yoctonear( - function_call_type.allowance.unwrap_or_default(), - )), - receiver_id: Some(function_call_type.receiver_id), - method_names: Some(function_call_type.method_names.join(", ")), - sign_option: Some(function_call_type.sign_option.into()), - } - } -} - impl FunctionCallType { - pub fn from( - item: CliFunctionCallType, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let allowance: Option = match item.allowance { - Some(cli_allowance) => Some(cli_allowance.to_yoctonear()), - None => FunctionCallType::input_allowance(), + let connection_config = context.connection_config.clone(); + let allowance: Option = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.allowance) + { + Some(cli_allowance) => Some(cli_allowance), + None => FunctionCallType::input_allowance(&context)?, }; - let receiver_id: near_primitives::types::AccountId = match item.receiver_id { - Some(cli_receiver_id) => near_primitives::types::AccountId::from(cli_receiver_id), - None => FunctionCallType::input_receiver_id(), + let receiver_account_id = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.receiver_account_id) + { + Some(receiver_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::get_account_state( + &network_connection_config, + receiver_account_id.clone().into(), + )? { + Some(_) => receiver_account_id, + None => { + println!("Account <{}> doesn't exist", receiver_account_id); + Self::input_receiver_account_id(&context)? + } + }, + None => receiver_account_id, + }, + None => Self::input_receiver_account_id(&context)?, }; - let method_names: Vec = match item.method_names { + let method_names: crate::types::vec_string::VecString = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.method_names) + { Some(cli_method_names) => { - if cli_method_names.is_empty() { - vec![] + if cli_method_names.0.is_empty() { + crate::types::vec_string::VecString(vec![]) } else { cli_method_names - .split(',') - .map(|s| s.trim().to_string()) - .collect::>() } } - None => FunctionCallType::input_method_names(), + None => FunctionCallType::input_method_names(&context)?, }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, + let sign_option = match optional_clap_variant.and_then(|clap_variant| clap_variant.sign_option) { + Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from_cli(Some(cli_sign_transaction), context)?, + None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_variant(context)?, }; Ok(Self { allowance, - receiver_id, + receiver_account_id, method_names, sign_option, }) @@ -106,7 +76,9 @@ impl FunctionCallType { } impl FunctionCallType { - pub fn input_method_names() -> Vec { + pub fn input_method_names( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); let choose_input = vec![ "Yes, I want to input a list of method names that can be used", @@ -116,32 +88,30 @@ impl FunctionCallType { .with_prompt("Do You want to input a list of method names that can be used") .items(&choose_input) .default(0) - .interact_on_opt(&Term::stderr()) - .unwrap(); + .interact_on_opt(&Term::stderr())?; match select_choose_input { Some(0) => { let mut input_method_names: String = Input::new() .with_prompt("Enter a comma-separated list of method names that will be allowed to be called in a transaction signed by this access key.") .interact_text() - .unwrap(); + ?; if input_method_names.contains("\"") { input_method_names.clear() }; if input_method_names.is_empty() { - vec![] + Ok(crate::types::vec_string::VecString(vec![])) } else { - input_method_names - .split(',') - .map(|s| s.trim().to_string()) - .collect::>() + crate::types::vec_string::VecString::from_str(&input_method_names) } } - Some(1) => vec![], + Some(1) => Ok(crate::types::vec_string::VecString(vec![])), _ => unreachable!("Error"), } } - pub fn input_allowance() -> Option { + pub fn input_allowance( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result> { println!(); let choose_input = vec![ "Yes, I want to input allowance for receiver ID", @@ -151,43 +121,65 @@ impl FunctionCallType { .with_prompt("Do You want to input an allowance for receiver ID") .items(&choose_input) .default(0) - .interact_on_opt(&Term::stderr()) - .unwrap(); + .interact_on_opt(&Term::stderr())?; match select_choose_input { Some(0) => { let allowance_near_balance: crate::common::NearBalance = Input::new() .with_prompt("Enter an allowance which is a balance limit to use by this access key to pay for function call gas and transaction fees.") .interact_text() - .unwrap(); - Some(allowance_near_balance.to_yoctonear()) + ?; + Ok(Some(allowance_near_balance)) } - Some(1) => None, + Some(1) => Ok(None), _ => unreachable!("Error"), } } - pub fn input_receiver_id() -> near_primitives::types::AccountId { - println!(); - Input::new() - .with_prompt("Enter a receiver to use by this access key to pay for function call gas and transaction fees.") - .interact_text() - .unwrap() + pub fn input_receiver_account_id( + context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + loop { + let account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("Enter a receiver to use by this access key to pay for function call gas and transaction fees.") + .interact_text() + ?; + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::get_account_state(&connection_config, account_id.clone().into())? + { + break Ok(account_id); + } else { + if !crate::common::is_64_len_hex(&account_id) { + println!("Account <{}> doesn't exist", account_id.to_string()); + } else { + break Ok(account_id); + } + } + } else { + break Ok(account_id); + } + } } pub async fn process( self, - nonce: near_primitives::types::Nonce, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, public_key: near_crypto::PublicKey, ) -> crate::CliResult { let access_key: near_primitives::account::AccessKey = near_primitives::account::AccessKey { - nonce, + nonce: 0, permission: near_primitives::account::AccessKeyPermission::FunctionCall( near_primitives::account::FunctionCallPermission { - allowance: self.allowance.clone(), - receiver_id: self.receiver_id.to_string().clone(), - method_names: self.method_names.clone(), + allowance: { + match self.allowance.clone() { + Some(allowance) => Some(allowance.to_yoctonear()), + None => None, + } + }, + receiver_id: self.receiver_account_id.to_string().clone(), + method_names: self.method_names.clone().into(), }, ), }; diff --git a/src/commands/add_command/access_key/public_key_mode/add_access_key/mod.rs b/src/commands/add_command/access_key/public_key_mode/add_access_key/mod.rs index 2737cb5c6..fca398095 100644 --- a/src/commands/add_command/access_key/public_key_mode/add_access_key/mod.rs +++ b/src/commands/add_command/access_key/public_key_mode/add_access_key/mod.rs @@ -1,84 +1,24 @@ -use dialoguer::{theme::ColorfulTheme, Input, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use dialoguer::Input; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod full_access_type; mod function_call_type; -/// добавление ключа пользователю -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliAddAccessKeyAction { - public_key: Option, - nonce: Option, - #[clap(subcommand)] - permission: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct AddAccessKeyAction { - pub public_key: near_crypto::PublicKey, - pub nonce: near_primitives::types::Nonce, + pub public_key: crate::types::public_key::PublicKey, + #[interactive_clap(subcommand)] pub permission: AccessKeyPermission, } -impl CliAddAccessKeyAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .permission - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(public_key) = &self.public_key { - args.push_front(public_key.to_string()); - }; - args - } -} - -impl From for CliAddAccessKeyAction { - fn from(add_access_key_action: AddAccessKeyAction) -> Self { - Self { - public_key: Some(add_access_key_action.public_key), - nonce: Some(add_access_key_action.nonce), - permission: Some(add_access_key_action.permission.into()), - } - } -} - -impl AddAccessKeyAction { - pub fn from( - item: CliAddAccessKeyAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let public_key: near_crypto::PublicKey = match item.public_key { - Some(cli_public_key) => cli_public_key, - None => AddAccessKeyAction::input_public_key(), - }; - let permission: AccessKeyPermission = match item.permission { - Some(cli_permission) => { - AccessKeyPermission::from(cli_permission, connection_config, sender_account_id)? - } - None => AccessKeyPermission::choose_permission(connection_config, sender_account_id)?, - }; - Ok(Self { - public_key, - nonce: 0, - permission, - }) - } -} - impl AddAccessKeyAction { - fn input_public_key() -> near_crypto::PublicKey { - Input::new() + fn input_public_key( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter a public key for this access key") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process( @@ -90,20 +30,18 @@ impl AddAccessKeyAction { AccessKeyPermission::GrantFullAccess(full_access_type) => { full_access_type .process( - self.nonce, prepopulated_unsigned_transaction, network_connection_config, - self.public_key, + self.public_key.into(), ) .await } AccessKeyPermission::GrantFunctionCallAccess(function_call_type) => { function_call_type .process( - self.nonce, prepopulated_unsigned_transaction, network_connection_config, - self.public_key, + self.public_key.into(), ) .await } @@ -111,109 +49,15 @@ impl AddAccessKeyAction { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliAccessKeyPermission { - /// Предоставьте данные для ключа с function call - GrantFunctionCallAccess(self::function_call_type::CliFunctionCallType), - /// Предоставьте данные для ключа с полным доступом - GrantFullAccess(self::full_access_type::CliFullAccessType), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Select a permission that you want to add to the access key pub enum AccessKeyPermission { #[strum_discriminants(strum(message = "A permission with function call"))] + /// Предоставьте данные для ключа с function call GrantFunctionCallAccess(self::function_call_type::FunctionCallType), #[strum_discriminants(strum(message = "A permission with full access"))] + /// Предоставьте данные для ключа с полным доступом GrantFullAccess(self::full_access_type::FullAccessType), } - -impl CliAccessKeyPermission { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::GrantFunctionCallAccess(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("grant-function-call-access".to_owned()); - args - } - Self::GrantFullAccess(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("grant-full-access".to_owned()); - args - } - } - } -} - -impl From for CliAccessKeyPermission { - fn from(access_key_permission: AccessKeyPermission) -> Self { - match access_key_permission { - AccessKeyPermission::GrantFunctionCallAccess(function_call_type) => { - Self::GrantFunctionCallAccess(function_call_type.into()) - } - AccessKeyPermission::GrantFullAccess(full_access_type) => { - Self::GrantFullAccess(full_access_type.into()) - } - } - } -} - -impl AccessKeyPermission { - pub fn from( - item: CliAccessKeyPermission, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliAccessKeyPermission::GrantFunctionCallAccess(cli_function_call_type) => { - let function_call_type = self::function_call_type::FunctionCallType::from( - cli_function_call_type, - connection_config, - sender_account_id, - )?; - Ok(AccessKeyPermission::GrantFunctionCallAccess( - function_call_type, - )) - } - CliAccessKeyPermission::GrantFullAccess(cli_full_access_type) => { - let full_access_type = self::full_access_type::FullAccessType::from( - cli_full_access_type, - connection_config, - sender_account_id, - )?; - Ok(AccessKeyPermission::GrantFullAccess(full_access_type)) - } - } - } -} - -impl AccessKeyPermission { - pub fn choose_permission( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let variants = AccessKeyPermissionDiscriminants::iter().collect::>(); - let permissions = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_permission = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select a permission that you want to add to the access key:") - .items(&permissions) - .default(0) - .interact() - .unwrap(); - match variants[select_permission] { - AccessKeyPermissionDiscriminants::GrantFunctionCallAccess => Ok(Self::from( - CliAccessKeyPermission::GrantFunctionCallAccess(Default::default()), - connection_config, - sender_account_id, - )?), - AccessKeyPermissionDiscriminants::GrantFullAccess => Ok(Self::from( - CliAccessKeyPermission::GrantFullAccess(Default::default()), - connection_config, - sender_account_id, - )?), - } - } -} diff --git a/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs b/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs index 2e6b204a5..ad2b597f0 100644 --- a/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs +++ b/src/commands/add_command/access_key/public_key_mode/generate_keypair/mod.rs @@ -1,63 +1,12 @@ use std::str::FromStr; -/// Generate a key pair of private and public keys (use it anywhere you need -/// Ed25519 keys) -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliGenerateKeypair { - #[clap(subcommand)] - permission: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct GenerateKeypair { + #[interactive_clap(subcommand)] pub permission: super::add_access_key::AccessKeyPermission, } -impl CliGenerateKeypair { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let args = self - .permission - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args - } -} - -impl From for CliGenerateKeypair { - fn from(generate_keypair: GenerateKeypair) -> Self { - Self { - permission: Some(generate_keypair.permission.into()), - } - } -} - -impl GenerateKeypair { - pub fn from( - item: CliGenerateKeypair, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let permission: super::add_access_key::AccessKeyPermission = match item.permission { - Some(cli_permission) => super::add_access_key::AccessKeyPermission::from( - cli_permission, - connection_config, - sender_account_id, - )?, - None => super::add_access_key::AccessKeyPermission::choose_permission( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { permission }) - } -} - impl GenerateKeypair { pub async fn process( self, @@ -81,7 +30,6 @@ impl GenerateKeypair { super::add_access_key::AccessKeyPermission::GrantFullAccess(full_access_type) => { full_access_type .process( - 0, prepopulated_unsigned_transaction, network_connection_config, near_crypto::PublicKey::from_str(&key_pair_properties.public_key_str)?, @@ -93,7 +41,6 @@ impl GenerateKeypair { ) => { function_call_type .process( - 0, prepopulated_unsigned_transaction, network_connection_config, near_crypto::PublicKey::from_str(&key_pair_properties.public_key_str)?, diff --git a/src/commands/add_command/access_key/public_key_mode/mod.rs b/src/commands/add_command/access_key/public_key_mode/mod.rs index 621a417e9..5a81068ad 100644 --- a/src/commands/add_command/access_key/public_key_mode/mod.rs +++ b/src/commands/add_command/access_key/public_key_mode/mod.rs @@ -1,111 +1,22 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod add_access_key; mod generate_keypair; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliPublicKeyMode { - /// Enter public key - PublicKey(self::add_access_key::CliAddAccessKeyAction), - /// Generate key pair - GenerateKeypair(self::generate_keypair::CliGenerateKeypair), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Select the mode for the public key pub enum PublicKeyMode { #[strum_discriminants(strum(message = "Enter public key"))] + /// Enter public key PublicKey(self::add_access_key::AddAccessKeyAction), #[strum_discriminants(strum(message = "Generate key pair"))] + /// Generate key pair GenerateKeypair(self::generate_keypair::GenerateKeypair), } -impl CliPublicKeyMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::PublicKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("public-key".to_owned()); - args - } - Self::GenerateKeypair(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("generate-keypair".to_owned()); - args - } - } - } -} - -impl From for CliPublicKeyMode { - fn from(public_key_mode: PublicKeyMode) -> Self { - match public_key_mode { - PublicKeyMode::PublicKey(add_access_key_action) => { - Self::PublicKey(add_access_key_action.into()) - } - PublicKeyMode::GenerateKeypair(generate_keypair) => { - Self::GenerateKeypair(generate_keypair.into()) - } - } - } -} - -impl PublicKeyMode { - pub fn from( - item: CliPublicKeyMode, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliPublicKeyMode::PublicKey(cli_add_access_key_action) => Ok(PublicKeyMode::PublicKey( - self::add_access_key::AddAccessKeyAction::from( - cli_add_access_key_action, - connection_config, - sender_account_id, - )?, - )), - CliPublicKeyMode::GenerateKeypair(cli_generate_keypair) => Ok( - PublicKeyMode::GenerateKeypair(self::generate_keypair::GenerateKeypair::from( - cli_generate_keypair, - connection_config, - sender_account_id, - )?), - ), - } - } -} - impl PublicKeyMode { - pub fn choose_public_key_mode( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let variants = PublicKeyModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select a permission that you want to add to the access key:") - .items(&modes) - .default(0) - .interact() - .unwrap(); - match variants[select_mode] { - PublicKeyModeDiscriminants::PublicKey => Ok(Self::from( - CliPublicKeyMode::PublicKey(Default::default()), - connection_config, - sender_account_id, - )?), - PublicKeyModeDiscriminants::GenerateKeypair => Ok(Self::from( - CliPublicKeyMode::GenerateKeypair(Default::default()), - connection_config, - sender_account_id, - )?), - } - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, diff --git a/src/commands/add_command/access_key/sender/mod.rs b/src/commands/add_command/access_key/sender/mod.rs index ab2c5f7d5..b9f4fc68c 100644 --- a/src/commands/add_command/access_key/sender/mod.rs +++ b/src/commands/add_command/access_key/sender/mod.rs @@ -1,98 +1,74 @@ use dialoguer::Input; -/// данные об отправителе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - public_key_mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::AddAccessKeyCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, + #[interactive_clap(skip_default_from_cli)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] pub public_key_mode: super::public_key_mode::PublicKeyMode, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .public_key_mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, +} + +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::AddAccessKeyCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - public_key_mode: Some(sender.public_key_mode.into()), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::AddAccessKeyCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let public_key_mode = match item.public_key_mode { - Some(cli_public_key_mode) => super::public_key_mode::PublicKeyMode::from( - cli_public_key_mode, - connection_config, - sender_account_id.clone(), - )?, - None => super::public_key_mode::PublicKeyMode::choose_public_key_mode( - connection_config, - sender_account_id.clone(), - )?, - }; - Ok(Self { - sender_account_id, - public_key_mode, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::AddAccessKeyCommandNetworkContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What account ID do you need to add a key?") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -110,8 +86,8 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), - receiver_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), + receiver_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; self.public_key_mode diff --git a/src/commands/add_command/contract_code/contract/initialize_mode/call_function_type/mod.rs b/src/commands/add_command/contract_code/contract/initialize_mode/call_function_type/mod.rs index 201c8cfa5..212250b10 100644 --- a/src/commands/add_command/contract_code/contract/initialize_mode/call_function_type/mod.rs +++ b/src/commands/add_command/contract_code/contract/initialize_mode/call_function_type/mod.rs @@ -1,160 +1,68 @@ use dialoguer::Input; -/// вызов CallFunction -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCallFunctionAction { - method_name: Option, - args: Option, - #[clap(long = "attached-deposit")] - deposit: Option, - #[clap(long = "prepaid-gas")] - gas: Option, - #[clap(subcommand)] - pub sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct CallFunctionAction { method_name: String, - args: Vec, - gas: near_primitives::types::Gas, - deposit: near_primitives::types::Balance, + args: String, + #[interactive_clap(long = "prepaid-gas")] + gas: crate::common::NearGas, + #[interactive_clap(long = "attached-deposit")] + deposit: crate::common::NearBalance, + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliCallFunctionAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(gas) = &self.gas { - args.push_front(gas.to_string()); - args.push_front("--prepaid-gas".to_owned()) - }; - if let Some(deposit) = &self.deposit { - args.push_front(deposit.to_string()); - args.push_front("--attached-deposit".to_owned()) - }; - if let Some(function_args) = &self.args { - args.push_front(function_args.to_owned()); - }; - if let Some(method_name) = &self.method_name { - args.push_front(method_name.to_string()); - }; - args - } -} - -impl From for CliCallFunctionAction { - fn from(call_function_action: CallFunctionAction) -> Self { - Self { - method_name: Some(call_function_action.method_name), - args: Some(String::from_utf8(call_function_action.args).unwrap_or_default()), - gas: Some(call_function_action.gas.into()), - deposit: Some(crate::common::NearBalance::from_yoctonear( - call_function_action.deposit, - )), - sign_option: Some(call_function_action.sign_option.into()), - } - } -} - -impl CallFunctionAction { - pub fn from( - item: CliCallFunctionAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let method_name: String = match item.method_name { - Some(cli_method_name) => cli_method_name, - None => CallFunctionAction::input_method_name(), - }; - let args: Vec = match item.args { - Some(cli_args) => cli_args.into_bytes(), - None => CallFunctionAction::input_args(), - }; - let gas: near_primitives::types::Gas = match item.gas { - Some(cli_gas) => match cli_gas { - crate::common::NearGas { inner: num } => num, - }, - None => CallFunctionAction::input_gas(), - }; - let deposit: near_primitives::types::Balance = match item.deposit { - Some(cli_deposit) => cli_deposit.to_yoctonear(), - None => CallFunctionAction::input_deposit(), - }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, - }; - Ok(Self { - method_name, - args, - gas, - deposit, - sign_option, - }) - } -} - impl CallFunctionAction { - fn input_method_name() -> String { + fn input_method_name( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); - Input::new() + Ok(Input::new() .with_prompt("Enter a method name") - .interact_text() - .unwrap() + .interact_text()?) } - fn input_gas() -> near_primitives::types::Gas { + fn input_gas( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); let gas: u64 = loop { let input_gas: crate::common::NearGas = Input::new() .with_prompt("Enter a gas for function") .with_initial_text("100 TeraGas") - .interact_text() - .unwrap(); + .interact_text()?; let gas: u64 = match input_gas { crate::common::NearGas { inner: num } => num, }; - if gas <= 200000000000000 { + if gas <= 300000000000000 { break gas; } else { println!("You need to enter a value of no more than 200 TERAGAS") } }; - gas + Ok(gas.into()) } - fn input_args() -> Vec { + fn input_args(_context: &crate::common::SignerContext) -> color_eyre::eyre::Result { println!(); - let input: String = Input::new() + Ok(Input::new() .with_prompt("Enter args for function") - .interact_text() - .unwrap(); - input.into_bytes() + .interact_text()?) } - fn input_deposit() -> near_primitives::types::Balance { + fn input_deposit( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); let deposit: crate::common::NearBalance = Input::new() .with_prompt( "Enter a deposit for function (example: 10NEAR or 0.5near or 10000yoctonear).", ) .with_initial_text("0 NEAR") - .interact_text() - .unwrap(); - deposit.to_yoctonear() + .interact_text()?; + Ok(deposit) } pub async fn process( @@ -165,9 +73,9 @@ impl CallFunctionAction { let action = near_primitives::transaction::Action::FunctionCall( near_primitives::transaction::FunctionCallAction { method_name: self.method_name.clone(), - args: self.args.clone(), - gas: self.gas.clone(), - deposit: self.deposit.clone(), + args: self.args.clone().into_bytes(), + gas: self.gas.clone().inner, + deposit: self.deposit.clone().to_yoctonear(), }, ); let mut actions = prepopulated_unsigned_transaction.actions.clone(); diff --git a/src/commands/add_command/contract_code/contract/initialize_mode/mod.rs b/src/commands/add_command/contract_code/contract/initialize_mode/mod.rs index ab2a37ffe..43cb9caa7 100644 --- a/src/commands/add_command/contract_code/contract/initialize_mode/mod.rs +++ b/src/commands/add_command/contract_code/contract/initialize_mode/mod.rs @@ -1,104 +1,20 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod call_function_type; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliNextAction { - /// Add an initialize - Initialize(self::call_function_type::CliCallFunctionAction), - /// Don't add an initialize - NoInitialize(CliNoInitialize), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] pub enum NextAction { + /// Add an initialize #[strum_discriminants(strum(message = "Add an initialize"))] Initialize(self::call_function_type::CallFunctionAction), + /// Don't add an initialize #[strum_discriminants(strum(message = "Don't add an initialize"))] NoInitialize(NoInitialize), } -impl CliNextAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Initialize(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("initialize".to_owned()); - args - } - Self::NoInitialize(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("no-initialize".to_owned()); - args - } - } - } -} - -impl From for CliNextAction { - fn from(next_action: NextAction) -> Self { - match next_action { - NextAction::Initialize(call_function_action) => { - Self::Initialize(call_function_action.into()) - } - NextAction::NoInitialize(no_initialize) => Self::NoInitialize(no_initialize.into()), - } - } -} - -impl NextAction { - pub fn from( - item: CliNextAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliNextAction::Initialize(cli_call_function_action) => Ok(NextAction::Initialize( - self::call_function_type::CallFunctionAction::from( - cli_call_function_action, - connection_config, - sender_account_id, - )?, - )), - CliNextAction::NoInitialize(cli_no_initialize) => Ok(NextAction::NoInitialize( - NoInitialize::from(cli_no_initialize, connection_config, sender_account_id)?, - )), - } - } -} - impl NextAction { - pub fn choose_next_action( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - println!(); - let variants = NextActionDiscriminants::iter().collect::>(); - let actions = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_action = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Do you want to choose next action") - .items(&actions) - .default(0) - .interact() - .unwrap(); - let cli_action = match variants[selected_action] { - NextActionDiscriminants::Initialize => CliNextAction::Initialize(Default::default()), - NextActionDiscriminants::NoInitialize => { - CliNextAction::NoInitialize(Default::default()) - } - }; - Ok(Self::from( - cli_action, - connection_config, - sender_account_id, - )?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -119,57 +35,14 @@ impl NextAction { } } -/// данные для инициализации -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNoInitialize { - #[clap(subcommand)] - pub sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct NoInitialize { + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliNoInitialize { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNoInitialize { - fn from(no_initialize: NoInitialize) -> Self { - Self { - sign_option: Some(no_initialize.sign_option.into()), - } - } -} - -impl NoInitialize { - fn from( - item: CliNoInitialize, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, - }; - Ok(Self { sign_option }) - } -} - impl NoInitialize { pub async fn process( self, diff --git a/src/commands/add_command/contract_code/contract/mod.rs b/src/commands/add_command/contract_code/contract/mod.rs index aa4e9b73b..674913c16 100644 --- a/src/commands/add_command/contract_code/contract/mod.rs +++ b/src/commands/add_command/contract_code/contract/mod.rs @@ -1,175 +1,24 @@ -use dialoguer::{theme::ColorfulTheme, Input, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use dialoguer::Input; mod initialize_mode; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliContract { - /// Add a contract file - ContractFile(CliContractFile), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Contract { - #[strum_discriminants(strum(message = "Add a contract file"))] - ContractFile(ContractFile), -} - -impl CliContract { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::ContractFile(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("contract-file".to_owned()); - args - } - } - } -} - -impl From for CliContract { - fn from(contract: Contract) -> Self { - match contract { - Contract::ContractFile(contract_file) => Self::ContractFile(contract_file.into()), - } - } -} - -impl Contract { - pub fn from( - item: CliContract, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliContract::ContractFile(cli_contract_file) => Ok(Contract::ContractFile( - ContractFile::from(cli_contract_file, connection_config, sender_account_id)?, - )), - } - } -} - -impl Contract { - pub fn choose_contract( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - println!(); - let variants = ContractDiscriminants::iter().collect::>(); - let contracts = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_contract = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("To deploy contract code you will need to choose next action") - .items(&contracts) - .default(0) - .interact() - .unwrap(); - let cli_contract = match variants[selected_contract] { - ContractDiscriminants::ContractFile => CliContract::ContractFile(Default::default()), - }; - Ok(Self::from( - cli_contract, - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - Contract::ContractFile(contract_file) => { - contract_file - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// add contract file -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliContractFile { - file_path: Option, - #[clap(subcommand)] - next_action: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct ContractFile { - pub file_path: std::path::PathBuf, + pub file_path: crate::types::path_buf::PathBuf, + #[interactive_clap(subcommand)] next_action: self::initialize_mode::NextAction, } -impl CliContractFile { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .next_action - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(file_path) = &self.file_path { - args.push_front(file_path.as_path().display().to_string()); - } - args - } -} - -impl From for CliContractFile { - fn from(contract_file: ContractFile) -> Self { - Self { - file_path: Some(contract_file.file_path), - next_action: Some(contract_file.next_action.into()), - } - } -} - -impl ContractFile { - fn from( - item: CliContractFile, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let file_path = match item.file_path { - Some(cli_file_path) => cli_file_path, - None => ContractFile::input_file_path(), - }; - let next_action = match item.next_action { - Some(cli_next_action) => self::initialize_mode::NextAction::from( - cli_next_action, - connection_config, - sender_account_id, - )?, - None => self::initialize_mode::NextAction::choose_next_action( - connection_config, - sender_account_id, - )?, - }; - Ok(ContractFile { - file_path, - next_action, - }) - } -} - impl ContractFile { - fn input_file_path() -> std::path::PathBuf { + fn input_file_path( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); let input_file_path: String = Input::new() .with_prompt("What is a file location of the contract?") - .interact_text() - .unwrap(); - input_file_path.into() + .interact_text()?; + Ok(std::path::PathBuf::from(input_file_path).into()) } pub async fn process( @@ -177,7 +26,7 @@ impl ContractFile { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, ) -> crate::CliResult { - let code = std::fs::read(&self.file_path.clone()) + let code = std::fs::read(&self.file_path.0.clone()) .map_err(|err| color_eyre::Report::msg(format!("Failed to open file: {:?}", err)))?; let action = near_primitives::transaction::Action::DeployContract( near_primitives::transaction::DeployContractAction { code }, diff --git a/src/commands/add_command/contract_code/operation_mode/mod.rs b/src/commands/add_command/contract_code/operation_mode/mod.rs index 2a3b9a12f..2c570c27f 100644 --- a/src/commands/add_command/contract_code/operation_mode/mod.rs +++ b/src/commands/add_command/contract_code/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { + /// Prepare and, optionally, submit a new transaction with online mode #[strum_discriminants(strum(message = "Yes, I keep it simple"))] Network(self::online_mode::NetworkArgs), + /// Prepare and, optionally, submit a new transaction with offline mode #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct AddContractCodeCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/add_command/contract_code/operation_mode/offline_mode/mod.rs b/src/commands/add_command/contract_code/operation_mode/offline_mode/mod.rs index 775982073..928caeb78 100644 --- a/src/commands/add_command/contract_code/operation_mode/offline_mode/mod.rs +++ b/src/commands/add_command/contract_code/operation_mode/offline_mode/mod.rs @@ -1,56 +1,38 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + ///Specify a sender + account: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::AddContractCodeCommandNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.account .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/add_command/contract_code/operation_mode/online_mode/mod.rs b/src/commands/add_command/contract_code/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/add_command/contract_code/operation_mode/online_mode/mod.rs +++ b/src/commands/add_command/contract_code/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/mod.rs b/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/mod.rs index 838909edd..3cd9453d6 100644 --- a/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,87 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::AddContractCodeCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/server/mod.rs index 376c3f71c..5ae3149bb 100644 --- a/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/add_command/contract_code/operation_mode/online_mode/select_server/server/mod.rs @@ -1,201 +1,75 @@ -use std::str::FromStr; - use dialoguer::Input; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, -} - -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a sender + pub account: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::TransferCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a sender + pub account: super::super::super::super::sender::Sender, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::AddContractCodeCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { url: url.inner }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone()), - None => SendFrom::choose_send_from(connection_config.clone()), - }?; - Ok(Server { - connection_config, - send_from, - }) - } -} - impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.account + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a sender - Account(super::super::super::super::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Account(super::super::super::super::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Account(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Account(sender) => Self::Account(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Account(cli_sender) => Ok(Self::Account( - super::super::super::super::sender::Sender::from( - cli_sender, - connection_config.clone(), - )?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::Account(Default::default()), - connection_config, - )?) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::Account(sender) => { - sender - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.account + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/add_command/contract_code/sender/mod.rs b/src/commands/add_command/contract_code/sender/mod.rs index d813aba9e..3f8dd8fdc 100644 --- a/src/commands/add_command/contract_code/sender/mod.rs +++ b/src/commands/add_command/contract_code/sender/mod.rs @@ -1,98 +1,75 @@ use dialoguer::Input; -/// данные об аккаунте контракта -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - contract: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::AddContractCodeCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] +pub struct Sender { + #[interactive_clap(skip_default_from_cli)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + ///Specify a contract + pub contract_file: super::contract::ContractFile, } -#[derive(Debug, Clone)] -pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, - pub contract: super::contract::Contract, +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .contract - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::AddContractCodeCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - contract: Some(sender.contract.into()), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::AddContractCodeCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let contract = match item.contract { - Some(cli_contract) => super::contract::Contract::from( - cli_contract, - connection_config, - sender_account_id.clone(), - )?, - None => super::contract::Contract::choose_contract( - connection_config, - sender_account_id.clone(), - )?, - }; - Ok(Self { - sender_account_id, - contract, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::AddContractCodeCommandNetworkContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What is the account ID of the contract?") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -110,11 +87,11 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), - receiver_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), + receiver_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; - self.contract + self.contract_file .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/add_command/implicit_account/generate_keypair/mod.rs b/src/commands/add_command/implicit_account/generate_keypair/mod.rs index 83e6de1f3..1f29b8158 100644 --- a/src/commands/add_command/implicit_account/generate_keypair/mod.rs +++ b/src/commands/add_command/implicit_account/generate_keypair/mod.rs @@ -22,7 +22,8 @@ fn bip32path_to_string(bip32path: &slip10::BIP32Path) -> String { /// Generate a key pair of private and public keys (use it anywhere you need /// Ed25519 keys) -#[derive(Debug, Clone, Default, clap::Clap)] +#[derive(Debug, Clone, Default, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct CliGenerateKeypair {} impl CliGenerateKeypair { diff --git a/src/commands/add_command/implicit_account/mod.rs b/src/commands/add_command/implicit_account/mod.rs index cc57d2638..70c3d013d 100644 --- a/src/commands/add_command/implicit_account/mod.rs +++ b/src/commands/add_command/implicit_account/mod.rs @@ -1,125 +1,31 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod generate_keypair; -/// Generate key pair -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliImplicitAccount { - #[clap(subcommand)] - public_key_mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct ImplicitAccount { + #[interactive_clap(subcommand)] pub public_key_mode: PublicKeyMode, } -impl CliImplicitAccount { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let args = self - .public_key_mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args - } -} - -impl From for CliImplicitAccount { - fn from(implicit_account: ImplicitAccount) -> Self { - Self { - public_key_mode: Some(implicit_account.public_key_mode.into()), - } - } -} - -impl From for ImplicitAccount { - fn from(item: CliImplicitAccount) -> Self { - let public_key_mode = match item.public_key_mode { - Some(cli_public_key_mode) => PublicKeyMode::from(cli_public_key_mode), - None => PublicKeyMode::choose_public_key_mode(), - }; - Self { public_key_mode } - } -} - impl ImplicitAccount { pub async fn process(self) -> crate::CliResult { self.public_key_mode.process().await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliPublicKeyMode { - /// Generate key pair - GenerateKeypair(self::generate_keypair::CliGenerateKeypair), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///Select the mode for the public key pub enum PublicKeyMode { #[strum_discriminants(strum(message = "Generate key pair"))] + /// Generate key pair GenerateKeypair(self::generate_keypair::CliGenerateKeypair), } -impl CliPublicKeyMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::GenerateKeypair(_) => { - let mut args = std::collections::VecDeque::new(); - args.push_front("generate-keypair".to_owned()); - args - } - } - } -} - -impl From for CliPublicKeyMode { - fn from(public_key_mode: PublicKeyMode) -> Self { - match public_key_mode { - PublicKeyMode::GenerateKeypair(generate_keypair) => { - Self::GenerateKeypair(generate_keypair) - } - } - } -} - -impl From for PublicKeyMode { - fn from(item: CliPublicKeyMode) -> Self { - match item { - CliPublicKeyMode::GenerateKeypair(cli_generate_keypair) => { - PublicKeyMode::GenerateKeypair(cli_generate_keypair) - } - } - } -} - impl PublicKeyMode { - pub fn choose_public_key_mode() -> Self { - let variants = PublicKeyModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select a permission that you want to add to the access key:") - .items(&modes) - .default(0) - .interact() - .unwrap(); - match variants[select_mode] { - PublicKeyModeDiscriminants::GenerateKeypair => { - Self::from(CliPublicKeyMode::GenerateKeypair(Default::default())) - } - } - } - pub async fn process(self) -> crate::CliResult { match self { PublicKeyMode::GenerateKeypair(cli_generate_keypair) => { diff --git a/src/commands/add_command/mod.rs b/src/commands/add_command/mod.rs index 50d75526b..28a0fcd8d 100644 --- a/src/commands/add_command/mod.rs +++ b/src/commands/add_command/mod.rs @@ -1,5 +1,4 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod access_key; mod contract_code; @@ -7,50 +6,13 @@ mod implicit_account; mod stake_proposal; mod sub_account; -/// инструмент выбора to add action -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliAddAction { - #[clap(subcommand)] - action: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct AddAction { + #[interactive_clap(subcommand)] pub action: Action, } -impl CliAddAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.action - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliAddAction { - fn from(item: AddAction) -> Self { - Self { - action: Some(item.action.into()), - } - } -} - -impl AddAction { - pub fn from(item: CliAddAction) -> color_eyre::eyre::Result { - let action = match item.action { - Some(cli_action) => Action::from(cli_action)?, - None => Action::choose_action()?, - }; - Ok(Self { action }) - } -} - impl AddAction { pub async fn process( self, @@ -60,129 +22,29 @@ impl AddAction { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliAction { - /// Add a new contract code - ContractCode(self::contract_code::operation_mode::CliOperationMode), - /// Add an implicit-account - ImplicitAccount(self::implicit_account::CliImplicitAccount), - /// Add a new stake proposal - StakeProposal(self::stake_proposal::operation_mode::CliOperationMode), - /// Add a new sub-account - SubAccount(self::sub_account::operation_mode::CliOperationMode), - /// Add a new access key for an account - AccessKey(self::access_key::operation_mode::CliOperationMode), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +/// What do you want to add? pub enum Action { #[strum_discriminants(strum(message = "Add a new access key for an account"))] + ///Add a new access key for an account AccessKey(self::access_key::operation_mode::OperationMode), #[strum_discriminants(strum(message = "Add a new contract code"))] + ///Add a contract code ContractCode(self::contract_code::operation_mode::OperationMode), #[strum_discriminants(strum(message = "Add an implicit-account"))] + ///Add implicit account ImplicitAccount(self::implicit_account::ImplicitAccount), #[strum_discriminants(strum(message = "Add a new stake proposal"))] + ///Add a new stake proposal StakeProposal(self::stake_proposal::operation_mode::OperationMode), #[strum_discriminants(strum(message = "Add a new sub-account"))] + ///Add a new sub-account SubAccount(self::sub_account::operation_mode::OperationMode), } -impl CliAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::ContractCode(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("contract-code".to_owned()); - command - } - Self::AccessKey(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("access-key".to_owned()); - command - } - Self::ImplicitAccount(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("implicit-account".to_owned()); - command - } - Self::StakeProposal(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("stake-proposal".to_owned()); - command - } - Self::SubAccount(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("sub-account".to_owned()); - command - } - } - } -} - -impl From for CliAction { - fn from(item: Action) -> Self { - match item { - Action::ContractCode(operation_mode) => Self::ContractCode(operation_mode.into()), - Action::AccessKey(operation_mode) => Self::AccessKey(operation_mode.into()), - Action::ImplicitAccount(implicit_account) => { - Self::ImplicitAccount(implicit_account.into()) - } - Action::StakeProposal(operation_mode) => Self::StakeProposal(operation_mode.into()), - Action::SubAccount(operation_mode) => Self::SubAccount(operation_mode.into()), - } - } -} - impl Action { - fn from(item: CliAction) -> color_eyre::eyre::Result { - match item { - CliAction::AccessKey(cli_operation_mode) => Ok(Action::AccessKey( - self::access_key::operation_mode::OperationMode::from(cli_operation_mode)?, - )), - CliAction::ContractCode(cli_operation_mode) => Ok(Action::ContractCode( - self::contract_code::operation_mode::OperationMode::from(cli_operation_mode) - .unwrap(), - )), - CliAction::ImplicitAccount(cli_generate_keypair) => { - Ok(Action::ImplicitAccount(cli_generate_keypair.into())) - } - CliAction::StakeProposal(cli_operation_mode) => Ok(Action::StakeProposal( - self::stake_proposal::operation_mode::OperationMode::from(cli_operation_mode) - .unwrap(), - )), - CliAction::SubAccount(cli_operation_mode) => Ok(Action::SubAccount( - self::sub_account::operation_mode::OperationMode::from(cli_operation_mode).unwrap(), - )), - } - } -} - -impl Action { - fn choose_action() -> color_eyre::eyre::Result { - println!(); - let variants = ActionDiscriminants::iter().collect::>(); - let actions = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_action = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Сhoose what you want to add") - .items(&actions) - .default(0) - .interact() - .unwrap(); - let cli_action = match variants[selected_action] { - ActionDiscriminants::AccessKey => CliAction::AccessKey(Default::default()), - ActionDiscriminants::ContractCode => CliAction::ContractCode(Default::default()), - ActionDiscriminants::ImplicitAccount => CliAction::ImplicitAccount(Default::default()), - ActionDiscriminants::StakeProposal => CliAction::StakeProposal(Default::default()), - ActionDiscriminants::SubAccount => CliAction::SubAccount(Default::default()), - }; - Ok(Self::from(cli_action)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, diff --git a/src/commands/add_command/stake_proposal/operation_mode/mod.rs b/src/commands/add_command/stake_proposal/operation_mode/mod.rs index 2a3b9a12f..da00c87c9 100644 --- a/src/commands/add_command/stake_proposal/operation_mode/mod.rs +++ b/src/commands/add_command/stake_proposal/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { #[strum_discriminants(strum(message = "Yes, I keep it simple"))] + /// Prepare and, optionally, submit a new transaction with online mode Network(self::online_mode::NetworkArgs), #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] + /// Prepare and, optionally, submit a new transaction with offline mode Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct AddStakeProposalCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/add_command/stake_proposal/operation_mode/offline_mode/mod.rs b/src/commands/add_command/stake_proposal/operation_mode/offline_mode/mod.rs index 775982073..c326d6ebe 100644 --- a/src/commands/add_command/stake_proposal/operation_mode/offline_mode/mod.rs +++ b/src/commands/add_command/stake_proposal/operation_mode/offline_mode/mod.rs @@ -1,56 +1,38 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + ///Specify a validator + validator: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::AddStakeProposalCommandNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.validator .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/add_command/stake_proposal/operation_mode/online_mode/mod.rs b/src/commands/add_command/stake_proposal/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/add_command/stake_proposal/operation_mode/online_mode/mod.rs +++ b/src/commands/add_command/stake_proposal/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/mod.rs b/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/mod.rs index 838909edd..2199b3000 100644 --- a/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,86 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { #[strum_discriminants(strum(message = "Testnet"))] + /// предоставление данных для сервера https://rpc.testnet.near.org Testnet(self::server::Server), #[strum_discriminants(strum(message = "Mainnet"))] + /// предоставление данных для сервера https://rpc.mainnet.near.org Mainnet(self::server::Server), #[strum_discriminants(strum(message = "Betanet"))] + /// предоставление данных для сервера https://rpc.betanet.near.org Betanet(self::server::Server), #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + /// предоставление данных для сервера, указанного вручную + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::AddStakeProposalCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/server/mod.rs index e7fa66f05..47ed2a982 100644 --- a/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/add_command/stake_proposal/operation_mode/online_mode/select_server/server/mod.rs @@ -1,118 +1,53 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a validator + pub validator: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::AddAccessKeyCommandNetworkContext)] +pub struct CustomServer { + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a validator + pub validator: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::AddStakeProposalCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { url: url.inner }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone())?, - None => SendFrom::choose_send_from(connection_config.clone())?, - }; - Ok(Server { - connection_config, - send_from, - }) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } } @@ -120,78 +55,22 @@ impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.validator + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a validator - Validator(super::super::super::super::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Validator(super::super::super::super::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Validator(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("validator".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Validator(sender) => Self::Validator(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Validator(cli_sender) => Ok(Self::Validator( - super::super::super::super::sender::Sender::from(cli_sender, connection_config)?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::Validator(Default::default()), - connection_config, - )?) - } - +impl CustomServer { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::Validator(sender) => { - sender - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.validator + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/add_command/stake_proposal/sender/mod.rs b/src/commands/add_command/stake_proposal/sender/mod.rs index 0276f4923..39f47e1fe 100644 --- a/src/commands/add_command/stake_proposal/sender/mod.rs +++ b/src/commands/add_command/stake_proposal/sender/mod.rs @@ -1,98 +1,75 @@ use dialoguer::Input; -/// данные об отправителе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - stake: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::AddStakeProposalCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] +pub struct Sender { + #[interactive_clap(skip_default_from_cli)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + /// Enter an amount + amount: super::stake_near_tokens_type::StakeNEARTokensAction, } -#[derive(Debug, Clone)] -pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, - pub stake: super::stake_near_tokens_type::Stake, +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .stake - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::AddStakeProposalCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - stake: Some(sender.stake.into()), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::AddStakeProposalCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let stake: super::stake_near_tokens_type::Stake = match item.stake { - Some(cli_stake) => super::stake_near_tokens_type::Stake::from( - cli_stake, - connection_config, - sender_account_id.clone(), - )?, - None => super::stake_near_tokens_type::Stake::choose_stake_near( - connection_config, - sender_account_id.clone(), - )?, - }; - Ok(Self { - sender_account_id, - stake, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::AddStakeProposalCommandNetworkContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What is the account ID of the validator?") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -110,11 +87,11 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), - receiver_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), + receiver_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; - self.stake + self.amount .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/add_command/stake_proposal/stake_near_tokens_type/mod.rs b/src/commands/add_command/stake_proposal/stake_near_tokens_type/mod.rs index 77c7982be..6b1535412 100644 --- a/src/commands/add_command/stake_proposal/stake_near_tokens_type/mod.rs +++ b/src/commands/add_command/stake_proposal/stake_near_tokens_type/mod.rs @@ -1,161 +1,22 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliStake { - /// Enter an amount - Amount(CliStakeNEARTokensAction), -} - -#[derive(Debug, Clone)] -pub enum Stake { - Amount(StakeNEARTokensAction), -} - -impl CliStake { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Amount(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("amount".to_owned()); - args - } - } - } -} - -impl From for CliStake { - fn from(stake: Stake) -> Self { - match stake { - Stake::Amount(stake_near_tokens_action) => { - Self::Amount(stake_near_tokens_action.into()) - } - } - } -} - -impl Stake { - pub fn from( - item: CliStake, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliStake::Amount(cli_stake_near_action) => { - Ok(Self::Amount(StakeNEARTokensAction::from( - cli_stake_near_action, - connection_config, - sender_account_id, - )?)) - } - } - } -} - -impl Stake { - pub fn choose_stake_near( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliStake::Amount(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - Stake::Amount(transfer_near_action) => { - transfer_near_action - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// создание перевода токенов -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliStakeNEARTokensAction { - stake_amount: Option, - #[clap(subcommand)] - sign_transactions: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct StakeNEARTokensAction { pub stake_amount: crate::common::NearBalance, - pub sign_transactions: super::transactions_signing::TransactionsSigning, -} - -impl CliStakeNEARTokensAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .sign_transactions - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(stake_amount) = &self.stake_amount { - args.push_front(stake_amount.to_string()); - } - args - } -} - -impl From for CliStakeNEARTokensAction { - fn from(stake_near_tokens_action: StakeNEARTokensAction) -> Self { - Self { - stake_amount: Some(stake_near_tokens_action.stake_amount.into()), - sign_transactions: Some(stake_near_tokens_action.sign_transactions.into()), - } - } -} - -impl StakeNEARTokensAction { - fn from( - item: CliStakeNEARTokensAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let stake_amount: crate::common::NearBalance = match item.stake_amount { - Some(cli_stake_amount) => cli_stake_amount, - None => StakeNEARTokensAction::input_stake_amount(), - }; - let sign_transactions = match item.sign_transactions { - Some(cli_transaction_signing) => { - super::transactions_signing::TransactionsSigning::from( - cli_transaction_signing, - connection_config, - sender_account_id, - )? - } - None => super::transactions_signing::TransactionsSigning::choose_sign_transactions( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { - stake_amount, - sign_transactions, - }) - } + #[interactive_clap(named_arg)] + ///Enter an public key + pub transactions_signing_public_key: super::transactions_signing::TransactionsSigningAction, } impl StakeNEARTokensAction { - fn input_stake_amount() -> crate::common::NearBalance { - Input::new() + fn input_stake_amount( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("How many NEAR Tokens do you want to stake? (example: 10NEAR or 0.5near or 10000yoctonear)") .interact_text() - .unwrap() + ?) } pub async fn process( @@ -163,7 +24,7 @@ impl StakeNEARTokensAction { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, ) -> crate::CliResult { - self.sign_transactions + self.transactions_signing_public_key .process( prepopulated_unsigned_transaction, network_connection_config, diff --git a/src/commands/add_command/stake_proposal/transactions_signing/mod.rs b/src/commands/add_command/stake_proposal/transactions_signing/mod.rs index 924b231fe..5cbf42b69 100644 --- a/src/commands/add_command/stake_proposal/transactions_signing/mod.rs +++ b/src/commands/add_command/stake_proposal/transactions_signing/mod.rs @@ -1,165 +1,21 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliTransactionsSigning { - /// Enter an public key - TransactionsSigningPublicKey(CliTransactionsSigningAction), -} - -#[derive(Debug, Clone)] -pub enum TransactionsSigning { - TransactionsSigningPublicKey(TransactionsSigningAction), -} - -impl CliTransactionsSigning { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::TransactionsSigningPublicKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("transactions-signing-public-key".to_owned()); - args - } - } - } -} - -impl From for CliTransactionsSigning { - fn from(transactions_signing: TransactionsSigning) -> Self { - match transactions_signing { - TransactionsSigning::TransactionsSigningPublicKey(transactions_signing_action) => { - Self::TransactionsSigningPublicKey(transactions_signing_action.into()) - } - } - } -} - -impl TransactionsSigning { - pub fn from( - item: CliTransactionsSigning, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliTransactionsSigning::TransactionsSigningPublicKey( - cli_transactions_signing_action, - ) => Ok(Self::TransactionsSigningPublicKey( - TransactionsSigningAction::from( - cli_transactions_signing_action, - connection_config, - sender_account_id, - )?, - )), - } - } -} - -impl TransactionsSigning { - pub fn choose_sign_transactions( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliTransactionsSigning::TransactionsSigningPublicKey(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - stake: u128, - ) -> crate::CliResult { - match self { - TransactionsSigning::TransactionsSigningPublicKey(transactions_sign_action) => { - transactions_sign_action - .process( - prepopulated_unsigned_transaction, - network_connection_config, - stake, - ) - .await - } - } - } -} - -/// данные о получателе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliTransactionsSigningAction { - transactions_signing_public_key: Option, - #[clap(subcommand)] - sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct TransactionsSigningAction { - pub transactions_signing_public_key: near_crypto::PublicKey, + pub public_key: crate::types::public_key::PublicKey, + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliTransactionsSigningAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(transactions_signing_public_key) = &self.transactions_signing_public_key { - args.push_front(transactions_signing_public_key.to_string()); - } - args - } -} - -impl From for CliTransactionsSigningAction { - fn from(transactions_signing_action: TransactionsSigningAction) -> Self { - Self { - transactions_signing_public_key: Some( - transactions_signing_action.transactions_signing_public_key, - ), - sign_option: Some(transactions_signing_action.sign_option.into()), - } - } -} - -impl TransactionsSigningAction { - fn from( - item: CliTransactionsSigningAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let transactions_signing_public_key: near_crypto::PublicKey = - match item.transactions_signing_public_key { - Some(cli_transactions_signing_public_key) => cli_transactions_signing_public_key, - None => TransactionsSigningAction::input_public_key(), - }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, - }; - Ok(Self { - transactions_signing_public_key, - sign_option, - }) - } -} - impl TransactionsSigningAction { - fn input_public_key() -> near_crypto::PublicKey { - Input::new() + fn input_public_key( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter a public key for this server") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process( @@ -171,7 +27,7 @@ impl TransactionsSigningAction { let action = near_primitives::transaction::Action::Stake( near_primitives::transaction::StakeAction { stake, - public_key: self.transactions_signing_public_key, + public_key: self.public_key.into(), }, ); let mut actions = prepopulated_unsigned_transaction.actions.clone(); diff --git a/src/commands/add_command/sub_account/deposit/mod.rs b/src/commands/add_command/sub_account/deposit/mod.rs index b0659bd0b..f98cc002d 100644 --- a/src/commands/add_command/sub_account/deposit/mod.rs +++ b/src/commands/add_command/sub_account/deposit/mod.rs @@ -1,83 +1,5 @@ use dialoguer::{console::Term, theme::ColorfulTheme, Input, Select}; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliDeposit { - /// Enter an amount - Deposit(CliTransferNEARTokensAction), -} - -#[derive(Debug, Clone)] -pub enum Deposit { - Deposit(TransferNEARTokensAction), -} - -impl CliDeposit { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Deposit(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("deposit".to_owned()); - args - } - } - } -} - -impl From for CliDeposit { - fn from(deposit: Deposit) -> Self { - match deposit { - Deposit::Deposit(transfer_near_token_action) => { - Self::Deposit(transfer_near_token_action.into()) - } - } - } -} - -impl Deposit { - pub fn from( - item: CliDeposit, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliDeposit::Deposit(cli_transfer_near_action) => { - Ok(Self::Deposit(TransferNEARTokensAction::from( - cli_transfer_near_action, - connection_config, - sender_account_id, - )?)) - } - } - } -} - -impl Deposit { - pub fn choose_deposit( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliDeposit::Deposit(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - Deposit::Deposit(transfer_near_action) => { - transfer_near_action - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - /// создание перевода токенов #[derive(Debug, Default, Clone, clap::Clap)] #[clap( @@ -100,6 +22,10 @@ pub struct TransferNEARTokensAction { crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } +impl interactive_clap::ToCli for TransferNEARTokensAction { + type CliVariant = CliTransferNEARTokensAction; +} + impl CliTransferNEARTokensAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -124,22 +50,28 @@ impl From for CliTransferNEARTokensAction { } impl TransferNEARTokensAction { - fn from( - item: CliTransferNEARTokensAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option< + ::CliVariant, + >, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let amount: crate::common::TransferAmount = match item.amount { + let amount: crate::common::TransferAmount = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.amount) + { Some(cli_amount) => crate::common::TransferAmount::from_unchecked(cli_amount), - None => TransferNEARTokensAction::input_amount( - connection_config.clone(), - sender_account_id.clone(), - )?, + None => TransferNEARTokensAction::input_amount(&context)?, }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, + + let sign_option = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.sign_option) + { + Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from_cli(Some(cli_sign_transaction), context)?, + None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_variant(context)?, }; + Ok(Self { amount, sign_option, @@ -149,20 +81,21 @@ impl TransferNEARTokensAction { impl TransferNEARTokensAction { fn input_amount( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + context: &crate::common::SignerContext, ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let sender_account_id = context.signer_account_id.clone(); match connection_config { Some(connection_config) => { let account_transfer_allowance = crate::common::get_account_transfer_allowance( &connection_config, - sender_account_id, + sender_account_id.into(), )?; loop { let input_amount: crate::common::NearBalance = Input::new() .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") .interact_text() - .unwrap(); + ?; if let Ok(transfer_amount) = crate::common::TransferAmount::from( input_amount.clone(), &account_transfer_allowance, @@ -181,8 +114,7 @@ impl TransferNEARTokensAction { .with_prompt("Do you want to keep this amount for the transfer?") .items(&choose_input) .default(0) - .interact_on_opt(&Term::stderr()) - .unwrap(); + .interact_on_opt(&Term::stderr())?; match select_choose_input { Some(0) => { break Ok(crate::common::TransferAmount::from_unchecked( @@ -199,7 +131,7 @@ impl TransferNEARTokensAction { let input_amount: crate::common::NearBalance = Input::new() .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") .interact_text() - .unwrap(); + ?; Ok(crate::common::TransferAmount::from_unchecked(input_amount)) } } diff --git a/src/commands/add_command/sub_account/full_access_key/mod.rs b/src/commands/add_command/sub_account/full_access_key/mod.rs index 435b891c9..5db571aaf 100644 --- a/src/commands/add_command/sub_account/full_access_key/mod.rs +++ b/src/commands/add_command/sub_account/full_access_key/mod.rs @@ -1,162 +1,12 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; - mod public_key_mode; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliFullAccessKey { - /// Specify a full access key for the sub-account - SubAccountFullAccess(CliSubAccountFullAccess), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum FullAccessKey { - #[strum_discriminants(strum(message = "Add a full access key for the sub-account"))] - SubAccountFullAccess(SubAccountFullAccess), -} - -impl CliFullAccessKey { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::SubAccountFullAccess(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("sub-account-full-access".to_owned()); - command - } - } - } -} - -impl From for CliFullAccessKey { - fn from(full_access_key: FullAccessKey) -> Self { - match full_access_key { - FullAccessKey::SubAccountFullAccess(sub_account_full_access) => { - Self::SubAccountFullAccess(sub_account_full_access.into()) - } - } - } -} - -impl FullAccessKey { - pub fn from( - item: CliFullAccessKey, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliFullAccessKey::SubAccountFullAccess(cli_sub_account_full_access) => Ok( - FullAccessKey::SubAccountFullAccess(SubAccountFullAccess::from( - cli_sub_account_full_access, - connection_config, - sender_account_id, - )?), - ), - } - } -} - -impl FullAccessKey { - pub fn choose_full_access_key( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - println!(); - let variants = FullAccessKeyDiscriminants::iter().collect::>(); - let actions = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_action = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Сhoose what you want to add") - .items(&actions) - .default(0) - .interact() - .unwrap(); - let cli_action = match variants[selected_action] { - FullAccessKeyDiscriminants::SubAccountFullAccess => { - CliFullAccessKey::SubAccountFullAccess(Default::default()) - } - }; - Ok(Self::from( - cli_action, - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - FullAccessKey::SubAccountFullAccess(sub_account_full_access) => { - sub_account_full_access - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// данные о ключе доступа -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSubAccountFullAccess { - #[clap(subcommand)] - public_key_mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct SubAccountFullAccess { + #[interactive_clap(subcommand)] pub public_key_mode: self::public_key_mode::PublicKeyMode, } -impl CliSubAccountFullAccess { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let args = self - .public_key_mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args - } -} - -impl From for CliSubAccountFullAccess { - fn from(sub_account_full_access: SubAccountFullAccess) -> Self { - Self { - public_key_mode: Some(sub_account_full_access.public_key_mode.into()), - } - } -} - -impl SubAccountFullAccess { - fn from( - item: CliSubAccountFullAccess, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let public_key_mode = match item.public_key_mode { - Some(cli_public_key_mode) => self::public_key_mode::PublicKeyMode::from( - cli_public_key_mode, - connection_config, - sender_account_id, - )?, - None => self::public_key_mode::PublicKeyMode::choose_public_key_mode( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { public_key_mode }) - } -} - impl SubAccountFullAccess { pub async fn process( self, diff --git a/src/commands/add_command/sub_account/full_access_key/public_key_mode/add_full_access_key/mod.rs b/src/commands/add_command/sub_account/full_access_key/public_key_mode/add_full_access_key/mod.rs index 6cbd4e07b..6d8b5ec7c 100644 --- a/src/commands/add_command/sub_account/full_access_key/public_key_mode/add_full_access_key/mod.rs +++ b/src/commands/add_command/sub_account/full_access_key/public_key_mode/add_full_access_key/mod.rs @@ -1,88 +1,28 @@ use dialoguer::Input; -/// Add full access key to the sub-account -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliAddAccessKeyAction { - public_key: Option, - nonce: Option, - #[clap(subcommand)] - deposit: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct AddAccessKeyAction { - pub public_key: near_crypto::PublicKey, + pub public_key: crate::types::public_key::PublicKey, pub nonce: near_primitives::types::Nonce, - pub deposit: super::super::super::deposit::Deposit, -} - -impl CliAddAccessKeyAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .deposit - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(nonce) = &self.nonce { - args.push_front(nonce.to_string()); - } - if let Some(public_key) = &self.public_key { - args.push_front(public_key.to_string()); - } - args - } -} - -impl From for CliAddAccessKeyAction { - fn from(add_access_key_action: AddAccessKeyAction) -> Self { - Self { - public_key: Some(add_access_key_action.public_key), - nonce: Some(add_access_key_action.nonce), - deposit: Some(add_access_key_action.deposit.into()), - } - } + #[interactive_clap(named_arg)] + ///Enter an amount + pub deposit: super::super::super::deposit::TransferNEARTokensAction, } impl AddAccessKeyAction { - pub fn from( - item: CliAddAccessKeyAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let public_key: near_crypto::PublicKey = match item.public_key { - Some(cli_public_key) => cli_public_key, - None => AddAccessKeyAction::input_public_key(), - }; - let deposit = match item.deposit { - Some(cli_deposit) => super::super::super::deposit::Deposit::from( - cli_deposit, - connection_config, - sender_account_id, - )?, - None => super::super::super::deposit::Deposit::choose_deposit( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { - public_key, - nonce: 0, - deposit, - }) + fn input_public_key( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("Enter a public key for this access key") + .interact_text()?) } -} -impl AddAccessKeyAction { - fn input_public_key() -> near_crypto::PublicKey { - Input::new() - .with_prompt("Enter a public key for this access key") - .interact_text() - .unwrap() + fn input_nonce( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(0) } pub async fn process( @@ -90,13 +30,13 @@ impl AddAccessKeyAction { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, ) -> crate::CliResult { - let access_key: near_primitives::account::AccessKey = near_primitives::account::AccessKey { + let access_key = near_primitives::account::AccessKey { nonce: self.nonce.clone(), permission: near_primitives::account::AccessKeyPermission::FullAccess, }; let action = near_primitives::transaction::Action::AddKey( near_primitives::transaction::AddKeyAction { - public_key: self.public_key.clone(), + public_key: self.public_key.clone().into(), access_key, }, ); diff --git a/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs b/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs index 5f630eddd..561117941 100644 --- a/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs +++ b/src/commands/add_command/sub_account/full_access_key/public_key_mode/generate_keypair/mod.rs @@ -1,61 +1,11 @@ use std::str::FromStr; -/// Generate a key pair of private and public keys (use it anywhere you need -/// Ed25519 keys) -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliGenerateKeypair { - #[clap(subcommand)] - pub deposit: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct GenerateKeypair { - pub deposit: super::super::super::deposit::Deposit, -} - -impl CliGenerateKeypair { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let args = self - .deposit - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args - } -} - -impl From for CliGenerateKeypair { - fn from(generate_keypair: GenerateKeypair) -> Self { - Self { - deposit: Some(generate_keypair.deposit.into()), - } - } -} - -impl GenerateKeypair { - pub fn from( - item: CliGenerateKeypair, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let deposit = match item.deposit { - Some(cli_deposit) => super::super::super::deposit::Deposit::from( - cli_deposit, - connection_config, - sender_account_id, - )?, - None => super::super::super::deposit::Deposit::choose_deposit( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { deposit }) - } + #[interactive_clap(named_arg)] + ///Enter an amount + pub deposit: super::super::super::deposit::TransferNEARTokensAction, } impl GenerateKeypair { diff --git a/src/commands/add_command/sub_account/full_access_key/public_key_mode/mod.rs b/src/commands/add_command/sub_account/full_access_key/public_key_mode/mod.rs index 6a1b71a00..0867fdb4c 100644 --- a/src/commands/add_command/sub_account/full_access_key/public_key_mode/mod.rs +++ b/src/commands/add_command/sub_account/full_access_key/public_key_mode/mod.rs @@ -1,111 +1,22 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod add_full_access_key; mod generate_keypair; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliPublicKeyMode { - /// Enter public key - PublicKey(self::add_full_access_key::CliAddAccessKeyAction), - /// Generate key pair - GenerateKeypair(self::generate_keypair::CliGenerateKeypair), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Add a full access key for the sub-account pub enum PublicKeyMode { #[strum_discriminants(strum(message = "Enter public key"))] + /// Enter public key PublicKey(self::add_full_access_key::AddAccessKeyAction), #[strum_discriminants(strum(message = "Generate key pair"))] + /// Generate key pair GenerateKeypair(self::generate_keypair::GenerateKeypair), } -impl CliPublicKeyMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::PublicKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("public-key".to_owned()); - args - } - Self::GenerateKeypair(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("generate-keypair".to_owned()); - args - } - } - } -} - -impl From for CliPublicKeyMode { - fn from(public_key_mode: PublicKeyMode) -> Self { - match public_key_mode { - PublicKeyMode::PublicKey(add_access_key_action) => { - Self::PublicKey(add_access_key_action.into()) - } - PublicKeyMode::GenerateKeypair(generate_keypair) => { - Self::GenerateKeypair(generate_keypair.into()) - } - } - } -} - -impl PublicKeyMode { - pub fn from( - item: CliPublicKeyMode, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliPublicKeyMode::PublicKey(cli_add_access_key_action) => Ok(PublicKeyMode::PublicKey( - self::add_full_access_key::AddAccessKeyAction::from( - cli_add_access_key_action, - connection_config, - sender_account_id, - )?, - )), - CliPublicKeyMode::GenerateKeypair(cli_generate_keypair) => Ok( - PublicKeyMode::GenerateKeypair(self::generate_keypair::GenerateKeypair::from( - cli_generate_keypair, - connection_config, - sender_account_id, - )?), - ), - } - } -} - impl PublicKeyMode { - pub fn choose_public_key_mode( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let variants = PublicKeyModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select a permission that you want to add to the access key:") - .items(&modes) - .default(0) - .interact() - .unwrap(); - match variants[select_mode] { - PublicKeyModeDiscriminants::PublicKey => Ok(Self::from( - CliPublicKeyMode::PublicKey(Default::default()), - connection_config, - sender_account_id, - )?), - PublicKeyModeDiscriminants::GenerateKeypair => Ok(Self::from( - CliPublicKeyMode::GenerateKeypair(Default::default()), - connection_config, - sender_account_id, - )?), - } - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, diff --git a/src/commands/add_command/sub_account/operation_mode/mod.rs b/src/commands/add_command/sub_account/operation_mode/mod.rs index 2a3b9a12f..2b70000f8 100644 --- a/src/commands/add_command/sub_account/operation_mode/mod.rs +++ b/src/commands/add_command/sub_account/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { #[strum_discriminants(strum(message = "Yes, I keep it simple"))] + /// Prepare and, optionally, submit a new transaction with online mode Network(self::online_mode::NetworkArgs), #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] + /// Prepare and, optionally, submit a new transaction with offline mode Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct AddSubAccountCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/add_command/sub_account/operation_mode/offline_mode/mod.rs b/src/commands/add_command/sub_account/operation_mode/offline_mode/mod.rs index 775982073..22cbfa252 100644 --- a/src/commands/add_command/sub_account/operation_mode/offline_mode/mod.rs +++ b/src/commands/add_command/sub_account/operation_mode/offline_mode/mod.rs @@ -1,56 +1,38 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + ///Specify owner account + owner_account: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::AddSubAccountCommandNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.owner_account .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/add_command/sub_account/operation_mode/online_mode/mod.rs b/src/commands/add_command/sub_account/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/add_command/sub_account/operation_mode/online_mode/mod.rs +++ b/src/commands/add_command/sub_account/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/mod.rs b/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/mod.rs index 838909edd..477b3617e 100644 --- a/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,86 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { #[strum_discriminants(strum(message = "Testnet"))] + /// предоставление данных для сервера https://rpc.testnet.near.org Testnet(self::server::Server), #[strum_discriminants(strum(message = "Mainnet"))] + /// предоставление данных для сервера https://rpc.mainnet.near.org Mainnet(self::server::Server), #[strum_discriminants(strum(message = "Betanet"))] + /// предоставление данных для сервера https://rpc.betanet.near.org Betanet(self::server::Server), #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + /// предоставление данных для сервера, указанного вручную + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::AddSubAccountCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/server/mod.rs index b9bc13a09..387424f4a 100644 --- a/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/add_command/sub_account/operation_mode/online_mode/select_server/server/mod.rs @@ -1,118 +1,53 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify owner account + pub owner_account: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::AddAccessKeyCommandNetworkContext)] +pub struct CustomServer { + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify owner account + pub owner_account: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::AddSubAccountCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { url: url.inner }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone())?, - None => SendFrom::choose_send_from(connection_config.clone())?, - }; - Ok(Server { - connection_config, - send_from, - }) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } } @@ -120,78 +55,22 @@ impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.owner_account + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a sender - OwnerAccount(super::super::super::super::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - OwnerAccount(super::super::super::super::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::OwnerAccount(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("owner-account".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::OwnerAccount(sender) => Self::OwnerAccount(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::OwnerAccount(cli_sender) => Ok(Self::OwnerAccount( - super::super::super::super::sender::Sender::from(cli_sender, connection_config)?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::OwnerAccount(Default::default()), - connection_config, - )?) - } - +impl CustomServer { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::OwnerAccount(sender) => { - sender - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.owner_account + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/add_command/sub_account/receiver/mod.rs b/src/commands/add_command/sub_account/receiver/mod.rs index 291836c1d..e64be6edb 100644 --- a/src/commands/add_command/sub_account/receiver/mod.rs +++ b/src/commands/add_command/sub_account/receiver/mod.rs @@ -1,155 +1,21 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify a sub-account - SubAccount(CliSubAccount), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - SubAccount(SubAccount), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::SubAccount(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("sub-account".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::SubAccount(sub_account) => Self::SubAccount(sub_account.into()), - } - } -} - -impl SendTo { - pub fn from( - item: CliSendTo, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliSendTo::SubAccount(cli_receiver) => { - let receiver = - SubAccount::from(cli_receiver, connection_config, sender_account_id)?; - Ok(Self::SubAccount(receiver)) - } - } - } -} - -impl SendTo { - pub fn send_to( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendTo::SubAccount(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - SendTo::SubAccount(receiver) => { - receiver - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// Specify a sub-account -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSubAccount { - sub_account_id: Option, - #[clap(subcommand)] - full_access_key: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct SubAccount { - pub sub_account_id: near_primitives::types::AccountId, - pub full_access_key: super::full_access_key::FullAccessKey, -} - -impl CliSubAccount { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .full_access_key - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sub_account_id) = &self.sub_account_id { - args.push_front(sub_account_id.to_string()); - } - args - } -} - -impl From for CliSubAccount { - fn from(sub_account: SubAccount) -> Self { - Self { - sub_account_id: Some(sub_account.sub_account_id), - full_access_key: Some(sub_account.full_access_key.into()), - } - } -} - -impl SubAccount { - fn from( - item: CliSubAccount, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let sub_account_id: near_primitives::types::AccountId = match item.sub_account_id { - Some(cli_sub_account_id) => cli_sub_account_id, - None => SubAccount::input_sub_account_id(), - }; - let full_access_key = match item.full_access_key { - Some(cli_full_access_key) => super::full_access_key::FullAccessKey::from( - cli_full_access_key, - connection_config, - sender_account_id, - )?, - None => super::full_access_key::FullAccessKey::choose_full_access_key( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { - sub_account_id, - full_access_key, - }) - } + pub sub_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + ///Specify a full access key for the sub-account + pub sub_account_full_access: super::full_access_key::SubAccountFullAccess, } impl SubAccount { - fn input_sub_account_id() -> near_primitives::types::AccountId { - Input::new() + fn input_sub_account_id( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("What is the sub-account ID?") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process( @@ -163,11 +29,11 @@ impl SubAccount { let mut actions = prepopulated_unsigned_transaction.actions.clone(); actions.push(action); let unsigned_transaction = near_primitives::transaction::Transaction { - receiver_id: self.sub_account_id.clone(), + receiver_id: self.sub_account_id.clone().into(), actions, ..prepopulated_unsigned_transaction }; - self.full_access_key + self.sub_account_full_access .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/add_command/sub_account/sender/mod.rs b/src/commands/add_command/sub_account/sender/mod.rs index 653c0191c..1e5c67b7a 100644 --- a/src/commands/add_command/sub_account/sender/mod.rs +++ b/src/commands/add_command/sub_account/sender/mod.rs @@ -1,95 +1,75 @@ use dialoguer::Input; -/// данные об отправителе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub owner_account_id: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::AddSubAccountCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] +pub struct Sender { + #[interactive_clap(skip_default_from_cli)] + pub owner_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + ///Specify a sub-account + pub sub_account: super::receiver::SubAccount, } -#[derive(Debug, Clone)] -pub struct Sender { - pub owner_account_id: near_primitives::types::AccountId, - pub send_to: super::receiver::SendTo, +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(owner_account_id) = &self.owner_account_id { - args.push_front(owner_account_id.to_string()); +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::AddSubAccountCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.owner_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - owner_account_id: Some(sender.owner_account_id), - send_to: Some(sender.send_to.into()), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let owner_account_id: near_primitives::types::AccountId = match item.owner_account_id { - Some(cli_owner_account_id) => match &connection_config { + fn from_cli_owner_account_id( + optional_cli_owner_account_id: Option, + context: &super::operation_mode::AddSubAccountCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_owner_account_id { + Some(cli_owner_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_owner_account_id.clone(), + &network_connection_config, + cli_owner_account_id.clone().into(), )? { - Some(_) => cli_owner_account_id, + Some(_) => Ok(cli_owner_account_id), None => { println!("Account <{}> doesn't exist", cli_owner_account_id); - Sender::input_owner_account_id(connection_config.clone())? + Sender::input_owner_account_id(&context) } }, - None => cli_owner_account_id, + None => Ok(cli_owner_account_id), }, - None => Sender::input_owner_account_id(connection_config.clone())?, - }; - let send_to: super::receiver::SendTo = match item.send_to { - Some(cli_send_to) => super::receiver::SendTo::from( - cli_send_to, - connection_config, - owner_account_id.clone(), - )?, - None => super::receiver::SendTo::send_to(connection_config, owner_account_id.clone())?, - }; - Ok(Self { - owner_account_id, - send_to, - }) + None => Self::input_owner_account_id(&context), + } } -} -impl Sender { fn input_owner_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::AddSubAccountCommandNetworkContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What is the owner account ID?") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -107,10 +87,11 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.owner_account_id.clone(), + signer_id: self.owner_account_id.clone().into(), + receiver_id: self.owner_account_id.clone().into(), ..prepopulated_unsigned_transaction }; - self.send_to + self.sub_account .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/construct_transaction_command/operation_mode/mod.rs b/src/commands/construct_transaction_command/operation_mode/mod.rs index daf2c6169..4a9802cda 100644 --- a/src/commands/construct_transaction_command/operation_mode/mod.rs +++ b/src/commands/construct_transaction_command/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { #[strum_discriminants(strum(message = "Yes, I keep it simple"))] + /// Prepare and, optionally, submit a new transaction with online mode Network(self::online_mode::NetworkArgs), #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] + /// Prepare and, optionally, submit a new transaction with offline mode Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -159,3 +52,7 @@ impl Mode { } } } + +pub struct ConstructTransactionNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/construct_transaction_command/operation_mode/offline_mode/mod.rs b/src/commands/construct_transaction_command/operation_mode/offline_mode/mod.rs index 775982073..41fedb007 100644 --- a/src/commands/construct_transaction_command/operation_mode/offline_mode/mod.rs +++ b/src/commands/construct_transaction_command/operation_mode/offline_mode/mod.rs @@ -1,56 +1,39 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + ///Specify a sender + sender: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +#[derive(Clone)] +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::ConstructTransactionNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.sender .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/construct_transaction_command/operation_mode/online_mode/mod.rs b/src/commands/construct_transaction_command/operation_mode/online_mode/mod.rs index 7c0e183ee..e5c9ed15d 100644 --- a/src/commands/construct_transaction_command/operation_mode/online_mode/mod.rs +++ b/src/commands/construct_transaction_command/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/mod.rs b/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/mod.rs index 838909edd..4d3bf7e90 100644 --- a/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,87 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { #[strum_discriminants(strum(message = "Testnet"))] + /// предоставление данных для сервера https://rpc.testnet.near.org Testnet(self::server::Server), #[strum_discriminants(strum(message = "Mainnet"))] + /// предоставление данных для сервера https://rpc.mainnet.near.org Mainnet(self::server::Server), #[strum_discriminants(strum(message = "Betanet"))] + /// предоставление данных для сервера https://rpc.betanet.near.org Betanet(self::server::Server), #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + /// предоставление данных для сервера, указанного вручную + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::ConstructTransactionNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/server/mod.rs index 1f093db43..7be999a48 100644 --- a/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/construct_transaction_command/operation_mode/online_mode/select_server/server/mod.rs @@ -1,200 +1,76 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + // pub connection_config: Option, + #[interactive_clap(named_arg)] + ///Specify a sender + pub sender: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::ConstructTransactionNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a sender + pub sender: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::ConstructTransactionNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { url: url.inner }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone())?, - None => SendFrom::choose_send_from(connection_config.clone())?, - }; - Ok(Server { - connection_config, - send_from, - }) - } -} - impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.sender + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a sender - Sender(crate::commands::construct_transaction_command::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Sender(crate::commands::construct_transaction_command::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Sender(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("sender".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Sender(sender) => Self::Sender(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Sender(cli_sender) => Ok(Self::Sender( - crate::commands::construct_transaction_command::sender::Sender::from( - cli_sender, - connection_config, - )?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::Sender(Default::default()), - connection_config, - )?) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::Sender(sender) => { - sender - .process(prepopulated_unsigned_transaction, connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.sender + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/construct_transaction_command/receiver/mod.rs b/src/commands/construct_transaction_command/receiver/mod.rs index c627f18c7..0e9663e30 100644 --- a/src/commands/construct_transaction_command/receiver/mod.rs +++ b/src/commands/construct_transaction_command/receiver/mod.rs @@ -1,157 +1,20 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify a receiver - Receiver(CliReceiver), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Receiver(Receiver), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Receiver(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("receiver".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Receiver(receiver) => Self::Receiver(CliReceiver::from(receiver)), - } - } -} - -impl SendTo { - pub fn from( - item: CliSendTo, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliSendTo::Receiver(cli_receiver) => { - let receiver = Receiver::from(cli_receiver, connection_config, sender_account_id)?; - Ok(Self::Receiver(receiver)) - } - } - } -} - -impl SendTo { - pub fn send_to( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendTo::Receiver(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - SendTo::Receiver(receiver) => { - receiver - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// данные о получателе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliReceiver { - receiver_account_id: Option, - #[clap(subcommand)] - action: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct Receiver { - pub receiver_account_id: near_primitives::types::AccountId, + pub receiver_account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] pub action: super::transaction_actions::NextAction, } -impl CliReceiver { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .action - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(receiver_account_id) = &self.receiver_account_id { - args.push_front(receiver_account_id.to_string()); - } - args - } -} - -impl From for CliReceiver { - fn from(receiver: Receiver) -> Self { - Self { - receiver_account_id: Some(receiver.receiver_account_id), - action: Some(super::transaction_actions::CliNextAction::from( - receiver.action, - )), - } - } -} - -impl Receiver { - fn from( - item: CliReceiver, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let receiver_account_id: near_primitives::types::AccountId = match item.receiver_account_id - { - Some(cli_receiver_account_id) => cli_receiver_account_id, - None => Receiver::input_receiver_account_id(), - }; - let action: super::transaction_actions::NextAction = match item.action { - Some(cli_next_action) => super::transaction_actions::NextAction::from_cli_next_action( - cli_next_action, - connection_config, - sender_account_id, - )?, - None => super::transaction_actions::NextAction::input_next_action( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { - receiver_account_id, - action, - }) - } -} - impl Receiver { - pub fn input_receiver_account_id() -> near_primitives::types::AccountId { - Input::new() + pub fn input_receiver_account_id( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("What is the account ID of the receiver?") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process( @@ -160,7 +23,7 @@ impl Receiver { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - receiver_id: self.receiver_account_id.clone(), + receiver_id: self.receiver_account_id.clone().into(), ..prepopulated_unsigned_transaction }; self.action diff --git a/src/commands/construct_transaction_command/sender/mod.rs b/src/commands/construct_transaction_command/sender/mod.rs index 07a48905a..f6e2f7de1 100644 --- a/src/commands/construct_transaction_command/sender/mod.rs +++ b/src/commands/construct_transaction_command/sender/mod.rs @@ -1,95 +1,76 @@ use dialoguer::Input; -/// данные об отправителе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::ConstructTransactionNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] +pub struct Sender { + #[interactive_clap(skip_default_from_cli)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + ///Specify a receiver + pub receiver: super::receiver::Receiver, } -#[derive(Debug, Clone)] -pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, - pub send_to: super::receiver::SendTo, +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::ConstructTransactionNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - send_to: Some(super::receiver::CliSendTo::from(sender.send_to)), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::ConstructTransactionNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let send_to: super::receiver::SendTo = match item.send_to { - Some(cli_send_to) => super::receiver::SendTo::from( - cli_send_to, - connection_config, - sender_account_id.clone(), - )?, - None => super::receiver::SendTo::send_to(connection_config, sender_account_id.clone())?, - }; - Ok(Self { - sender_account_id, - send_to, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::ConstructTransactionNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What is the account ID of the sender?") - .interact_text() - .unwrap(); + .interact_text()?; if let Some(connection_config) = &connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -107,10 +88,10 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; - self.send_to + self.receiver .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/construct_transaction_command/sign_transaction/mod.rs b/src/commands/construct_transaction_command/sign_transaction/mod.rs index 939c05726..477440a59 100644 --- a/src/commands/construct_transaction_command/sign_transaction/mod.rs +++ b/src/commands/construct_transaction_command/sign_transaction/mod.rs @@ -8,161 +8,35 @@ pub mod sign_with_keychain; pub mod sign_with_ledger; pub mod sign_with_private_key; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSignTransaction { - /// Provide arguments to sign a private key transaction - SignPrivateKey(self::sign_with_private_key::CliSignPrivateKey), - /// Provide arguments to sign a keychain transaction - SignWithKeychain(self::sign_with_keychain::CliSignKeychain), - #[cfg(feature = "ledger")] - /// Connect your Ledger device and sign transaction with it - SignWithLedger(self::sign_with_ledger::CliSignLedger), - /// Provide arguments to sign a manually transaction - SignManually(self::sign_manually::CliSignManually), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Would you like to sign the transaction? pub enum SignTransaction { + /// Provide arguments to sign a private key transaction #[strum_discriminants(strum( message = "Yes, I want to sign the transaction with a plain-text private key" ))] SignPrivateKey(self::sign_with_private_key::SignPrivateKey), + /// Provide arguments to sign a keychain transaction #[strum_discriminants(strum( message = "Yes, I want to sign the transaction with keychain (located in ~/.near-credentials)" ))] SignWithKeychain(self::sign_with_keychain::SignKeychain), + /// Connect your Ledger device and sign transaction with it #[cfg(feature = "ledger")] #[strum_discriminants(strum( message = "Yes, I want to sign the transaction with Ledger Nano S/X device" ))] SignWithLedger(self::sign_with_ledger::SignLedger), + /// Provide arguments to sign a manually transaction #[strum_discriminants(strum( message = "No, I want to construct the transaction and sign it somewhere else" ))] SignManually(self::sign_manually::SignManually), } -impl CliSignTransaction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - CliSignTransaction::SignPrivateKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("sign-private-key".to_owned()); - args - } - CliSignTransaction::SignWithKeychain(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("sign-with-keychain".to_owned()); - args - } - #[cfg(feature = "ledger")] - CliSignTransaction::SignWithLedger(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("sign-with-ledger".to_owned()); - args - } - CliSignTransaction::SignManually(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("sign-manually".to_owned()); - args - } - } - } -} - -impl From for CliSignTransaction { - fn from(sign_transaction: SignTransaction) -> Self { - match sign_transaction { - SignTransaction::SignPrivateKey(sign_with_private_key) => Self::SignPrivateKey( - self::sign_with_private_key::CliSignPrivateKey::from(sign_with_private_key), - ), - SignTransaction::SignWithKeychain(sign_with_keychain) => Self::SignWithKeychain( - self::sign_with_keychain::CliSignKeychain::from(sign_with_keychain), - ), - #[cfg(feature = "ledger")] - SignTransaction::SignWithLedger(sign_with_ledger) => Self::SignWithLedger( - self::sign_with_ledger::CliSignLedger::from(sign_with_ledger), - ), - SignTransaction::SignManually(sign_manually) => { - Self::SignManually(self::sign_manually::CliSignManually::from(sign_manually)) - } - } - } -} - impl SignTransaction { - pub fn from( - item: CliSignTransaction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliSignTransaction::SignPrivateKey(cli_private_key) => { - let private_key = self::sign_with_private_key::SignPrivateKey::from( - cli_private_key, - connection_config, - ); - Ok(SignTransaction::SignPrivateKey(private_key)) - } - CliSignTransaction::SignWithKeychain(cli_key_chain) => { - let key_chain = self::sign_with_keychain::SignKeychain::from( - cli_key_chain, - connection_config, - sender_account_id, - )?; - Ok(SignTransaction::SignWithKeychain(key_chain)) - } - #[cfg(feature = "ledger")] - CliSignTransaction::SignWithLedger(cli_ledger) => { - let ledger = - self::sign_with_ledger::SignLedger::from(cli_ledger, connection_config)?; - Ok(SignTransaction::SignWithLedger(ledger)) - } - CliSignTransaction::SignManually(cli_manually) => { - let manually = - self::sign_manually::SignManually::from(cli_manually, connection_config); - Ok(SignTransaction::SignManually(manually)) - } - } - } -} - -impl SignTransaction { - pub fn choose_sign_option( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - println!(); - let variants = SignTransactionDiscriminants::iter().collect::>(); - let sign_options = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_sign_options = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Would you like to sign the transaction?") - .items(&sign_options) - .default(0) - .interact() - .unwrap(); - let cli_sign_option = match variants[select_sign_options] { - SignTransactionDiscriminants::SignPrivateKey => { - CliSignTransaction::SignPrivateKey(Default::default()) - } - SignTransactionDiscriminants::SignWithKeychain => { - CliSignTransaction::SignWithKeychain(Default::default()) - } - #[cfg(feature = "ledger")] - SignTransactionDiscriminants::SignWithLedger => { - CliSignTransaction::SignWithLedger(Default::default()) - } - SignTransactionDiscriminants::SignManually => { - CliSignTransaction::SignManually(Default::default()) - } - }; - Self::from(cli_sign_option, connection_config, sender_account_id) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -193,23 +67,21 @@ impl SignTransaction { } } -fn input_signer_public_key() -> near_crypto::PublicKey { - Input::new() +fn input_signer_public_key() -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter sender (signer) public key") - .interact_text() - .unwrap() + .interact_text()?) } -fn input_signer_private_key() -> near_crypto::SecretKey { - Input::new() +fn input_signer_private_key() -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter sender (signer) private (secret) key") - .interact_text() - .unwrap() + .interact_text()?) } -fn input_access_key_nonce(public_key: &str) -> u64 { +fn input_access_key_nonce(public_key: &str) -> color_eyre::eyre::Result { println!("Your public key: `{}`", public_key); - Input::new() + Ok(Input::new() .with_prompt( "Enter transaction nonce for this public key (query the access key information with \ `./near-cli view nonce \ @@ -217,22 +89,22 @@ fn input_access_key_nonce(public_key: &str) -> u64 { account 'volodymyr.testnet' \ public-key ed25519:...` incremented by 1)", ) - .interact_text() - .unwrap() + .interact_text()?) } -fn input_block_hash() -> near_primitives::hash::CryptoHash { +fn input_block_hash() -> color_eyre::eyre::Result { let input_block_hash: crate::common::BlockHashAsBase58 = Input::new() .with_prompt( "Enter recent block hash (query information about the hash of the last block with \ `./near-cli view recent-block-hash network testnet`)", ) - .interact_text() - .unwrap(); - input_block_hash.inner + .interact_text()?; + Ok(crate::types::crypto_hash::CryptoHash( + input_block_hash.inner, + )) } -#[derive(Debug, EnumDiscriminants, Clone, clap::Clap)] +#[derive(Debug, EnumDiscriminants, Clone, clap::Clap, interactive_clap_derive::ToCliArgs)] #[strum_discriminants(derive(EnumMessage, EnumIter))] pub enum Submit { #[strum_discriminants(strum(message = "I want to send the transaction to the network"))] @@ -244,21 +116,6 @@ pub enum Submit { } impl Submit { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Send => { - let mut args = std::collections::VecDeque::new(); - args.push_front("send".to_owned()); - args - } - Self::Display => { - let mut args = std::collections::VecDeque::new(); - args.push_front("display".to_owned()); - args - } - } - } - pub fn choose_submit(connection_config: Option) -> Self { if connection_config.is_none() { return Submit::Display; diff --git a/src/commands/construct_transaction_command/sign_transaction/sign_manually/mod.rs b/src/commands/construct_transaction_command/sign_transaction/sign_manually/mod.rs index 16d00d532..0947ff7fb 100644 --- a/src/commands/construct_transaction_command/sign_transaction/sign_manually/mod.rs +++ b/src/commands/construct_transaction_command/sign_transaction/sign_manually/mod.rs @@ -1,86 +1,56 @@ use near_primitives::borsh::BorshSerialize; -/// подписание сформированной транзакции в режиме manually -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSignManually { - #[clap(long)] - signer_public_key: Option, - #[clap(long)] - nonce: Option, - #[clap(long)] - block_hash: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] +#[interactive_clap(skip_default_from_cli)] pub struct SignManually { - pub signer_public_key: near_crypto::PublicKey, + #[interactive_clap(long)] + pub signer_public_key: crate::types::public_key::PublicKey, + #[interactive_clap(long)] nonce: Option, - block_hash: Option, -} - -impl CliSignManually { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(signer_public_key) = &self.signer_public_key { - args.push_front(signer_public_key.to_string()); - args.push_front("--signer-public-key".to_owned()) - } - if let Some(nonce) = &self.nonce { - args.push_front(nonce.to_string()); - args.push_front("--nonce".to_owned()) - } - if let Some(block_hash) = &self.block_hash { - args.push_front(block_hash.to_string()); - args.push_front("--block-hash".to_owned()) - } - args - } -} - -impl From for CliSignManually { - fn from(sign_manually: SignManually) -> Self { - Self { - signer_public_key: Some(sign_manually.signer_public_key), - nonce: sign_manually.nonce, - block_hash: sign_manually.block_hash, - } - } + #[interactive_clap(long)] + block_hash: Option, } impl SignManually { - pub fn from( - item: CliSignManually, - connection_config: Option, - ) -> Self { - let signer_public_key: near_crypto::PublicKey = match item.signer_public_key { + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let signer_public_key: crate::types::public_key::PublicKey = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.signer_public_key) + { Some(cli_public_key) => cli_public_key, - None => super::input_signer_public_key(), + None => super::input_signer_public_key()?, }; match connection_config { - Some(_) => Self { + Some(_) => Ok(Self { signer_public_key, nonce: None, block_hash: None, - }, + }), None => { - let nonce: u64 = match item.nonce { + let nonce: u64 = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.nonce) + { Some(cli_nonce) => cli_nonce, - None => super::input_access_key_nonce(&signer_public_key.to_string()), + None => super::input_access_key_nonce(&signer_public_key.to_string())?, }; - let block_hash = match item.block_hash { + let block_hash = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.block_hash) + { Some(cli_block_hash) => cli_block_hash, - None => super::input_block_hash(), + None => super::input_block_hash()?, }; - Self { + Ok(Self { signer_public_key, nonce: Some(nonce), block_hash: Some(block_hash), - } + }) } } } @@ -96,13 +66,13 @@ impl SignManually { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, ) -> color_eyre::eyre::Result> { - let public_key: near_crypto::PublicKey = self.signer_public_key.clone(); + let public_key: near_crypto::PublicKey = self.signer_public_key.0.clone(); let unsigned_transaction = match network_connection_config { None => near_primitives::transaction::Transaction { public_key, nonce: self.nonce.unwrap_or_default().clone(), - block_hash: self.block_hash.unwrap_or_default().clone(), + block_hash: self.block_hash.unwrap_or_default().0.clone(), ..prepopulated_unsigned_transaction }, Some(network_connection_config) => { diff --git a/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs b/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs index eb4d7e426..6a36210bc 100644 --- a/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs +++ b/src/commands/construct_transaction_command/sign_transaction/sign_with_keychain/mod.rs @@ -2,66 +2,31 @@ extern crate dirs; use serde::Deserialize; -/// подписание сформированной транзакции с помощью файла с ключами -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSignKeychain { - #[clap(long)] - nonce: Option, - #[clap(long)] - block_hash: Option, - #[clap(subcommand)] - submit: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] +#[interactive_clap(skip_default_from_cli)] pub struct SignKeychain { + #[interactive_clap(long)] nonce: Option, - block_hash: Option, + #[interactive_clap(long)] + block_hash: Option, + #[interactive_clap(subcommand)] pub submit: Option, } -impl CliSignKeychain { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .submit - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(nonce) = &self.nonce { - args.push_front(nonce.to_string()); - args.push_front("--nonce".to_owned()) - } - if let Some(block_hash) = &self.block_hash { - args.push_front(block_hash.to_string()); - args.push_front("--block-hash".to_owned()) - } - args - } -} - -impl From for CliSignKeychain { - fn from(sign_keychain: SignKeychain) -> Self { - Self { - nonce: sign_keychain.nonce, - block_hash: sign_keychain.block_hash, - submit: sign_keychain.submit, - } - } +impl interactive_clap::ToCli for super::Submit { + type CliVariant = super::Submit; } impl SignKeychain { - pub fn from( - item: CliSignKeychain, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let submit: Option = item.submit; - match connection_config { + let submit: Option = optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.submit); + match context.connection_config { Some(_) => Ok(Self { nonce: None, block_hash: None, @@ -69,7 +34,11 @@ impl SignKeychain { }), None => { let home_dir = dirs::home_dir().expect("Impossible to get your home dir!"); - let file_name = format!("{}.json", sender_account_id); + let file_name = format!( + "{}.json", + context.signer_account_id // .clone() + // .expect("wrong sender_account_id") + ); let mut path = std::path::PathBuf::from(&home_dir); let dir_name = crate::consts::DIR_NAME_KEY_CHAIN; path.push(dir_name); @@ -84,14 +53,18 @@ impl SignKeychain { )) })?; - let nonce: u64 = match item.nonce { + let nonce: u64 = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.nonce) + { Some(cli_nonce) => cli_nonce, - None => super::input_access_key_nonce(&account_json.public_key.to_string()), - }; - let block_hash = match item.block_hash { - Some(cli_block_hash) => cli_block_hash, - None => super::input_block_hash(), + None => super::input_access_key_nonce(&account_json.public_key.to_string())?, }; + let block_hash = + match optional_clap_variant.and_then(|clap_variant| clap_variant.block_hash) { + Some(cli_block_hash) => cli_block_hash, + None => super::input_block_hash()?, + }; Ok(SignKeychain { nonce: Some(nonce), block_hash: Some(block_hash), @@ -212,8 +185,8 @@ impl SignKeychain { let account_json: User = serde_json::from_str(&data) .map_err(|err| color_eyre::Report::msg(format!("Error reading data: {}", err)))?; let sign_with_private_key = super::sign_with_private_key::SignPrivateKey { - signer_public_key: account_json.public_key, - signer_private_key: account_json.private_key, + signer_public_key: crate::types::public_key::PublicKey(account_json.public_key), + signer_private_key: crate::types::secret_key::SecretKey(account_json.private_key), nonce: self.nonce.clone(), block_hash: self.block_hash.clone(), submit: self.submit.clone(), diff --git a/src/commands/construct_transaction_command/sign_transaction/sign_with_ledger/mod.rs b/src/commands/construct_transaction_command/sign_transaction/sign_with_ledger/mod.rs index 7ae88b667..e35652829 100644 --- a/src/commands/construct_transaction_command/sign_transaction/sign_with_ledger/mod.rs +++ b/src/commands/construct_transaction_command/sign_transaction/sign_with_ledger/mod.rs @@ -1,69 +1,37 @@ use dialoguer::Input; +use interactive_clap::ToCli; use near_primitives::borsh::BorshSerialize; -/// Sign constructed transaction with Ledger -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSignLedger { - #[clap(long)] - seed_phrase_hd_path: Option, - #[clap(long)] - nonce: Option, - #[clap(long)] - block_hash: Option, - #[clap(subcommand)] - submit: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] +#[interactive_clap(skip_default_from_cli)] pub struct SignLedger { - pub seed_phrase_hd_path: slip10::BIP32Path, - pub signer_public_key: near_crypto::PublicKey, + #[interactive_clap(long)] + pub seed_phrase_hd_path: crate::types::slip10::BIP32Path, + #[interactive_clap(skip)] + pub signer_public_key: crate::types::public_key::PublicKey, + #[interactive_clap(long)] nonce: Option, - block_hash: Option, + #[interactive_clap(long)] + block_hash: Option, + #[interactive_clap(subcommand)] pub submit: Option, } -impl CliSignLedger { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .submit - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(nonce) = &self.nonce { - args.push_front(nonce.to_string()); - args.push_front("--nonce".to_owned()) - } - if let Some(block_hash) = &self.block_hash { - args.push_front(block_hash.to_string()); - args.push_front("--block-hash".to_owned()) - } - args - } -} - -impl From for CliSignLedger { - fn from(sign_ledger: SignLedger) -> Self { - Self { - seed_phrase_hd_path: Some(sign_ledger.seed_phrase_hd_path), - nonce: sign_ledger.nonce, - block_hash: sign_ledger.block_hash, - submit: sign_ledger.submit.into(), - } - } +impl ToCli for crate::types::slip10::BIP32Path { + type CliVariant = crate::types::slip10::BIP32Path; } impl SignLedger { - pub fn from( - item: CliSignLedger, - connection_config: Option, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let seed_phrase_hd_path = match item.seed_phrase_hd_path { + let connection_config = context.connection_config.clone(); + let seed_phrase_hd_path = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.seed_phrase_hd_path) + { Some(hd_path) => hd_path, None => SignLedger::input_seed_phrase_hd_path(), }; @@ -72,17 +40,27 @@ impl SignLedger { seed_phrase_hd_path ); let public_key = actix::System::new() - .block_on(async { near_ledger::get_public_key(seed_phrase_hd_path.clone()).await }) + .block_on(async { + near_ledger::get_public_key(seed_phrase_hd_path.clone().into()).await + }) .map_err(|near_ledger_error| { color_eyre::Report::msg(format!( "An error occurred while trying to get PublicKey from Ledger device: {:?}", near_ledger_error )) })?; - let signer_public_key = near_crypto::PublicKey::ED25519( - near_crypto::ED25519PublicKey::from(public_key.to_bytes()), - ); - let submit: Option = item.submit; + let signer_public_key: crate::types::public_key::PublicKey = + near_crypto::PublicKey::ED25519(near_crypto::ED25519PublicKey::from( + public_key.to_bytes(), + )) + .into(); + let submit: Option = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.submit) + { + Some(submit) => Some(submit), + None => None, + }; match connection_config { Some(_) => Ok(Self { seed_phrase_hd_path, @@ -92,13 +70,19 @@ impl SignLedger { submit, }), None => { - let nonce: u64 = match item.nonce { + let nonce: u64 = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.nonce) + { Some(cli_nonce) => cli_nonce, - None => super::input_access_key_nonce(&signer_public_key.to_string().clone()), + None => super::input_access_key_nonce(&signer_public_key.to_string())?, }; - let block_hash = match item.block_hash { + let block_hash = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.block_hash) + { Some(cli_block_hash) => cli_block_hash, - None => super::input_block_hash(), + None => super::input_block_hash()?, }; Ok(Self { seed_phrase_hd_path, @@ -117,7 +101,7 @@ impl SignLedger { near_jsonrpc_client::new_client(&selected_server_url) } - pub fn input_seed_phrase_hd_path() -> slip10::BIP32Path { + pub fn input_seed_phrase_hd_path() -> crate::types::slip10::BIP32Path { Input::new() .with_prompt("Enter seed phrase HD Path (if you not sure leave blank for default)") .with_initial_text("44'/397'/0'/0'/1'") @@ -130,10 +114,11 @@ impl SignLedger { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, connection_config: Option, ) -> color_eyre::eyre::Result> { - let seed_phrase_hd_path = self.seed_phrase_hd_path.clone(); - let public_key = self.signer_public_key.clone(); + let seed_phrase_hd_path: slip10::BIP32Path = self.seed_phrase_hd_path.clone().into(); + let public_key: near_crypto::PublicKey = self.signer_public_key.clone().into(); let nonce = self.nonce.unwrap_or_default().clone(); - let block_hash = self.block_hash.unwrap_or_default().clone(); + let block_hash: near_primitives::hash::CryptoHash = + self.clone().block_hash.unwrap_or_default().into(); let submit: Option = self.submit.clone(); match connection_config.clone() { None => { diff --git a/src/commands/construct_transaction_command/sign_transaction/sign_with_private_key/mod.rs b/src/commands/construct_transaction_command/sign_transaction/sign_with_private_key/mod.rs index c93f98dfb..3c7c49f13 100644 --- a/src/commands/construct_transaction_command/sign_transaction/sign_with_private_key/mod.rs +++ b/src/commands/construct_transaction_command/sign_transaction/sign_with_private_key/mod.rs @@ -1,129 +1,97 @@ use near_primitives::borsh::BorshSerialize; -/// подписание сформированной транзакции с помощью личных ключей -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSignPrivateKey { - #[clap(long)] - signer_public_key: Option, - #[clap(long)] - signer_private_key: Option, - #[clap(long)] - nonce: Option, - #[clap(long)] - block_hash: Option, - #[clap(subcommand)] - submit: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] +#[interactive_clap(skip_default_from_cli)] pub struct SignPrivateKey { - pub signer_public_key: near_crypto::PublicKey, - pub signer_private_key: near_crypto::SecretKey, + #[interactive_clap(long)] + pub signer_public_key: crate::types::public_key::PublicKey, + #[interactive_clap(long)] + pub signer_private_key: crate::types::secret_key::SecretKey, + #[interactive_clap(long)] pub nonce: Option, - pub block_hash: Option, + #[interactive_clap(long)] + pub block_hash: Option, + #[interactive_clap(subcommand)] pub submit: Option, } -impl CliSignPrivateKey { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .submit - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(block_hash) = &self.block_hash { - args.push_front(block_hash.to_string()); - args.push_front("--block-hash".to_owned()) - } - if let Some(nonce) = &self.nonce { - args.push_front(nonce.to_string()); - args.push_front("--nonce".to_owned()) - } - if let Some(signer_secret_key) = &self.signer_private_key { - args.push_front(signer_secret_key.to_string()); - args.push_front("--signer-private-key".to_owned()) - } - if let Some(signer_public_key) = &self.signer_public_key { - args.push_front(signer_public_key.to_string()); - args.push_front("--signer-public-key".to_owned()) - } - args - } -} - -impl From for CliSignPrivateKey { - fn from(sign_private_key: SignPrivateKey) -> Self { - Self { - signer_public_key: Some(sign_private_key.signer_public_key), - signer_private_key: Some(sign_private_key.signer_private_key), - nonce: sign_private_key.nonce, - block_hash: sign_private_key.block_hash, - submit: sign_private_key.submit, - } - } -} - impl SignPrivateKey { - pub fn from( - item: CliSignPrivateKey, - connection_config: Option, - ) -> Self { - let signer_public_key: near_crypto::PublicKey = match item.signer_public_key { + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let signer_public_key: crate::types::public_key::PublicKey = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.signer_public_key) + { Some(cli_public_key) => cli_public_key, - None => super::input_signer_public_key(), + None => super::input_signer_public_key()?, }; - let signer_private_key: near_crypto::SecretKey = match item.signer_private_key { + let signer_private_key: crate::types::secret_key::SecretKey = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.signer_private_key) + { Some(signer_private_key) => signer_private_key, - None => super::input_signer_private_key(), + None => super::input_signer_private_key()?, + }; + let submit: Option = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.submit) + { + Some(submit) => Some(submit), + None => None, }; - let submit: Option = item.submit; + match connection_config { - Some(_) => Self { + Some(_) => Ok(Self { signer_public_key, signer_private_key, nonce: None, block_hash: None, submit, - }, + }), None => { - let nonce: u64 = match item.nonce { + let nonce: u64 = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.nonce) + { Some(cli_nonce) => cli_nonce, - None => super::input_access_key_nonce(&signer_public_key.to_string()), + None => super::input_access_key_nonce(&signer_public_key.to_string())?, }; - let block_hash = match item.block_hash { + let block_hash = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.block_hash) + { Some(cli_block_hash) => cli_block_hash, - None => super::input_block_hash(), + None => super::input_block_hash()?, }; let public_key_origin: near_crypto::PublicKey = - near_crypto::SecretKey::public_key(&signer_private_key); - if &signer_public_key == &public_key_origin { - Self { + near_crypto::SecretKey::public_key(&signer_private_key.clone().into()); + if &signer_public_key.0 == &public_key_origin { + Ok(Self { signer_public_key, signer_private_key, nonce: Some(nonce), block_hash: Some(block_hash), submit, - } + }) } else { println!("\nError: The key pair does not match. Re-enter the keys.\n"); - let signer_public_key: near_crypto::PublicKey = - super::input_signer_public_key(); - let signer_secret_key: near_crypto::SecretKey = - super::input_signer_private_key(); - Self::from( - CliSignPrivateKey { + let signer_public_key: crate::types::public_key::PublicKey = + super::input_signer_public_key()?; + let signer_secret_key: crate::types::secret_key::SecretKey = + super::input_signer_private_key()?; + Self::from_cli( + Some(CliSignPrivateKey { signer_public_key: Some(signer_public_key), signer_private_key: Some(signer_secret_key), nonce: Some(nonce), block_hash: Some(block_hash), submit: None, - }, - connection_config, + }), + context, ) } } @@ -141,11 +109,11 @@ impl SignPrivateKey { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, connection_config: Option, ) -> color_eyre::eyre::Result> { - let public_key: near_crypto::PublicKey = self.signer_public_key.clone(); - let signer_secret_key: near_crypto::SecretKey = self.signer_private_key.clone(); + let public_key: near_crypto::PublicKey = self.signer_public_key.0.clone(); + let signer_secret_key: near_crypto::SecretKey = self.signer_private_key.clone().into(); let nonce: u64 = self.nonce.unwrap_or_default().clone(); let block_hash: near_primitives::hash::CryptoHash = - self.block_hash.unwrap_or_default().clone(); + self.clone().block_hash.unwrap_or_default().0; let submit: Option = self.submit.clone(); match connection_config.clone() { None => { diff --git a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/full_access_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/full_access_type/mod.rs index 41251ad7c..5a3869240 100644 --- a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/full_access_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/full_access_type/mod.rs @@ -17,6 +17,10 @@ pub struct FullAccessType { pub next_action: Box, } +impl interactive_clap::ToCli for FullAccessType { + type CliVariant = CliFullAccessType; +} + impl CliFullAccessType { pub fn to_cli_args(&self) -> std::collections::VecDeque { self.next_action @@ -37,21 +41,18 @@ impl From for CliFullAccessType { } impl FullAccessType { - pub fn from( - item: CliFullAccessType, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let skip_next_action: super::super::super::NextAction = match item.next_action { + let skip_next_action: super::super::super::NextAction = match optional_clap_variant + .and_then(|clap_variant| clap_variant.next_action) + { Some(cli_skip_action) => super::super::super::NextAction::from_cli_skip_next_action( cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::super::super::NextAction::input_next_action( - connection_config, - sender_account_id, + context, )?, + None => super::super::super::NextAction::choose_variant(context)?, }; Ok(Self { next_action: Box::new(skip_next_action), diff --git a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/function_call_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/function_call_type/mod.rs index d4289a9ae..f4ce4cb27 100644 --- a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/function_call_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/function_call_type/mod.rs @@ -13,7 +13,7 @@ pub struct CliFunctionCallType { #[clap(long)] allowance: Option, #[clap(long)] - receiver_id: Option, + pub receiver_account_id: Option, #[clap(long)] method_names: Option, #[clap(subcommand)] @@ -22,12 +22,16 @@ pub struct CliFunctionCallType { #[derive(Debug, Clone)] pub struct FunctionCallType { - pub allowance: Option, - pub receiver_id: near_primitives::types::AccountId, + pub allowance: Option, + pub receiver_account_id: crate::types::account_id::AccountId, pub method_names: Vec, pub next_action: Box, } +impl interactive_clap::ToCli for FunctionCallType { + type CliVariant = CliFunctionCallType; +} + impl CliFunctionCallType { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -43,9 +47,9 @@ impl CliFunctionCallType { args.push_front(allowance.to_string()); args.push_front("--allowance".to_owned()) }; - if let Some(receiver_id) = &self.receiver_id { + if let Some(receiver_id) = &self.receiver_account_id { args.push_front(receiver_id.to_string()); - args.push_front("--receiver-id".to_owned()) + args.push_front("--receiver-account-id".to_owned()) }; args } @@ -54,10 +58,8 @@ impl CliFunctionCallType { impl From for CliFunctionCallType { fn from(function_call_type: FunctionCallType) -> Self { Self { - allowance: Some(crate::common::NearBalance::from_yoctonear( - function_call_type.allowance.unwrap_or_default(), - )), - receiver_id: Some(function_call_type.receiver_id), + allowance: function_call_type.allowance, + receiver_account_id: Some(function_call_type.receiver_account_id), method_names: Some(function_call_type.method_names.join(", ")), next_action: Some(super::super::super::CliSkipNextAction::Skip( super::super::super::CliSkipAction { sign_option: None }, @@ -67,20 +69,41 @@ impl From for CliFunctionCallType { } impl FunctionCallType { - pub fn from( - item: CliFunctionCallType, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let allowance: Option = match item.allowance { - Some(cli_allowance) => Some(cli_allowance.to_yoctonear()), - None => FunctionCallType::input_allowance(), + let connection_config = context.connection_config.clone(); + let allowance: Option = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.allowance) + { + Some(cli_allowance) => Some(cli_allowance), + None => FunctionCallType::input_allowance(&context)?, }; - let receiver_id: near_primitives::types::AccountId = match item.receiver_id { - Some(cli_receiver_id) => near_primitives::types::AccountId::from(cli_receiver_id), - None => FunctionCallType::input_receiver_id(), + let receiver_account_id = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.receiver_account_id) + { + Some(receiver_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::get_account_state( + &network_connection_config, + receiver_account_id.clone().into(), + )? { + Some(_) => receiver_account_id, + None => { + println!("Account <{}> doesn't exist", receiver_account_id); + Self::input_receiver_account_id(&context)? + } + }, + None => receiver_account_id, + }, + None => Self::input_receiver_account_id(&context)?, }; - let method_names: Vec = match item.method_names { + let method_names: Vec = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.method_names) + { Some(cli_method_names) => { if cli_method_names.is_empty() { vec![] @@ -91,22 +114,20 @@ impl FunctionCallType { .collect::>() } } - None => FunctionCallType::input_method_names(), + None => FunctionCallType::input_method_names(&context)?, }; - let skip_next_action: super::super::super::NextAction = match item.next_action { + let skip_next_action: super::super::super::NextAction = match optional_clap_variant + .and_then(|clap_variant| clap_variant.next_action) + { Some(cli_skip_action) => super::super::super::NextAction::from_cli_skip_next_action( cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::super::super::NextAction::input_next_action( - connection_config, - sender_account_id, + context, )?, + None => super::super::super::NextAction::choose_variant(context)?, }; Ok(Self { allowance, - receiver_id, + receiver_account_id, method_names, next_action: Box::new(skip_next_action), }) @@ -114,7 +135,9 @@ impl FunctionCallType { } impl FunctionCallType { - pub fn input_method_names() -> Vec { + pub fn input_method_names( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result> { println!(); let choose_input = vec![ "Yes, I want to input a list of method names that can be used", @@ -124,32 +147,33 @@ impl FunctionCallType { .with_prompt("Do You want to input a list of method names that can be used") .items(&choose_input) .default(0) - .interact_on_opt(&Term::stderr()) - .unwrap(); + .interact_on_opt(&Term::stderr())?; match select_choose_input { Some(0) => { let mut input_method_names: String = Input::new() - .with_prompt("Enter a list of method names that can be used. The access key only allows transactions with the function call of one of the given method names. Empty list means any method name can be used.") + .with_prompt("Enter a comma-separated list of method names that will be allowed to be called in a transaction signed by this access key.") .interact_text() - .unwrap(); + ?; if input_method_names.contains("\"") { input_method_names.clear() }; if input_method_names.is_empty() { - vec![] + Ok(vec![]) } else { - input_method_names + Ok(input_method_names .split(',') .map(String::from) - .collect::>() + .collect::>()) } } - Some(1) => vec![], + Some(1) => Ok(vec![]), _ => unreachable!("Error"), } } - pub fn input_allowance() -> Option { + pub fn input_allowance( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result> { println!(); let choose_input = vec![ "Yes, I want to input allowance for receiver ID", @@ -159,27 +183,45 @@ impl FunctionCallType { .with_prompt("Do You want to input an allowance for receiver ID") .items(&choose_input) .default(0) - .interact_on_opt(&Term::stderr()) - .unwrap(); + .interact_on_opt(&Term::stderr())?; match select_choose_input { Some(0) => { let allowance_near_balance: crate::common::NearBalance = Input::new() .with_prompt("Enter an allowance which is a balance limit to use by this access key to pay for function call gas and transaction fees.") .interact_text() - .unwrap(); - Some(allowance_near_balance.to_yoctonear()) + ?; + Ok(Some(allowance_near_balance)) } - Some(1) => None, + Some(1) => Ok(None), _ => unreachable!("Error"), } } - pub fn input_receiver_id() -> near_primitives::types::AccountId { - println!(); - Input::new() - .with_prompt("Enter a receiver to use by this access key to pay for function call gas and transaction fees.") - .interact_text() - .unwrap() + pub fn input_receiver_account_id( + context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + loop { + let account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("Enter a receiver to use by this access key to pay for function call gas and transaction fees.") + .interact_text() + ?; + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::get_account_state(&connection_config, account_id.clone().into())? + { + break Ok(account_id); + } else { + if !crate::common::is_64_len_hex(&account_id) { + println!("Account <{}> doesn't exist", account_id.to_string()); + } else { + break Ok(account_id); + } + } + } else { + break Ok(account_id); + } + } } #[async_recursion(?Send)] @@ -194,8 +236,13 @@ impl FunctionCallType { nonce, permission: near_primitives::account::AccessKeyPermission::FunctionCall( near_primitives::account::FunctionCallPermission { - allowance: self.allowance.clone(), - receiver_id: self.receiver_id.to_string().clone(), + allowance: { + match self.allowance.clone() { + Some(allowance) => Some(allowance.to_yoctonear()), + None => None, + } + }, + receiver_id: self.receiver_account_id.to_string().clone(), method_names: self.method_names.clone(), }, ), diff --git a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/mod.rs b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/mod.rs index 23b43059a..bff353349 100644 --- a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/add_access_key/mod.rs @@ -1,96 +1,32 @@ use async_recursion::async_recursion; -use dialoguer::{theme::ColorfulTheme, Input, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use dialoguer::Input; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod full_access_type; mod function_call_type; -/// добавление ключа пользователю -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliAddAccessKeyAction { - public_key: Option, - nonce: Option, - #[clap(subcommand)] - permission: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct AddAccessKeyAction { - pub public_key: near_crypto::PublicKey, + pub public_key: crate::types::public_key::PublicKey, pub nonce: near_primitives::types::Nonce, + #[interactive_clap(subcommand)] pub permission: AccessKeyPermission, } -impl CliAddAccessKeyAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .permission - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(public_key) = &self.public_key { - args.push_front(public_key.to_string()); - }; - args - } -} - -impl From for CliAddAccessKeyAction { - fn from(add_access_key_action: AddAccessKeyAction) -> Self { - Self { - public_key: Some(add_access_key_action.public_key), - nonce: Some(add_access_key_action.nonce), - permission: Some(add_access_key_action.permission.into()), - } - } -} - impl AddAccessKeyAction { - pub fn from( - item: CliAddAccessKeyAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let public_key: near_crypto::PublicKey = match item.public_key { - Some(cli_public_key) => cli_public_key, - None => AddAccessKeyAction::input_public_key(), - }; - let nonce: near_primitives::types::Nonce = match item.nonce { - Some(cli_nonce) => near_primitives::types::Nonce::from(cli_nonce), - None => AddAccessKeyAction::input_access_key_nonce(), - }; - let permission: AccessKeyPermission = match item.permission { - Some(cli_permission) => { - AccessKeyPermission::from(cli_permission, connection_config, sender_account_id)? - } - None => AccessKeyPermission::choose_permission(connection_config, sender_account_id)?, - }; - Ok(Self { - public_key, - nonce, - permission, - }) - } -} - -impl AddAccessKeyAction { - fn input_access_key_nonce() -> near_primitives::types::Nonce { - Input::new() - .with_prompt("Enter the nonce for this access key") - .interact_text() - .unwrap() + fn input_public_key( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("Enter a public key for this access key") + .interact_text()?) } - fn input_public_key() -> near_crypto::PublicKey { - Input::new() - .with_prompt("Enter a public key for this access key") - .interact_text() - .unwrap() + fn input_nonce( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(0) } #[async_recursion(?Send)] @@ -106,7 +42,7 @@ impl AddAccessKeyAction { self.nonce, prepopulated_unsigned_transaction, network_connection_config, - self.public_key, + self.public_key.into(), ) .await } @@ -116,7 +52,7 @@ impl AddAccessKeyAction { self.nonce, prepopulated_unsigned_transaction, network_connection_config, - self.public_key, + self.public_key.into(), ) .await } @@ -124,109 +60,15 @@ impl AddAccessKeyAction { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliAccessKeyPermission { - /// Предоставьте данные для ключа с function call - GrantFunctionCallAccess(self::function_call_type::CliFunctionCallType), - /// Предоставьте данные для ключа с полным доступом - GrantFullAccess(self::full_access_type::CliFullAccessType), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Select a permission that you want to add to the access key pub enum AccessKeyPermission { #[strum_discriminants(strum(message = "A permission with function call"))] + /// Предоставьте данные для ключа с function call GrantFunctionCallAccess(self::function_call_type::FunctionCallType), #[strum_discriminants(strum(message = "A permission with full access"))] + /// Предоставьте данные для ключа с полным доступом GrantFullAccess(self::full_access_type::FullAccessType), } - -impl CliAccessKeyPermission { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::GrantFunctionCallAccess(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("grant-function-call-access".to_owned()); - args - } - Self::GrantFullAccess(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("grant-full-access".to_owned()); - args - } - } - } -} - -impl From for CliAccessKeyPermission { - fn from(access_key_permission: AccessKeyPermission) -> Self { - match access_key_permission { - AccessKeyPermission::GrantFunctionCallAccess(function_call_type) => { - Self::GrantFunctionCallAccess(function_call_type.into()) - } - AccessKeyPermission::GrantFullAccess(full_access_type) => { - Self::GrantFullAccess(full_access_type.into()) - } - } - } -} - -impl AccessKeyPermission { - pub fn from( - item: CliAccessKeyPermission, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliAccessKeyPermission::GrantFunctionCallAccess(cli_function_call_type) => { - let function_call_type = self::function_call_type::FunctionCallType::from( - cli_function_call_type, - connection_config, - sender_account_id, - )?; - Ok(AccessKeyPermission::GrantFunctionCallAccess( - function_call_type, - )) - } - CliAccessKeyPermission::GrantFullAccess(cli_full_access_type) => { - let full_access_type = self::full_access_type::FullAccessType::from( - cli_full_access_type, - connection_config, - sender_account_id, - )?; - Ok(AccessKeyPermission::GrantFullAccess(full_access_type)) - } - } - } -} - -impl AccessKeyPermission { - pub fn choose_permission( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let variants = AccessKeyPermissionDiscriminants::iter().collect::>(); - let permissions = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_permission = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select a permission that you want to add to the access key:") - .items(&permissions) - .default(0) - .interact() - .unwrap(); - match variants[select_permission] { - AccessKeyPermissionDiscriminants::GrantFunctionCallAccess => Ok(Self::from( - CliAccessKeyPermission::GrantFunctionCallAccess(Default::default()), - connection_config, - sender_account_id, - )?), - AccessKeyPermissionDiscriminants::GrantFullAccess => Ok(Self::from( - CliAccessKeyPermission::GrantFullAccess(Default::default()), - connection_config, - sender_account_id, - )?), - } - } -} diff --git a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/generate_keypair/mod.rs b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/generate_keypair/mod.rs index 4f3088b02..8629c2424 100644 --- a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/generate_keypair/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/generate_keypair/mod.rs @@ -1,63 +1,12 @@ use std::str::FromStr; -/// Generate a key pair of private and public keys (use it anywhere you need -/// Ed25519 keys) -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliGenerateKeypair { - #[clap(subcommand)] - permission: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct GenerateKeypair { + #[interactive_clap(subcommand)] pub permission: super::add_access_key::AccessKeyPermission, } -impl CliGenerateKeypair { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let args = self - .permission - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args - } -} - -impl From for CliGenerateKeypair { - fn from(generate_keypair: GenerateKeypair) -> Self { - Self { - permission: Some(generate_keypair.permission.into()), - } - } -} - -impl GenerateKeypair { - pub fn from( - item: CliGenerateKeypair, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let permission: super::add_access_key::AccessKeyPermission = match item.permission { - Some(cli_permission) => super::add_access_key::AccessKeyPermission::from( - cli_permission, - connection_config, - sender_account_id, - )?, - None => super::add_access_key::AccessKeyPermission::choose_permission( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { permission }) - } -} - impl GenerateKeypair { pub async fn process( self, diff --git a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/mod.rs b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/mod.rs index bb003d1c2..01d481db6 100644 --- a/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/add_access_key_mode/mod.rs @@ -1,59 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod add_access_key; mod generate_keypair; -/// данные об отправителе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliAddAccessKeyMode { - #[clap(subcommand)] - public_key_mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct AddAccessKeyMode { + #[interactive_clap(subcommand)] pub public_key_mode: PublicKeyMode, } -impl CliAddAccessKeyMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.public_key_mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliAddAccessKeyMode { - fn from(add_access_key_mode: AddAccessKeyMode) -> Self { - Self { - public_key_mode: Some(add_access_key_mode.public_key_mode.into()), - } - } -} - -impl AddAccessKeyMode { - pub fn from( - item: CliAddAccessKeyMode, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let public_key_mode = match item.public_key_mode { - Some(cli_public_key_mode) => { - PublicKeyMode::from(cli_public_key_mode, connection_config, sender_account_id)? - } - None => PublicKeyMode::choose_public_key_mode(connection_config, sender_account_id)?, - }; - Ok(Self { public_key_mode }) - } -} - impl AddAccessKeyMode { pub async fn process( self, @@ -66,108 +22,20 @@ impl AddAccessKeyMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliPublicKeyMode { - /// Enter public key - PublicKey(self::add_access_key::CliAddAccessKeyAction), - /// Generate key pair - GenerateKeypair(self::generate_keypair::CliGenerateKeypair), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Select the mode for the public key pub enum PublicKeyMode { #[strum_discriminants(strum(message = "Enter public key"))] + /// Enter public key PublicKey(self::add_access_key::AddAccessKeyAction), #[strum_discriminants(strum(message = "Generate key pair"))] + /// Generate key pair GenerateKeypair(self::generate_keypair::GenerateKeypair), } impl PublicKeyMode { - fn from( - item: CliPublicKeyMode, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliPublicKeyMode::PublicKey(cli_add_access_key_action) => Ok(PublicKeyMode::PublicKey( - self::add_access_key::AddAccessKeyAction::from( - cli_add_access_key_action, - connection_config, - sender_account_id, - )?, - )), - CliPublicKeyMode::GenerateKeypair(cli_generate_keypair) => Ok( - PublicKeyMode::GenerateKeypair(self::generate_keypair::GenerateKeypair::from( - cli_generate_keypair, - connection_config, - sender_account_id, - )?), - ), - } - } -} - -impl CliPublicKeyMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::PublicKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("public-key".to_owned()); - args - } - Self::GenerateKeypair(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("generate-keypair".to_owned()); - args - } - } - } -} - -impl From for CliPublicKeyMode { - fn from(public_key_mode: PublicKeyMode) -> Self { - match public_key_mode { - PublicKeyMode::PublicKey(add_access_key_action) => { - Self::PublicKey(add_access_key_action.into()) - } - PublicKeyMode::GenerateKeypair(generate_keypair) => { - Self::GenerateKeypair(generate_keypair.into()) - } - } - } -} - -impl PublicKeyMode { - fn choose_public_key_mode( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let variants = PublicKeyModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select a permission that you want to add to the access key:") - .items(&modes) - .default(0) - .interact() - .unwrap(); - match variants[select_mode] { - PublicKeyModeDiscriminants::PublicKey => Ok(Self::from( - CliPublicKeyMode::PublicKey(Default::default()), - connection_config, - sender_account_id, - )?), - PublicKeyModeDiscriminants::GenerateKeypair => Ok(Self::from( - CliPublicKeyMode::GenerateKeypair(Default::default()), - connection_config, - sender_account_id, - )?), - } - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, diff --git a/src/commands/construct_transaction_command/transaction_actions/add_contract_code_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/add_contract_code_type/mod.rs index 01dd95d8e..86d3d60d6 100644 --- a/src/commands/construct_transaction_command/transaction_actions/add_contract_code_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/add_contract_code_type/mod.rs @@ -20,6 +20,10 @@ pub struct ContractFile { pub next_action: Box, } +impl interactive_clap::ToCli for ContractFile { + type CliVariant = CliContractFile; +} + impl CliContractFile { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -46,24 +50,25 @@ impl From for CliContractFile { } impl ContractFile { - pub fn from( - item: CliContractFile, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let file_path = match item.file_path { + let file_path = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.file_path) + { Some(cli_file_path) => cli_file_path, - None => ContractFile::input_file_path(), - }; - let skip_next_action: super::NextAction = match item.next_action { - Some(cli_skip_action) => super::NextAction::from_cli_skip_next_action( - cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::NextAction::input_next_action(connection_config, sender_account_id)?, + None => Self::input_file_path(&context)?, }; - Ok(ContractFile { + let skip_next_action: super::NextAction = + match optional_clap_variant.and_then(|clap_variant| clap_variant.next_action) { + Some(cli_skip_action) => { + super::NextAction::from_cli_skip_next_action(cli_skip_action, context)? + } + None => super::NextAction::choose_variant(context)?, + }; + Ok(Self { file_path, next_action: Box::new(skip_next_action), }) @@ -71,13 +76,14 @@ impl ContractFile { } impl ContractFile { - fn input_file_path() -> std::path::PathBuf { + fn input_file_path( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); let input_file_path: String = Input::new() .with_prompt("What is a file location of the contract?") - .interact_text() - .unwrap(); - input_file_path.into() + .interact_text()?; + Ok(std::path::PathBuf::from(input_file_path).into()) } #[async_recursion(?Send)] diff --git a/src/commands/construct_transaction_command/transaction_actions/call_function_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/call_function_type/mod.rs index 7c343db24..bfef0fc0e 100644 --- a/src/commands/construct_transaction_command/transaction_actions/call_function_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/call_function_type/mod.rs @@ -28,6 +28,10 @@ pub struct CallFunctionAction { next_action: Box, } +impl interactive_clap::ToCli for CallFunctionAction { + type CliVariant = CliCallFunctionAction; +} + impl CliCallFunctionAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -70,37 +74,47 @@ impl From for CliCallFunctionAction { } impl CallFunctionAction { - pub fn from( - item: CliCallFunctionAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let method_name: String = match item.method_name { + let method_name: String = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.method_name) + { Some(cli_method_name) => cli_method_name, - None => CallFunctionAction::input_method_name(), + None => CallFunctionAction::input_method_name(&context)?, }; - let args: Vec = match item.args { + let args: Vec = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.args) + { Some(cli_args) => cli_args.into_bytes(), - None => CallFunctionAction::input_args(), + None => CallFunctionAction::input_args(&context)?, }; - let gas: near_primitives::types::Gas = match item.gas { + let gas: near_primitives::types::Gas = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.gas) + { Some(cli_gas) => match cli_gas { crate::common::NearGas { inner: num } => num, }, - None => CallFunctionAction::input_gas(), + None => CallFunctionAction::input_gas(&context)?, }; - let deposit: near_primitives::types::Balance = match item.deposit { + let deposit: near_primitives::types::Balance = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.deposit) + { Some(cli_deposit) => cli_deposit.to_yoctonear(), - None => CallFunctionAction::input_deposit(), - }; - let skip_next_action: super::NextAction = match item.next_action { - Some(cli_skip_action) => super::NextAction::from_cli_skip_next_action( - cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::NextAction::input_next_action(connection_config, sender_account_id)?, + None => CallFunctionAction::input_deposit(&context)?, }; + let skip_next_action: super::NextAction = + match optional_clap_variant.and_then(|clap_variant| clap_variant.next_action) { + Some(cli_skip_action) => { + super::NextAction::from_cli_skip_next_action(cli_skip_action, context)? + } + None => super::NextAction::choose_variant(context)?, + }; Ok(Self { method_name, args, @@ -112,22 +126,24 @@ impl CallFunctionAction { } impl CallFunctionAction { - fn input_method_name() -> String { + fn input_method_name( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); - Input::new() + Ok(Input::new() .with_prompt("Enter a method name") - .interact_text() - .unwrap() + .interact_text()?) } - fn input_gas() -> near_primitives::types::Gas { + fn input_gas( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); let gas: u64 = loop { let input_gas: crate::common::NearGas = Input::new() .with_prompt("Enter a gas for function") .with_initial_text("100 TeraGas") - .interact_text() - .unwrap(); + .interact_text()?; let gas: u64 = match input_gas { crate::common::NearGas { inner: num } => num, }; @@ -137,28 +153,28 @@ impl CallFunctionAction { println!("You need to enter a value of no more than 300 TERAGAS") } }; - gas + Ok(gas) } - fn input_args() -> Vec { + fn input_args(_context: &crate::common::SignerContext) -> color_eyre::eyre::Result> { println!(); let input: String = Input::new() .with_prompt("Enter args for function") - .interact_text() - .unwrap(); - input.into_bytes() + .interact_text()?; + Ok(input.into_bytes()) } - fn input_deposit() -> near_primitives::types::Balance { + fn input_deposit( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); let deposit: crate::common::NearBalance = Input::new() .with_prompt( "Enter a deposit for function (example: 10NEAR or 0.5near or 10000yoctonear).", ) .with_initial_text("0 NEAR") - .interact_text() - .unwrap(); - deposit.to_yoctonear() + .interact_text()?; + Ok(deposit.to_yoctonear()) } #[async_recursion(?Send)] diff --git a/src/commands/construct_transaction_command/transaction_actions/create_account_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/create_account_type/mod.rs index 48a5ccc3e..d0d4f24a3 100644 --- a/src/commands/construct_transaction_command/transaction_actions/create_account_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/create_account_type/mod.rs @@ -17,6 +17,10 @@ pub struct CreateAccountAction { pub next_action: Box, } +impl interactive_clap::ToCli for CreateAccountAction { + type CliVariant = CliCreateAccountAction; +} + impl CliCreateAccountAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { self.next_action @@ -37,19 +41,17 @@ impl From for CliCreateAccountAction { } impl CreateAccountAction { - pub fn from( - item: CliCreateAccountAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let skip_next_action: super::NextAction = match item.next_action { - Some(cli_skip_action) => super::NextAction::from_cli_skip_next_action( - cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::NextAction::input_next_action(connection_config, sender_account_id)?, - }; + let skip_next_action: super::NextAction = + match optional_clap_variant.and_then(|clap_variant| clap_variant.next_action) { + Some(cli_skip_action) => { + super::NextAction::from_cli_skip_next_action(cli_skip_action, context)? + } + None => super::NextAction::choose_variant(context)?, + }; Ok(Self { next_action: Box::new(skip_next_action), }) diff --git a/src/commands/construct_transaction_command/transaction_actions/delete_access_key_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/delete_access_key_type/mod.rs index a101cea1e..cceed6ebf 100644 --- a/src/commands/construct_transaction_command/transaction_actions/delete_access_key_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/delete_access_key_type/mod.rs @@ -20,6 +20,10 @@ pub struct DeleteAccessKeyAction { pub next_action: Box, } +impl interactive_clap::ToCli for DeleteAccessKeyAction { + type CliVariant = CliDeleteAccessKeyAction; +} + impl CliDeleteAccessKeyAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -46,23 +50,26 @@ impl From for CliDeleteAccessKeyAction { } impl DeleteAccessKeyAction { - pub fn from( - item: CliDeleteAccessKeyAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option< + ::CliVariant, + >, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let public_key: near_crypto::PublicKey = match item.public_key { + let public_key: near_crypto::PublicKey = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.public_key) + { Some(cli_public_key) => cli_public_key, - None => DeleteAccessKeyAction::input_public_key(), - }; - let skip_next_action: super::NextAction = match item.next_action { - Some(cli_skip_action) => super::NextAction::from_cli_skip_next_action( - cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::NextAction::input_next_action(connection_config, sender_account_id)?, + None => DeleteAccessKeyAction::input_public_key(&context)?, }; + let skip_next_action: super::NextAction = + match optional_clap_variant.and_then(|clap_variant| clap_variant.next_action) { + Some(cli_skip_action) => { + super::NextAction::from_cli_skip_next_action(cli_skip_action, context)? + } + None => super::NextAction::choose_variant(context)?, + }; Ok(Self { public_key, next_action: Box::new(skip_next_action), @@ -71,11 +78,12 @@ impl DeleteAccessKeyAction { } impl DeleteAccessKeyAction { - pub fn input_public_key() -> near_crypto::PublicKey { - Input::new() + pub fn input_public_key( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter the access key to remove it") - .interact_text() - .unwrap() + .interact_text()?) } #[async_recursion(?Send)] diff --git a/src/commands/construct_transaction_command/transaction_actions/delete_account_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/delete_account_type/mod.rs index adfec8eed..44a35349a 100644 --- a/src/commands/construct_transaction_command/transaction_actions/delete_account_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/delete_account_type/mod.rs @@ -21,6 +21,10 @@ pub struct DeleteAccountAction { pub next_action: Box, } +impl interactive_clap::ToCli for DeleteAccountAction { + type CliVariant = CliDeleteAccountAction; +} + impl CliDeleteAccountAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -48,23 +52,24 @@ impl From for CliDeleteAccountAction { } impl DeleteAccountAction { - pub fn from( - item: CliDeleteAccountAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let beneficiary_id: near_primitives::types::AccountId = match item.beneficiary_id { + let beneficiary_id: near_primitives::types::AccountId = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.beneficiary_id) + { Some(cli_account_id) => cli_account_id, - None => DeleteAccountAction::input_beneficiary_id(), - }; - let skip_next_action: super::NextAction = match item.next_action { - Some(cli_skip_action) => super::NextAction::from_cli_skip_next_action( - cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::NextAction::input_next_action(connection_config, sender_account_id)?, + None => DeleteAccountAction::input_beneficiary_id(&context)?, }; + let skip_next_action: super::NextAction = + match optional_clap_variant.and_then(|clap_variant| clap_variant.next_action) { + Some(cli_skip_action) => { + super::NextAction::from_cli_skip_next_action(cli_skip_action, context)? + } + None => super::NextAction::choose_variant(context)?, + }; Ok(Self { beneficiary_id, next_action: Box::new(skip_next_action), @@ -73,12 +78,13 @@ impl DeleteAccountAction { } impl DeleteAccountAction { - pub fn input_beneficiary_id() -> near_primitives::types::AccountId { + pub fn input_beneficiary_id( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); - Input::new() + Ok(Input::new() .with_prompt("Enter the beneficiary ID to delete this account ID") - .interact_text() - .unwrap() + .interact_text()?) } #[async_recursion(?Send)] diff --git a/src/commands/construct_transaction_command/transaction_actions/mod.rs b/src/commands/construct_transaction_command/transaction_actions/mod.rs index ec36e2d53..3d8742c36 100644 --- a/src/commands/construct_transaction_command/transaction_actions/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/mod.rs @@ -1,5 +1,4 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod add_access_key_mode; mod add_contract_code_type; @@ -10,26 +9,22 @@ mod delete_account_type; mod stake_near_tokens_type; mod transfer_near_tokens_type; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliNextAction { - /// Choose next action - AddAction(CliSelectAction), - /// Go to transaction signing - Skip(CliSkipAction), -} - #[derive(Debug, Clone, clap::Clap)] pub enum CliSkipNextAction { /// Go to transaction signing Skip(CliSkipAction), } -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Select an action that you want to add to the action: pub enum NextAction { #[strum_discriminants(strum(message = "Select a new action"))] + /// Choose next action AddAction(SelectAction), #[strum_discriminants(strum(message = "Skip adding a new action"))] + /// Go to transaction signing Skip(SkipAction), } @@ -57,69 +52,20 @@ impl From for CliSkipNextAction { } } -impl CliNextAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::AddAction(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("add-action".to_owned()); - args - } - Self::Skip(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("skip".to_owned()); - args - } - } - } -} - -impl From for CliNextAction { - fn from(next_action: NextAction) -> Self { - match next_action { - NextAction::AddAction(select_action) => Self::AddAction(select_action.into()), - NextAction::Skip(skip_action) => Self::Skip(skip_action.into()), - } - } -} - -impl NextAction { - pub fn from_cli_next_action( - item: CliNextAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliNextAction::AddAction(cli_select_action) => { - let select_action: SelectAction = - SelectAction::from(cli_select_action, connection_config, sender_account_id)?; - Ok(Self::AddAction(select_action)) - } - CliNextAction::Skip(cli_skip_action) => { - let skip_action: SkipAction = - SkipAction::from(cli_skip_action, connection_config, sender_account_id)?; - Ok(Self::Skip(skip_action)) - } - } - } -} - impl NextAction { pub fn from_cli_skip_next_action( item: CliSkipNextAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { match item { CliSkipNextAction::Skip(cli_skip_action) => { - let skip_action: SkipAction = - SkipAction::from(cli_skip_action, connection_config, sender_account_id)?; + let skip_action: SkipAction = SkipAction::from_cli(Some(cli_skip_action), context)?; Ok(Self::Skip(skip_action)) } } } } - +//------------------------------------- /// This mode is not provided now // impl CliNextAction { // fn from(item: CliSkipNextAction) -> color_eyre::eyre::Result { @@ -128,35 +74,8 @@ impl NextAction { // } // } // } - +//-------------------------------------- impl NextAction { - pub fn input_next_action( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - println!(); - let variants = NextActionDiscriminants::iter().collect::>(); - let next_action = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_next_action = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select an action that you want to add to the action:") - .items(&next_action) - .default(0) - .interact() - .unwrap(); - let cli_next_action = match variants[select_next_action] { - NextActionDiscriminants::AddAction => CliNextAction::AddAction(Default::default()), - NextActionDiscriminants::Skip => CliNextAction::Skip(Default::default()), - }; - Ok(Self::from_cli_next_action( - cli_next_action, - connection_config, - sender_account_id, - )?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -177,60 +96,13 @@ impl NextAction { } } -/// инструмент для добавления команды в транзакцию -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSelectAction { - #[clap(subcommand)] - transaction_subcommand: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct SelectAction { + #[interactive_clap(subcommand)] transaction_subcommand: ActionSubcommand, } -impl CliSelectAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.transaction_subcommand - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliSelectAction { - fn from(select_action: SelectAction) -> Self { - Self { - transaction_subcommand: Some(select_action.transaction_subcommand.into()), - } - } -} - -impl SelectAction { - fn from( - item: CliSelectAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let transaction_subcommand: ActionSubcommand = match item.transaction_subcommand { - Some(cli_transaction_subcommand) => ActionSubcommand::from( - cli_transaction_subcommand, - connection_config, - sender_account_id, - ), - None => ActionSubcommand::choose_action_command(connection_config, sender_account_id), - }; - Ok(Self { - transaction_subcommand, - }) - } -} - impl SelectAction { pub async fn process( self, @@ -243,255 +115,45 @@ impl SelectAction { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliActionSubcommand { - /// Предоставьте данные для перевода Near - TransferNEARTokens(self::transfer_near_tokens_type::CliTransferNEARTokensAction), - /// Предоставьте данные для call function - CallFunction(self::call_function_type::CliCallFunctionAction), - /// Предоставьте данные для ставки - StakeNEARTokens(self::stake_near_tokens_type::CliStakeNEARTokensAction), - /// Предоставьте данные для создания аккаунта - CreateAccount(self::create_account_type::CliCreateAccountAction), - /// Предоставьте данные для удаления аккаунта - DeleteAccount(self::delete_account_type::CliDeleteAccountAction), - /// Предоставьте данные для добавления ключа доступа пользователю - AddAccessKey(self::add_access_key_mode::CliAddAccessKeyMode), - /// Предоставьте данные для удаления ключа доступа у пользователя - DeleteAccessKey(self::delete_access_key_type::CliDeleteAccessKeyAction), - /// Предоставьте данные для добавления контракта - AddContractCode(self::add_contract_code_type::CliContractFile), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Select an action that you want to add to the action: pub enum ActionSubcommand { #[strum_discriminants(strum(message = "Transfer NEAR Tokens"))] - TransferNEARTokens(self::transfer_near_tokens_type::TransferNEARTokensAction), + /// Предоставьте данные для перевода Near + TransferNearTokens(self::transfer_near_tokens_type::TransferNEARTokensAction), #[strum_discriminants(strum(message = "Call a Function"))] + /// Предоставьте данные для call function CallFunction(self::call_function_type::CallFunctionAction), #[strum_discriminants(strum(message = "Stake NEAR Tokens"))] - StakeNEARTokens(self::stake_near_tokens_type::StakeNEARTokensAction), + /// Предоставьте данные для ставки + StakeNearTokens(self::stake_near_tokens_type::StakeNEARTokensAction), #[strum_discriminants(strum(message = "Create an Account"))] + /// Предоставьте данные для создания аккаунта CreateAccount(self::create_account_type::CreateAccountAction), #[strum_discriminants(strum(message = "Delete an Account"))] + /// Предоставьте данные для удаления аккаунта DeleteAccount(self::delete_account_type::DeleteAccountAction), #[strum_discriminants(strum(message = "Add an Access Key"))] + /// Предоставьте данные для добавления ключа доступа пользователю AddAccessKey(self::add_access_key_mode::AddAccessKeyMode), #[strum_discriminants(strum(message = "Detete an Access Key"))] + /// Предоставьте данные для удаления ключа доступа у пользователя DeleteAccessKey(self::delete_access_key_type::DeleteAccessKeyAction), #[strum_discriminants(strum(message = "Add a contract code"))] + /// Предоставьте данные для добавления контракта AddContractCode(self::add_contract_code_type::ContractFile), } -impl CliActionSubcommand { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::TransferNEARTokens(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("transfer-near-tokens".to_owned()); - args - } - Self::CallFunction(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("call-function".to_owned()); - args - } - Self::StakeNEARTokens(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("stake-near-tokens".to_owned()); - args - } - Self::CreateAccount(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("create-account".to_owned()); - args - } - Self::DeleteAccount(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("delete-account".to_owned()); - args - } - Self::AddAccessKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("add-access-key".to_owned()); - args - } - Self::DeleteAccessKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("delete-access-key".to_owned()); - args - } - Self::AddContractCode(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("add-contract-code".to_owned()); - args - } - } - } -} - -impl From for CliActionSubcommand { - fn from(action_subcommand: ActionSubcommand) -> Self { - match action_subcommand { - ActionSubcommand::TransferNEARTokens(transfer_near_tokens_action) => { - Self::TransferNEARTokens(transfer_near_tokens_action.into()) - } - ActionSubcommand::CallFunction(call_function_action) => { - Self::CallFunction(call_function_action.into()) - } - ActionSubcommand::StakeNEARTokens(stake_near_tokens_action) => { - Self::StakeNEARTokens(stake_near_tokens_action.into()) - } - ActionSubcommand::CreateAccount(create_account_action) => { - Self::CreateAccount(create_account_action.into()) - } - ActionSubcommand::DeleteAccount(delete_account_action) => { - Self::DeleteAccount(delete_account_action.into()) - } - ActionSubcommand::AddAccessKey(add_access_key_action) => { - Self::AddAccessKey(add_access_key_action.into()) - } - ActionSubcommand::DeleteAccessKey(delete_access_key_action) => { - Self::DeleteAccessKey(delete_access_key_action.into()) - } - ActionSubcommand::AddContractCode(add_contract_code_action) => { - Self::AddContractCode(add_contract_code_action.into()) - } - } - } -} - -impl ActionSubcommand { - fn from( - item: CliActionSubcommand, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> Self { - match item { - CliActionSubcommand::TransferNEARTokens(cli_transfer_near_token) => { - Self::TransferNEARTokens( - self::transfer_near_tokens_type::TransferNEARTokensAction::from( - cli_transfer_near_token, - connection_config, - sender_account_id, - ) - .unwrap(), - ) - } - CliActionSubcommand::CreateAccount(cli_create_account) => Self::CreateAccount( - self::create_account_type::CreateAccountAction::from( - cli_create_account, - connection_config, - sender_account_id, - ) - .unwrap(), - ), - CliActionSubcommand::DeleteAccount(cli_delete_account) => Self::DeleteAccount( - self::delete_account_type::DeleteAccountAction::from( - cli_delete_account, - connection_config, - sender_account_id, - ) - .unwrap(), - ), - CliActionSubcommand::AddAccessKey(cli_add_access_key) => Self::AddAccessKey( - self::add_access_key_mode::AddAccessKeyMode::from( - cli_add_access_key, - connection_config, - sender_account_id, - ) - .unwrap(), - ), - CliActionSubcommand::DeleteAccessKey(cli_delete_access_key) => Self::DeleteAccessKey( - self::delete_access_key_type::DeleteAccessKeyAction::from( - cli_delete_access_key, - connection_config, - sender_account_id, - ) - .unwrap(), - ), - CliActionSubcommand::StakeNEARTokens(cli_stake_near_token) => Self::StakeNEARTokens( - self::stake_near_tokens_type::StakeNEARTokensAction::from( - cli_stake_near_token, - connection_config, - sender_account_id, - ) - .unwrap(), - ), - CliActionSubcommand::CallFunction(cli_call_function) => Self::CallFunction( - self::call_function_type::CallFunctionAction::from( - cli_call_function, - connection_config, - sender_account_id, - ) - .unwrap(), - ), - CliActionSubcommand::AddContractCode(cli_contract_file) => Self::AddContractCode( - self::add_contract_code_type::ContractFile::from( - cli_contract_file, - connection_config, - sender_account_id, - ) - .unwrap(), - ), - } - } -} - impl ActionSubcommand { - pub fn choose_action_command( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> ActionSubcommand { - println!(); - let variants = ActionSubcommandDiscriminants::iter().collect::>(); - let action_subcommands = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let select_action_subcommand = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select an action that you want to add to the action:") - .items(&action_subcommands) - .default(0) - .interact() - .unwrap(); - let cli_action_subcomand = match variants[select_action_subcommand] { - ActionSubcommandDiscriminants::TransferNEARTokens => { - CliActionSubcommand::TransferNEARTokens(Default::default()) - } - ActionSubcommandDiscriminants::CallFunction => { - CliActionSubcommand::CallFunction(Default::default()) - } - ActionSubcommandDiscriminants::StakeNEARTokens => { - CliActionSubcommand::StakeNEARTokens(Default::default()) - } - ActionSubcommandDiscriminants::CreateAccount => { - CliActionSubcommand::CreateAccount(Default::default()) - } - ActionSubcommandDiscriminants::DeleteAccount => { - CliActionSubcommand::DeleteAccount(Default::default()) - } - ActionSubcommandDiscriminants::AddAccessKey => { - CliActionSubcommand::AddAccessKey(Default::default()) - } - ActionSubcommandDiscriminants::DeleteAccessKey => { - CliActionSubcommand::DeleteAccessKey(Default::default()) - } - ActionSubcommandDiscriminants::AddContractCode => { - CliActionSubcommand::AddContractCode(Default::default()) - } - }; - Self::from(cli_action_subcomand, connection_config, sender_account_id) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, ) -> crate::CliResult { match self { - ActionSubcommand::TransferNEARTokens(args_transfer) => { + ActionSubcommand::TransferNearTokens(args_transfer) => { args_transfer .process(prepopulated_unsigned_transaction, network_connection_config) .await @@ -501,7 +163,7 @@ impl ActionSubcommand { .process(prepopulated_unsigned_transaction, network_connection_config) .await } - ActionSubcommand::StakeNEARTokens(args_stake) => { + ActionSubcommand::StakeNearTokens(args_stake) => { args_stake .process(prepopulated_unsigned_transaction, network_connection_config) .await @@ -535,40 +197,13 @@ impl ActionSubcommand { } } -/// инструмент, показывающий окончание набора команд в одной транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSkipAction { - #[clap(subcommand)] - sign_option: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct SkipAction { + #[interactive_clap(subcommand)] pub sign_option: super::sign_transaction::SignTransaction, } - -impl CliSkipAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliSkipAction { - fn from(skip_action: SkipAction) -> Self { - Self { - sign_option: Some(skip_action.sign_option.into()), - } - } -} - +//------------------------------------ // impl From for CliSkipAction { // fn from(select_action: SelectAction) -> Self { // Self{ @@ -576,27 +211,7 @@ impl From for CliSkipAction { // } // } // } - -impl SkipAction { - fn from( - item: CliSkipAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let sign_option: super::sign_transaction::SignTransaction = match item.sign_option { - Some(cli_sign_transaction) => super::sign_transaction::SignTransaction::from( - cli_sign_transaction, - connection_config, - sender_account_id, - )?, - None => super::sign_transaction::SignTransaction::choose_sign_option( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { sign_option }) - } -} +//----------------------------------------- impl SkipAction { pub async fn process( diff --git a/src/commands/construct_transaction_command/transaction_actions/stake_near_tokens_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/stake_near_tokens_type/mod.rs index cc3624e06..71376e40a 100644 --- a/src/commands/construct_transaction_command/transaction_actions/stake_near_tokens_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/stake_near_tokens_type/mod.rs @@ -22,6 +22,10 @@ pub struct StakeNEARTokensAction { pub next_action: Box, } +impl interactive_clap::ToCli for StakeNEARTokensAction { + type CliVariant = CliStakeNEARTokensAction; +} + impl CliStakeNEARTokensAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -52,27 +56,33 @@ impl From for CliStakeNEARTokensAction { } impl StakeNEARTokensAction { - pub fn from( - item: CliStakeNEARTokensAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option< + ::CliVariant, + >, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let stake_amount: crate::common::NearBalance = match item.stake_amount { - Some(cli_stake_amount) => cli_stake_amount, - None => StakeNEARTokensAction::input_stake_amount(), + let stake_amount: crate::common::NearBalance = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.stake_amount) + { + Some(cli_amount) => cli_amount, + None => StakeNEARTokensAction::input_stake_amount(&context)?, }; - let public_key: near_crypto::PublicKey = match item.public_key { + let public_key: near_crypto::PublicKey = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.public_key) + { Some(cli_public_key) => cli_public_key, - None => StakeNEARTokensAction::input_public_key(), - }; - let skip_next_action: super::NextAction = match item.next_action { - Some(cli_skip_action) => super::NextAction::from_cli_skip_next_action( - cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::NextAction::input_next_action(connection_config, sender_account_id)?, + None => StakeNEARTokensAction::input_public_key(&context)?, }; + let skip_next_action: super::NextAction = + match optional_clap_variant.and_then(|clap_variant| clap_variant.next_action) { + Some(cli_skip_action) => { + super::NextAction::from_cli_skip_next_action(cli_skip_action, context)? + } + None => super::NextAction::choose_variant(context)?, + }; Ok(Self { stake_amount, public_key, @@ -82,18 +92,21 @@ impl StakeNEARTokensAction { } impl StakeNEARTokensAction { - fn input_public_key() -> near_crypto::PublicKey { - Input::new() + fn input_public_key( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter a public key for this stake") - .interact_text() - .unwrap() + .interact_text()?) } - fn input_stake_amount() -> crate::common::NearBalance { - Input::new() - .with_prompt("How many NEAR Tokens do you want to stake? (example: 10NEAR or 0.5near or 10000yoctonear)") - .interact_text() - .unwrap() + fn input_stake_amount( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("How many NEAR Tokens do you want to stake? (example: 10NEAR or 0.5near or 10000yoctonear)") + .interact_text() + ?) } #[async_recursion(?Send)] diff --git a/src/commands/construct_transaction_command/transaction_actions/transfer_near_tokens_type/mod.rs b/src/commands/construct_transaction_command/transaction_actions/transfer_near_tokens_type/mod.rs index 0d49415ec..26daa9bcf 100644 --- a/src/commands/construct_transaction_command/transaction_actions/transfer_near_tokens_type/mod.rs +++ b/src/commands/construct_transaction_command/transaction_actions/transfer_near_tokens_type/mod.rs @@ -20,6 +20,10 @@ pub struct TransferNEARTokensAction { pub next_action: Box, } +impl interactive_clap::ToCli for TransferNEARTokensAction { + type CliVariant = CliTransferNEARTokensAction; +} + impl CliTransferNEARTokensAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -46,25 +50,27 @@ impl From for CliTransferNEARTokensAction { } impl TransferNEARTokensAction { - pub fn from( - item: CliTransferNEARTokensAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option< + ::CliVariant, + >, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let amount: crate::common::TransferAmount = match item.amount { + let amount: crate::common::TransferAmount = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.amount) + { Some(cli_amount) => crate::common::TransferAmount::from_unchecked(cli_amount), - None => TransferNEARTokensAction::input_amount( - connection_config.clone(), - sender_account_id.clone(), - )?, + None => TransferNEARTokensAction::input_amount(&context)?, }; - let skip_next_action: super::NextAction = match item.next_action { - Some(cli_skip_action) => super::NextAction::from_cli_skip_next_action( - cli_skip_action, - connection_config, - sender_account_id, - )?, - None => super::NextAction::input_next_action(connection_config, sender_account_id)?, + let skip_next_action: super::NextAction = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.next_action) + { + Some(cli_skip_action) => { + super::NextAction::from_cli_skip_next_action(cli_skip_action, context)? + } + None => super::NextAction::choose_variant(context)?, }; Ok(Self { amount, @@ -75,20 +81,21 @@ impl TransferNEARTokensAction { impl TransferNEARTokensAction { fn input_amount( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + context: &crate::common::SignerContext, ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let sender_account_id = context.signer_account_id.clone(); match connection_config { Some(connection_config) => { let account_transfer_allowance = crate::common::get_account_transfer_allowance( &connection_config, - sender_account_id, + sender_account_id.into(), )?; loop { let input_amount: crate::common::NearBalance = Input::new() .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") .interact_text() - .unwrap(); + ?; if let Ok(transfer_amount) = crate::common::TransferAmount::from( input_amount.clone(), &account_transfer_allowance, @@ -107,8 +114,7 @@ impl TransferNEARTokensAction { .with_prompt("Do you want to keep this amount for the transfer?") .items(&choose_input) .default(0) - .interact_on_opt(&Term::stderr()) - .unwrap(); + .interact_on_opt(&Term::stderr())?; match select_choose_input { Some(0) => { break Ok(crate::common::TransferAmount::from_unchecked( @@ -125,7 +131,7 @@ impl TransferNEARTokensAction { let input_amount: crate::common::NearBalance = Input::new() .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") .interact_text() - .unwrap(); + ?; Ok(crate::common::TransferAmount::from_unchecked(input_amount)) } } diff --git a/src/commands/delete_command/access_key/mod.rs b/src/commands/delete_command/access_key/mod.rs index e3d5c7b26..d7837066e 100644 --- a/src/commands/delete_command/access_key/mod.rs +++ b/src/commands/delete_command/access_key/mod.rs @@ -3,156 +3,22 @@ use dialoguer::Input; pub mod operation_mode; mod sender; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliDeleteAccessKeyAction { - /// Specify public key - PublicKey(CliDeleteAccessKeyType), -} - -#[derive(Debug, Clone)] -pub enum DeleteAccessKeyAction { - PublicKey(DeleteAccessKeyType), -} - -impl CliDeleteAccessKeyAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::PublicKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("public-key".to_owned()); - args - } - } - } -} - -impl From for CliDeleteAccessKeyAction { - fn from(delete_access_key_action: DeleteAccessKeyAction) -> Self { - match delete_access_key_action { - DeleteAccessKeyAction::PublicKey(delete_access_key_type) => { - Self::PublicKey(delete_access_key_type.into()) - } - } - } -} - -impl DeleteAccessKeyAction { - pub fn from( - item: CliDeleteAccessKeyAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliDeleteAccessKeyAction::PublicKey(cli_delete_access_key_type) => { - Ok(Self::PublicKey(DeleteAccessKeyType::from( - cli_delete_access_key_type, - connection_config, - sender_account_id, - )?)) - } - } - } -} - -impl DeleteAccessKeyAction { - pub fn choose_delete_access_key_action( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliDeleteAccessKeyAction::PublicKey(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - DeleteAccessKeyAction::PublicKey(delete_access_key_type) => { - delete_access_key_type - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// Specify the access key to be deleted -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliDeleteAccessKeyType { - public_key: Option, - #[clap(subcommand)] - sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct DeleteAccessKeyType { - pub public_key: near_crypto::PublicKey, + pub public_key: crate::types::public_key::PublicKey, + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliDeleteAccessKeyType { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(public_key) = &self.public_key { - args.push_front(public_key.to_string()); - } - args - } -} - -impl From for CliDeleteAccessKeyType { - fn from(delete_access_key_type: DeleteAccessKeyType) -> Self { - Self { - public_key: Some(delete_access_key_type.public_key), - sign_option: Some(delete_access_key_type.sign_option.into()), - } - } -} - -impl DeleteAccessKeyType { - fn from( - item: CliDeleteAccessKeyType, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let public_key: near_crypto::PublicKey = match item.public_key { - Some(cli_public_key) => cli_public_key, - None => DeleteAccessKeyType::input_public_key(), - }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, - }; - Ok(Self { - public_key, - sign_option, - }) - } -} - impl DeleteAccessKeyType { - fn input_public_key() -> near_crypto::PublicKey { - Input::new() + fn input_public_key( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter a public key for this access key") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process( @@ -162,7 +28,7 @@ impl DeleteAccessKeyType { ) -> crate::CliResult { let action = near_primitives::transaction::Action::DeleteKey( near_primitives::transaction::DeleteKeyAction { - public_key: self.public_key, + public_key: self.public_key.into(), }, ); let mut actions = prepopulated_unsigned_transaction.actions.clone(); diff --git a/src/commands/delete_command/access_key/operation_mode/mod.rs b/src/commands/delete_command/access_key/operation_mode/mod.rs index 2a3b9a12f..91fd5f79b 100644 --- a/src/commands/delete_command/access_key/operation_mode/mod.rs +++ b/src/commands/delete_command/access_key/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { #[strum_discriminants(strum(message = "Yes, I keep it simple"))] + /// Prepare and, optionally, submit a new transaction with online mode Network(self::online_mode::NetworkArgs), #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] + /// Prepare and, optionally, submit a new transaction with offline mode Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct DeleteAccessKeyCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/delete_command/access_key/operation_mode/offline_mode/mod.rs b/src/commands/delete_command/access_key/operation_mode/offline_mode/mod.rs index 775982073..e88152e1d 100644 --- a/src/commands/delete_command/access_key/operation_mode/offline_mode/mod.rs +++ b/src/commands/delete_command/access_key/operation_mode/offline_mode/mod.rs @@ -1,56 +1,38 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + ///Specify the account to be deleted + account: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::DeleteAccessKeyCommandNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.account .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/delete_command/access_key/operation_mode/online_mode/mod.rs b/src/commands/delete_command/access_key/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/delete_command/access_key/operation_mode/online_mode/mod.rs +++ b/src/commands/delete_command/access_key/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/mod.rs b/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/mod.rs index 838909edd..9d57000a5 100644 --- a/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,86 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { #[strum_discriminants(strum(message = "Testnet"))] + /// предоставление данных для сервера https://rpc.testnet.near.org Testnet(self::server::Server), #[strum_discriminants(strum(message = "Mainnet"))] + /// предоставление данных для сервера https://rpc.mainnet.near.org Mainnet(self::server::Server), #[strum_discriminants(strum(message = "Betanet"))] + /// предоставление данных для сервера https://rpc.betanet.near.org Betanet(self::server::Server), #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + /// предоставление данных для сервера, указанного вручную + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::DeleteAccessKeyCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/server/mod.rs index 516ef3ac3..88521c04e 100644 --- a/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/delete_command/access_key/operation_mode/online_mode/select_server/server/mod.rs @@ -1,118 +1,53 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify the account to be deleted + pub account: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::AddAccessKeyCommandNetworkContext)] +pub struct CustomServer { + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify the account to be deleted + pub account: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::DeleteAccessKeyCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { url: url.inner }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone())?, - None => SendFrom::choose_send_from(connection_config.clone())?, - }; - Ok(Server { - connection_config, - send_from, - }) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } } @@ -120,78 +55,22 @@ impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.account + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify the account to be deleted - Account(super::super::super::super::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Account(super::super::super::super::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Account(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Account(sender) => Self::Account(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Account(cli_sender) => Ok(Self::Account( - super::super::super::super::sender::Sender::from(cli_sender, connection_config)?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::Account(Default::default()), - connection_config, - )?) - } - +impl CustomServer { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::Account(sender) => { - sender - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.account + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/delete_command/access_key/sender/mod.rs b/src/commands/delete_command/access_key/sender/mod.rs index abd04fc59..cbc9c7f49 100644 --- a/src/commands/delete_command/access_key/sender/mod.rs +++ b/src/commands/delete_command/access_key/sender/mod.rs @@ -1,98 +1,75 @@ use dialoguer::Input; -/// Specify the account to be deleted -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - delete_public_key: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::DeleteAccessKeyCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] +pub struct Sender { + #[interactive_clap(skip_default_from_cli)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + ///Specify public key + pub public_key: super::DeleteAccessKeyType, } -#[derive(Debug, Clone)] -pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, - pub delete_public_key: super::DeleteAccessKeyAction, +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .delete_public_key - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::DeleteAccessKeyCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - delete_public_key: Some(sender.delete_public_key.into()), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::DeleteAccessKeyCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let delete_public_key = match item.delete_public_key { - Some(cli_delete_access_key) => super::DeleteAccessKeyAction::from( - cli_delete_access_key, - connection_config, - sender_account_id.clone(), - )?, - None => super::DeleteAccessKeyAction::choose_delete_access_key_action( - connection_config, - sender_account_id.clone(), - )?, - }; - Ok(Self { - sender_account_id, - delete_public_key, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::DeleteAccessKeyCommandNetworkContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("Which account ID do you need to remove the key from?") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -110,11 +87,11 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), - receiver_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), + receiver_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; - self.delete_public_key + self.public_key .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/delete_command/account/mod.rs b/src/commands/delete_command/account/mod.rs index 699cf5c03..06acabef4 100644 --- a/src/commands/delete_command/account/mod.rs +++ b/src/commands/delete_command/account/mod.rs @@ -3,100 +3,53 @@ use dialoguer::Input; pub mod operation_mode; mod sender; -/// удаление аккаунта -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliDeleteAccountAction { - beneficiary_id: Option, - #[clap(subcommand)] - sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct DeleteAccountAction { - pub beneficiary_id: near_primitives::types::AccountId, + #[interactive_clap(skip_default_from_cli)] + pub beneficiary_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliDeleteAccountAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(beneficiary_id) = &self.beneficiary_id { - args.push_front(beneficiary_id.to_string()); - } - args - } -} - -impl From for CliDeleteAccountAction { - fn from(delete_account_action: DeleteAccountAction) -> Self { - Self { - beneficiary_id: Some(delete_account_action.beneficiary_id), - sign_option: Some(delete_account_action.sign_option.into()), - } - } -} - impl DeleteAccountAction { - pub fn from( - item: CliDeleteAccountAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let beneficiary_id: near_primitives::types::AccountId = match item.beneficiary_id { - Some(cli_account_id) => match &connection_config { + fn from_cli_beneficiary_id( + optional_cli_sender_account_id: Option, + context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_beneficiary_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_account_id.clone(), + &network_connection_config, + cli_beneficiary_id.clone().into(), )? { - Some(_) => cli_account_id, + Some(_) => Ok(cli_beneficiary_id), None => { - println!("Account <{}> doesn't exist", cli_account_id); - DeleteAccountAction::input_beneficiary_id(connection_config.clone())? + println!("Account <{}> doesn't exist", cli_beneficiary_id); + Self::input_beneficiary_id(&context) } }, - None => cli_account_id, + None => Ok(cli_beneficiary_id), }, - None => DeleteAccountAction::input_beneficiary_id(connection_config.clone())?, - }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, - }; - Ok(Self { - beneficiary_id, - sign_option, - }) + None => Self::input_beneficiary_id(&context), + } } -} -impl DeleteAccountAction { pub fn input_beneficiary_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("Enter the beneficiary ID to delete this account ID") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { - println!("Account <{}> doesn't exist", account_id); + println!("Account <{}> doesn't exist", account_id.to_string()); } } else { break Ok(account_id); @@ -109,7 +62,7 @@ impl DeleteAccountAction { prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, network_connection_config: Option, ) -> crate::CliResult { - let beneficiary_id: near_primitives::types::AccountId = self.beneficiary_id.clone(); + let beneficiary_id: near_primitives::types::AccountId = self.beneficiary_id.clone().into(); let action = near_primitives::transaction::Action::DeleteAccount( near_primitives::transaction::DeleteAccountAction { beneficiary_id }, ); diff --git a/src/commands/delete_command/account/operation_mode/mod.rs b/src/commands/delete_command/account/operation_mode/mod.rs index 2a3b9a12f..57d9eae05 100644 --- a/src/commands/delete_command/account/operation_mode/mod.rs +++ b/src/commands/delete_command/account/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { #[strum_discriminants(strum(message = "Yes, I keep it simple"))] + /// Prepare and, optionally, submit a new transaction with online mode Network(self::online_mode::NetworkArgs), #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] + /// Prepare and, optionally, submit a new transaction with offline mode Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct DeleteAccountCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/delete_command/account/operation_mode/offline_mode/mod.rs b/src/commands/delete_command/account/operation_mode/offline_mode/mod.rs index 775982073..a99140685 100644 --- a/src/commands/delete_command/account/operation_mode/offline_mode/mod.rs +++ b/src/commands/delete_command/account/operation_mode/offline_mode/mod.rs @@ -1,56 +1,38 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + ///Specify the account to be deleted + account: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::DeleteAccountCommandNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.account .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/delete_command/account/operation_mode/online_mode/mod.rs b/src/commands/delete_command/account/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/delete_command/account/operation_mode/online_mode/mod.rs +++ b/src/commands/delete_command/account/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/delete_command/account/operation_mode/online_mode/select_server/mod.rs b/src/commands/delete_command/account/operation_mode/online_mode/select_server/mod.rs index 838909edd..8f54ccce8 100644 --- a/src/commands/delete_command/account/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/delete_command/account/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,86 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { #[strum_discriminants(strum(message = "Testnet"))] + /// предоставление данных для сервера https://rpc.testnet.near.org Testnet(self::server::Server), #[strum_discriminants(strum(message = "Mainnet"))] + /// предоставление данных для сервера https://rpc.mainnet.near.org Mainnet(self::server::Server), #[strum_discriminants(strum(message = "Betanet"))] + /// предоставление данных для сервера https://rpc.betanet.near.org Betanet(self::server::Server), #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + /// предоставление данных для сервера, указанного вручную + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::DeleteAccountCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/delete_command/account/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/delete_command/account/operation_mode/online_mode/select_server/server/mod.rs index 516ef3ac3..b84fb2fdd 100644 --- a/src/commands/delete_command/account/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/delete_command/account/operation_mode/online_mode/select_server/server/mod.rs @@ -1,118 +1,53 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify the account to be deleted + pub account: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::DeleteAccountCommandNetworkContext)] +pub struct CustomServer { + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify the account to be deleted + pub account: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::DeleteAccountCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { url: url.inner }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone())?, - None => SendFrom::choose_send_from(connection_config.clone())?, - }; - Ok(Server { - connection_config, - send_from, - }) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } } @@ -120,78 +55,22 @@ impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.account + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify the account to be deleted - Account(super::super::super::super::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Account(super::super::super::super::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Account(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Account(sender) => Self::Account(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Account(cli_sender) => Ok(Self::Account( - super::super::super::super::sender::Sender::from(cli_sender, connection_config)?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::Account(Default::default()), - connection_config, - )?) - } - +impl CustomServer { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::Account(sender) => { - sender - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.account + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/delete_command/account/sender/mod.rs b/src/commands/delete_command/account/sender/mod.rs index e07d7caac..f9cc0fe6d 100644 --- a/src/commands/delete_command/account/sender/mod.rs +++ b/src/commands/delete_command/account/sender/mod.rs @@ -1,92 +1,76 @@ use dialoguer::Input; -/// Specify the account to be deleted -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::DeleteAccountCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] +pub struct Sender { + #[interactive_clap(skip_default_from_cli)] + // #[interactive_clap(skip_default_getter)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + ///Specify a beneficiary + pub beneficiary: super::DeleteAccountAction, } -#[derive(Debug, Clone)] -pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, - pub send_to: SendTo, +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::DeleteAccountCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - send_to: Some(sender.send_to.into()), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } + impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::DeleteAccountCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let send_to: SendTo = match item.send_to { - Some(cli_send_to) => { - SendTo::from(cli_send_to, connection_config, sender_account_id.clone())? - } - None => SendTo::send_to(connection_config, sender_account_id.clone())?, - }; - Ok(Self { - sender_account_id, - send_to, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::DeleteAccountCommandNetworkContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("Which account ID do you need to remove?") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -104,91 +88,12 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), - receiver_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), + receiver_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; - self.send_to + self.beneficiary .process(unsigned_transaction, network_connection_config) .await } } - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify a beneficiary - Beneficiary(super::CliDeleteAccountAction), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Beneficiary(super::DeleteAccountAction), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Beneficiary(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("beneficiary".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Beneficiary(delete_account_action) => { - Self::Beneficiary(delete_account_action.into()) - } - } - } -} - -impl SendTo { - fn from( - item: CliSendTo, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliSendTo::Beneficiary(cli_delete_accaunt) => { - let delete_accaunt = super::DeleteAccountAction::from( - cli_delete_accaunt, - connection_config, - sender_account_id, - )?; - Ok(Self::Beneficiary(delete_accaunt)) - } - } - } -} - -impl SendTo { - pub fn send_to( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendTo::Beneficiary(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - SendTo::Beneficiary(delete_account_action) => { - delete_account_action - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} diff --git a/src/commands/delete_command/mod.rs b/src/commands/delete_command/mod.rs index 317a39ee5..d37832cdb 100644 --- a/src/commands/delete_command/mod.rs +++ b/src/commands/delete_command/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod access_key; mod account; -/// инструмент выбора to delete action -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliDeleteAction { - #[clap(subcommand)] - action: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct DeleteAction { + #[interactive_clap(subcommand)] pub action: Action, } -impl CliDeleteAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.action - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliDeleteAction { - fn from(delete_action: DeleteAction) -> Self { - Self { - action: Some(delete_action.action.into()), - } - } -} - -impl DeleteAction { - pub fn from(item: CliDeleteAction) -> color_eyre::eyre::Result { - let action = match item.action { - Some(cli_action) => Action::from(cli_action)?, - None => Action::choose_action()?, - }; - Ok(Self { action }) - } -} - impl DeleteAction { pub async fn process( self, @@ -57,83 +19,20 @@ impl DeleteAction { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliAction { - /// Delete an access key for an account - AccessKey(self::access_key::operation_mode::CliOperationMode), - /// Delete this account - Account(self::account::operation_mode::CliOperationMode), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///Сhoose what you want to delete pub enum Action { #[strum_discriminants(strum(message = "Delete an access key for this account"))] + /// Delete an access key for an account AccessKey(self::access_key::operation_mode::OperationMode), #[strum_discriminants(strum(message = "Delete this account"))] + /// Delete this account Account(self::account::operation_mode::OperationMode), } -impl CliAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::AccessKey(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("access-key".to_owned()); - command - } - Self::Account(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("account".to_owned()); - command - } - } - } -} - -impl From for CliAction { - fn from(action: Action) -> Self { - match action { - Action::AccessKey(operation_mode) => Self::AccessKey(operation_mode.into()), - Action::Account(operation_mode) => Self::Account(operation_mode.into()), - } - } -} - impl Action { - fn from(item: CliAction) -> color_eyre::eyre::Result { - match item { - CliAction::AccessKey(cli_operation_mode) => Ok(Action::AccessKey( - self::access_key::operation_mode::OperationMode::from(cli_operation_mode).unwrap(), - )), - CliAction::Account(cli_operation_mode) => Ok(Action::Account( - self::account::operation_mode::OperationMode::from(cli_operation_mode).unwrap(), - )), - } - } -} - -impl Action { - fn choose_action() -> color_eyre::eyre::Result { - println!(); - let variants = ActionDiscriminants::iter().collect::>(); - let actions = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_action = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Сhoose what you want to delete") - .items(&actions) - .default(0) - .interact() - .unwrap(); - let cli_action = match variants[selected_action] { - ActionDiscriminants::AccessKey => CliAction::AccessKey(Default::default()), - ActionDiscriminants::Account => CliAction::Account(Default::default()), - }; - Ok(Self::from(cli_action)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, diff --git a/src/commands/execute_command/change_method/call_function_type/mod.rs b/src/commands/execute_command/change_method/call_function_type/mod.rs index d97509df0..382f7a773 100644 --- a/src/commands/execute_command/change_method/call_function_type/mod.rs +++ b/src/commands/execute_command/change_method/call_function_type/mod.rs @@ -1,158 +1,71 @@ use dialoguer::Input; -/// вызов CallFunction -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCallFunctionAction { - method_name: Option, - args: Option, - #[clap(long = "attached-deposit")] - deposit: Option, - #[clap(long = "prepaid-gas")] - gas: Option, - #[clap(subcommand)] - send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::ExecuteChangeMethodCommandNetworkContext)] pub struct CallFunctionAction { method_name: String, - args: Vec, - gas: near_primitives::types::Gas, - deposit: near_primitives::types::Balance, - send_from: super::signer::SendFrom, -} - -impl CliCallFunctionAction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(gas) = &self.gas { - args.push_front(gas.to_string()); - args.push_front("--prepaid-gas".to_owned()) - }; - if let Some(deposit) = &self.deposit { - args.push_front(deposit.to_string()); - args.push_front("--attached-deposit".to_owned()) - }; - if let Some(function_args) = &self.args { - args.push_front(function_args.to_owned()); - }; - if let Some(method_name) = &self.method_name { - args.push_front(method_name.to_string()); - }; - args - } -} - -impl From for CliCallFunctionAction { - fn from(call_function_action: CallFunctionAction) -> Self { - Self { - method_name: Some(call_function_action.method_name), - args: Some(String::from_utf8(call_function_action.args).unwrap_or_default()), - gas: Some(call_function_action.gas.into()), - deposit: Some(crate::common::NearBalance::from_yoctonear( - call_function_action.deposit, - )), - send_from: Some(call_function_action.send_from.into()), - } - } + args: String, + #[interactive_clap(long = "prepaid-gas")] + gas: crate::common::NearGas, + #[interactive_clap(long = "attached-deposit")] + deposit: crate::common::NearBalance, + #[interactive_clap(named_arg)] + /// Specify a signer + signer: super::signer::Sender, } impl CallFunctionAction { - pub fn from( - item: CliCallFunctionAction, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let method_name: String = match item.method_name { - Some(cli_method_name) => cli_method_name, - None => CallFunctionAction::input_method_name(), - }; - let args: Vec = match item.args { - Some(cli_args) => cli_args.into_bytes(), - None => CallFunctionAction::input_args(), - }; - let gas: near_primitives::types::Gas = match item.gas { - Some(cli_gas) => match cli_gas { - crate::common::NearGas { inner: num } => num, - }, - None => CallFunctionAction::input_gas(), - }; - let deposit: near_primitives::types::Balance = match item.deposit { - Some(cli_deposit) => cli_deposit.to_yoctonear(), - None => CallFunctionAction::input_deposit(), - }; - let send_from = match item.send_from { - Some(cli_send_from) => super::signer::SendFrom::from(cli_send_from, connection_config)?, - None => super::signer::SendFrom::choose_send_from(connection_config)?, - }; - Ok(Self { - method_name, - args, - gas, - deposit, - send_from, - }) - } -} - -impl CallFunctionAction { - fn input_method_name() -> String { + fn input_method_name( + _context: &super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); - Input::new() + Ok(Input::new() .with_prompt("Enter a method name") - .interact_text() - .unwrap() + .interact_text()?) } - fn input_gas() -> near_primitives::types::Gas { + fn input_gas( + _context: &super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); let gas: u64 = loop { let input_gas: crate::common::NearGas = Input::new() .with_prompt("Enter a gas for function") .with_initial_text("100 TeraGas") - .interact_text() - .unwrap(); + .interact_text()?; let gas: u64 = match input_gas { crate::common::NearGas { inner: num } => num, }; if gas <= 300000000000000 { break gas; } else { - println!("You need to enter a value of no more than 300 TERAGAS") + println!("You need to enter a value of no more than 200 TERAGAS") } }; - gas + Ok(gas.into()) } - fn input_args() -> Vec { + fn input_args( + _context: &super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); - let input: String = Input::new() + Ok(Input::new() .with_prompt("Enter args for function") - .interact_text() - .unwrap(); - input.into_bytes() + .interact_text()?) } - fn input_deposit() -> near_primitives::types::Balance { + fn input_deposit( + _context: &super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); let deposit: crate::common::NearBalance = Input::new() .with_prompt( "Enter a deposit for function (example: 10NEAR or 0.5near or 10000yoctonear).", ) .with_initial_text("0 NEAR") - .interact_text() - .unwrap(); - deposit.to_yoctonear() + .interact_text()?; + Ok(deposit) } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,9 +74,9 @@ impl CallFunctionAction { let action = near_primitives::transaction::Action::FunctionCall( near_primitives::transaction::FunctionCallAction { method_name: self.method_name.clone(), - args: self.args.clone(), - gas: self.gas.clone(), - deposit: self.deposit.clone(), + args: self.args.clone().into_bytes(), + gas: self.gas.clone().inner, + deposit: self.deposit.clone().to_yoctonear(), }, ); let mut actions = prepopulated_unsigned_transaction.actions.clone(); @@ -172,7 +85,7 @@ impl CallFunctionAction { actions, ..prepopulated_unsigned_transaction }; - self.send_from + self.signer .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/execute_command/change_method/contract/mod.rs b/src/commands/execute_command/change_method/contract/mod.rs index 7ed5c9ba5..f44d60f21 100644 --- a/src/commands/execute_command/change_method/contract/mod.rs +++ b/src/commands/execute_command/change_method/contract/mod.rs @@ -1,140 +1,47 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify a contract ID - Contract(CliContract), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Contract(Contract), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Contract(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("contract".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Contract(contract) => Self::Contract(contract.into()), - } - } -} - -impl SendTo { - pub fn from( - item: CliSendTo, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendTo::Contract(cli_contract) => { - let contract = Contract::from(cli_contract, connection_config)?; - Ok(Self::Contract(contract)) - } - } - } -} - -impl SendTo { - pub fn send_to( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Self::from(CliSendTo::Contract(Default::default()), connection_config) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - SendTo::Contract(receiver) => { - receiver - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// данные о контракте -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliContract { - contract_account_id: Option, - #[clap(subcommand)] - call: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::ExecuteCommandNetworkContext)] +#[interactive_clap(skip_default_from_cli)] pub struct Contract { - pub contract_account_id: near_primitives::types::AccountId, - pub call: super::CallFunction, -} - -impl CliContract { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .call - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(contract_account_id) = &self.contract_account_id { - args.push_front(contract_account_id.to_string()); - } - args - } -} - -impl From for CliContract { - fn from(contract: Contract) -> Self { - Self { - contract_account_id: Some(contract.contract_account_id), - call: Some(contract.call.into()), - } - } + pub contract_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + /// вызов метода изменения + pub call: super::call_function_type::CallFunctionAction, } impl Contract { - fn from( - item: CliContract, - connection_config: Option, + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: super::operation_mode::ExecuteChangeMethodCommandNetworkContext, ) -> color_eyre::eyre::Result { - let contract_account_id: near_primitives::types::AccountId = match item.contract_account_id + let connection_config = context.connection_config.clone(); + let contract_account_id = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.contract_account_id) { - Some(cli_contract_account_id) => match &connection_config { + Some(contract_account_id) => match &connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_contract_account_id.clone(), + &network_connection_config, + contract_account_id.clone().into(), )? { - Some(_) => cli_contract_account_id, + Some(_) => contract_account_id, None => { - println!("Account <{}> doesn't exist", cli_contract_account_id); - Contract::input_receiver_account_id(connection_config.clone())? + println!("Account <{}> doesn't exist", contract_account_id); + Self::input_contract_account_id(&context)? } }, - None => cli_contract_account_id, + None => contract_account_id, }, - None => Contract::input_receiver_account_id(connection_config.clone())?, - }; - let call = match item.call { - Some(cli_call) => super::CallFunction::from(cli_call, connection_config)?, - None => super::CallFunction::choose_call_function(connection_config)?, + None => Self::input_contract_account_id(&context)?, }; + let call = super::call_function_type::CallFunctionAction::from_cli( + optional_clap_variant.and_then(|clap_variant| match clap_variant.call { + Some(ClapNamedArgCallFunctionActionForContract::Call(cli_args)) => Some(cli_args), + None => None, + }), + context, + )?; Ok(Self { contract_account_id, call, @@ -143,17 +50,17 @@ impl Contract { } impl Contract { - fn input_receiver_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + fn input_contract_account_id( + context: &super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What is the account ID of the contract?") - .interact_text() - .unwrap(); + .interact_text()?; if let Some(connection_config) = &connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -171,7 +78,7 @@ impl Contract { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - receiver_id: self.contract_account_id.clone(), + receiver_id: self.contract_account_id.clone().into(), ..prepopulated_unsigned_transaction }; self.call diff --git a/src/commands/execute_command/change_method/mod.rs b/src/commands/execute_command/change_method/mod.rs index 858728d27..19301ee47 100644 --- a/src/commands/execute_command/change_method/mod.rs +++ b/src/commands/execute_command/change_method/mod.rs @@ -1,93 +1,4 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; - mod call_function_type; mod contract; pub mod operation_mode; mod signer; - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliCallFunction { - /// вызов метода изменения - Call(self::call_function_type::CliCallFunctionAction), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum CallFunction { - #[strum_discriminants(strum(message = "Call function"))] - Call(self::call_function_type::CallFunctionAction), -} - -impl CliCallFunction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Call(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("call".to_owned()); - args - } - } - } -} - -impl From for CliCallFunction { - fn from(call_function: CallFunction) -> Self { - match call_function { - CallFunction::Call(call_function_action) => Self::Call(call_function_action.into()), - } - } -} - -impl CallFunction { - pub fn from( - item: CliCallFunction, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliCallFunction::Call(cli_call_function_action) => Ok(CallFunction::Call( - self::call_function_type::CallFunctionAction::from( - cli_call_function_action, - connection_config, - )?, - )), - } - } -} - -impl CallFunction { - pub fn choose_call_function( - connection_config: Option, - ) -> color_eyre::eyre::Result { - println!(); - let variants = CallFunctionDiscriminants::iter().collect::>(); - let commands = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Call your function") - .items(&commands) - .default(0) - .interact() - .unwrap(); - let cli_call = match variants[selection] { - CallFunctionDiscriminants::Call => CliCallFunction::Call(Default::default()), - }; - Ok(Self::from(cli_call, connection_config)?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - Self::Call(call_function_action) => { - call_function_action - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} diff --git a/src/commands/execute_command/change_method/operation_mode/mod.rs b/src/commands/execute_command/change_method/operation_mode/mod.rs index 2a3b9a12f..f225aa4a0 100644 --- a/src/commands/execute_command/change_method/operation_mode/mod.rs +++ b/src/commands/execute_command/change_method/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { + /// Prepare and, optionally, submit a new transaction with online mode #[strum_discriminants(strum(message = "Yes, I keep it simple"))] Network(self::online_mode::NetworkArgs), + /// Prepare and, optionally, submit a new transaction with offline mode #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct ExecuteChangeMethodCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs b/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs index e59b5a781..e34f47833 100644 --- a/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs +++ b/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs @@ -1,54 +1,38 @@ -/// аргументы, необходимые для offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_to: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_to: super::super::contract::SendTo, + #[interactive_clap(named_arg)] + ///Specify a contract + contract: super::super::contract::Contract, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::ExecuteChangeMethodCommandNetworkContext { + fn from(_: OfflineArgsContext) -> Self { Self { - send_to: Some(offline_args.send_to.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_to = match item.send_to { - Some(cli_send_to) => super::super::contract::SendTo::from(cli_send_to, None)?, - None => super::super::contract::SendTo::send_to(None)?, - }; - Ok(Self { send_to }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_to + self.contract .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/execute_command/change_method/operation_mode/online_mode/mod.rs b/src/commands/execute_command/change_method/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/execute_command/change_method/operation_mode/online_mode/mod.rs +++ b/src/commands/execute_command/change_method/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/mod.rs b/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/mod.rs index 838909edd..40b3320d1 100644 --- a/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,87 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::ExecuteChangeMethodCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs index 20b3c8a42..091906607 100644 --- a/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs @@ -1,140 +1,75 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_to: Option, -} - -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a contract + pub contract: super::super::super::super::contract::Contract, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_to: super::super::super::super::contract::SendTo, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::TransferCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a contract + pub contract: super::super::super::super::contract::Contract, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_to: Some(server.send_to.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::ExecuteChangeMethodCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_to: Some(server.send_to.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( +impl Server { + pub async fn process( self, + prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_to = match self.send_to { - Some(cli_send_to) => super::super::super::super::contract::SendTo::from( - cli_send_to, - Some(connection_config.clone()), - )?, - None => super::super::super::super::contract::SendTo::send_to(Some( - connection_config.clone(), - ))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_to, - }) + ) -> crate::CliResult { + self.contract + .process(prepopulated_unsigned_transaction, Some(connection_config)) + .await } } -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { - url: url.inner.clone(), - }); - let send_to = match self.send_to { - Some(cli_send_to) => super::super::super::super::contract::SendTo::from( - cli_send_to, - connection_config.clone(), - )?, - None => { - super::super::super::super::contract::SendTo::send_to(connection_config.clone())? - } - }; - Ok(Server { - connection_config, - send_to, - }) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { - self.send_to - .process(prepopulated_unsigned_transaction, self.connection_config) + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.contract + .process(prepopulated_unsigned_transaction, connection_config) .await } } diff --git a/src/commands/execute_command/change_method/signer/mod.rs b/src/commands/execute_command/change_method/signer/mod.rs index 21110a267..5cfdd7f1b 100644 --- a/src/commands/execute_command/change_method/signer/mod.rs +++ b/src/commands/execute_command/change_method/signer/mod.rs @@ -1,159 +1,75 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a signer - Signer(CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Signer(Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Signer(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("signer".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Signer(sender) => Self::Signer(sender.into()), - } - } -} -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Signer(cli_sender) => { - Ok(Self::Signer(Sender::from(cli_sender, connection_config)?)) - } - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Self::from(CliSendFrom::Signer(Default::default()), connection_config) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - SendFrom::Signer(sender) => { - sender - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// Specify a signer -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - pub sign_option: Option< - crate::commands::construct_transaction_command::sign_transaction::CliSignTransaction, - >, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::ExecuteChangeMethodCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, + #[interactive_clap(skip_default_from_cli)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] pub sign_option: crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .sign_option - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, +} + +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - sign_option: Some(sender.sign_option.into()), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id.clone())?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id.clone())?, - }; - Ok(Self { - sender_account_id, - sign_option, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::ExecuteChangeMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { loop { - let account_id: near_primitives::types::AccountId = Input::new() - .with_prompt("What is the account ID of the signer?") - .interact_text() - .unwrap(); - if let Some(connection_config) = &connection_config { + let account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("What is the account ID of the sender?") + .interact_text()?; + if let Some(connection_config) = &context.connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -171,7 +87,7 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; match self diff --git a/src/commands/execute_command/mod.rs b/src/commands/execute_command/mod.rs index 5f6ac524f..b192d6275 100644 --- a/src/commands/execute_command/mod.rs +++ b/src/commands/execute_command/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod change_method; mod view_method; -/// выбор метода для выполнения -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOptionMethod { - #[clap(subcommand)] - method: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OptionMethod { + #[interactive_clap(subcommand)] method: Method, } -impl CliOptionMethod { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.method - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOptionMethod { - fn from(option_method: OptionMethod) -> Self { - Self { - method: Some(option_method.method.into()), - } - } -} - -impl OptionMethod { - pub fn from(item: CliOptionMethod) -> color_eyre::eyre::Result { - let method = match item.method { - Some(cli_method) => Method::from(cli_method)?, - None => Method::choose_method()?, - }; - Ok(Self { method }) - } -} - impl OptionMethod { pub async fn process( self, @@ -57,87 +19,24 @@ impl OptionMethod { } } -#[derive(Debug, Clone, clap::Clap)] -enum CliMethod { - /// Specify a change method - ChangeMethod(self::change_method::operation_mode::CliOperationMode), - /// Specify a view method - ViewMethod(self::view_method::operation_mode::CliOperationMode), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] -enum Method { +#[interactive_clap(context = ())] +///Choose your method +pub enum Method { #[strum_discriminants(strum( message = "Execute a changing method (construct a transaction with a function call)" ))] + /// Specify a change method ChangeMethod(self::change_method::operation_mode::OperationMode), #[strum_discriminants(strum( message = "Execute a viewing method (read-only call, which does not require a transaction)" ))] + /// Specify a view method ViewMethod(self::view_method::operation_mode::OperationMode), } -impl CliMethod { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::ChangeMethod(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("change-method".to_owned()); - command - } - Self::ViewMethod(subcommand) => { - let mut command = subcommand.to_cli_args(); - command.push_front("view-method".to_owned()); - command - } - } - } -} - -impl From for CliMethod { - fn from(method: Method) -> Self { - match method { - Method::ChangeMethod(operation_mode) => Self::ChangeMethod(operation_mode.into()), - Method::ViewMethod(operation_mode) => Self::ViewMethod(operation_mode.into()), - } - } -} - impl Method { - fn from(item: CliMethod) -> color_eyre::eyre::Result { - match item { - CliMethod::ChangeMethod(cli_operation_mode) => Ok(Method::ChangeMethod( - self::change_method::operation_mode::OperationMode::from(cli_operation_mode)?, - )), - CliMethod::ViewMethod(cli_operation_mode) => Ok(Method::ViewMethod( - self::view_method::operation_mode::OperationMode::from(cli_operation_mode)?, - )), - } - } -} - -impl Method { - fn choose_method() -> color_eyre::eyre::Result { - println!(); - let variants = MethodDiscriminants::iter().collect::>(); - let methods = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_method = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Choose your method") - .items(&methods) - .default(0) - .interact() - .unwrap(); - let cli_method = match variants[selected_method] { - MethodDiscriminants::ChangeMethod => CliMethod::ChangeMethod(Default::default()), - MethodDiscriminants::ViewMethod => CliMethod::ViewMethod(Default::default()), - }; - Self::from(cli_method) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, diff --git a/src/commands/execute_command/view_method/block_id/block_id_hash/mod.rs b/src/commands/execute_command/view_method/block_id/block_id_hash/mod.rs index d3c7c3cf2..cfc3e22bc 100644 --- a/src/commands/execute_command/view_method/block_id/block_id_hash/mod.rs +++ b/src/commands/execute_command/view_method/block_id/block_id_hash/mod.rs @@ -1,50 +1,18 @@ use dialoguer::Input; -/// Specify the block_id hash for this contract to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHash { - block_id_hash: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext)] pub struct BlockIdHash { - block_id_hash: near_primitives::hash::CryptoHash, -} - -impl CliBlockIdHash { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_hash) = &self.block_id_hash { - args.push_front(block_id_hash.to_string()); - } - args - } -} - -impl From for CliBlockIdHash { - fn from(block_id_hash: BlockIdHash) -> Self { - Self { - block_id_hash: Some(block_id_hash.block_id_hash), - } - } -} - -impl From for BlockIdHash { - fn from(item: CliBlockIdHash) -> Self { - let block_id_hash: near_primitives::hash::CryptoHash = match item.block_id_hash { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHash::input_block_id_hash(), - }; - Self { block_id_hash } - } + block_id_hash: crate::types::crypto_hash::CryptoHash, } impl BlockIdHash { - pub fn input_block_id_hash() -> near_primitives::hash::CryptoHash { - Input::new() + pub fn input_block_id_hash( + _context: &super::super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Type the block ID hash for this contract") - .interact_text() - .unwrap() + .interact_text()?) } fn rpc_client(&self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { @@ -64,7 +32,7 @@ impl BlockIdHash { .rpc_client(network_connection_config.archival_rpc_url().as_str()) .query(near_jsonrpc_primitives::types::query::RpcQueryRequest { block_reference: near_primitives::types::BlockReference::BlockId( - near_primitives::types::BlockId::Hash(self.block_id_hash.clone()), + near_primitives::types::BlockId::Hash(self.block_id_hash.clone().into()), ), request: near_primitives::views::QueryRequest::CallFunction { account_id: contract_account_id, @@ -84,15 +52,12 @@ impl BlockIdHash { } else { return Err(color_eyre::Report::msg(format!("Error call result"))); }; - let call_result_str = String::from_utf8(call_result).unwrap(); + let call_result_str = String::from_utf8(call_result)?; let serde_call_result: serde_json::Value = serde_json::from_str(&call_result_str) .map_err(|err| color_eyre::Report::msg(format!("serde json: {:?}", err)))?; println!("--------------"); println!(); - println!( - "{}", - serde_json::to_string_pretty(&serde_call_result).unwrap() - ); + println!("{}", serde_json::to_string_pretty(&serde_call_result)?); Ok(()) } } diff --git a/src/commands/execute_command/view_method/block_id/block_id_height/mod.rs b/src/commands/execute_command/view_method/block_id/block_id_height/mod.rs index 9c7e327e5..285e09727 100644 --- a/src/commands/execute_command/view_method/block_id/block_id_height/mod.rs +++ b/src/commands/execute_command/view_method/block_id/block_id_height/mod.rs @@ -1,50 +1,18 @@ use dialoguer::Input; -/// Specify the block_id height for this contract to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHeight { - block_id_height: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext)] pub struct BlockIdHeight { block_id_height: near_primitives::types::BlockHeight, } -impl CliBlockIdHeight { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_height) = &self.block_id_height { - args.push_front(block_id_height.to_string()); - } - args - } -} - -impl From for CliBlockIdHeight { - fn from(block_id_height: BlockIdHeight) -> Self { - Self { - block_id_height: Some(block_id_height.block_id_height), - } - } -} - -impl From for BlockIdHeight { - fn from(item: CliBlockIdHeight) -> Self { - let block_id_height: near_primitives::types::BlockHeight = match item.block_id_height { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHeight::input_block_id_height(), - }; - Self { block_id_height } - } -} - impl BlockIdHeight { - pub fn input_block_id_height() -> near_primitives::types::BlockHeight { - Input::new() + pub fn input_block_id_height( + _context: &super::super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Type the block ID height for this contract") - .interact_text() - .unwrap() + .interact_text()?) } fn rpc_client(&self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { @@ -84,15 +52,12 @@ impl BlockIdHeight { } else { return Err(color_eyre::Report::msg(format!("Error call result"))); }; - let call_result_str = String::from_utf8(call_result).unwrap(); + let call_result_str = String::from_utf8(call_result)?; let serde_call_result: serde_json::Value = serde_json::from_str(&call_result_str) .map_err(|err| color_eyre::Report::msg(format!("serde json: {:?}", err)))?; println!("--------------"); println!(); - println!( - "{}", - serde_json::to_string_pretty(&serde_call_result).unwrap() - ); + println!("{}", serde_json::to_string_pretty(&serde_call_result)?); Ok(()) } } diff --git a/src/commands/execute_command/view_method/block_id/mod.rs b/src/commands/execute_command/view_method/block_id/mod.rs index e5560c3bf..4700117df 100644 --- a/src/commands/execute_command/view_method/block_id/mod.rs +++ b/src/commands/execute_command/view_method/block_id/mod.rs @@ -1,98 +1,25 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod block_id_hash; mod block_id_height; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliBlockId { - /// Specify a block ID final to view this contract - AtFinalBlock, - /// Specify a block ID height to view this contract - AtBlockHeight(self::block_id_height::CliBlockIdHeight), - /// Specify a block ID hash to view this contract - AtBlockHash(self::block_id_hash::CliBlockIdHash), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext)] +///Choose Block ID pub enum BlockId { #[strum_discriminants(strum(message = "View this contract at final block"))] + /// Specify a block ID final to view this contract AtFinalBlock, #[strum_discriminants(strum(message = "View this contract at block heigt"))] + /// Specify a block ID height to view this contract AtBlockHeight(self::block_id_height::BlockIdHeight), #[strum_discriminants(strum(message = "View this contract at block hash"))] + /// Specify a block ID hash to view this contract AtBlockHash(self::block_id_hash::BlockIdHash), } -impl CliBlockId { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::AtFinalBlock => { - let mut args = std::collections::VecDeque::new(); - args.push_front("at-final-block".to_owned()); - args - } - Self::AtBlockHeight(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-height".to_owned()); - args - } - Self::AtBlockHash(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-hash".to_owned()); - args - } - } - } -} - -impl From for CliBlockId { - fn from(block_id: BlockId) -> Self { - match block_id { - BlockId::AtFinalBlock => Self::AtFinalBlock, - BlockId::AtBlockHeight(block_id_height) => Self::AtBlockHeight(block_id_height.into()), - BlockId::AtBlockHash(block_id_hash) => Self::AtBlockHash(block_id_hash.into()), - } - } -} - -impl From for BlockId { - fn from(item: CliBlockId) -> Self { - match item { - CliBlockId::AtFinalBlock => Self::AtFinalBlock, - CliBlockId::AtBlockHeight(cli_block_id_height) => { - Self::AtBlockHeight(cli_block_id_height.into()) - } - CliBlockId::AtBlockHash(cli_block_id_hash) => { - Self::AtBlockHash(cli_block_id_hash.into()) - } - } - } -} - impl BlockId { - pub fn choose_block_id() -> Self { - println!(); - let variants = BlockIdDiscriminants::iter().collect::>(); - let blocks = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Choose your action") - .items(&blocks) - .default(0) - .interact() - .unwrap(); - let cli_block_id = match variants[selection] { - BlockIdDiscriminants::AtFinalBlock => CliBlockId::AtFinalBlock, - BlockIdDiscriminants::AtBlockHeight => CliBlockId::AtBlockHeight(Default::default()), - BlockIdDiscriminants::AtBlockHash => CliBlockId::AtBlockHash(Default::default()), - }; - Self::from(cli_block_id) - } - pub async fn process( self, contract_account_id: near_primitives::types::AccountId, @@ -169,15 +96,12 @@ impl BlockId { } else { return Err(color_eyre::Report::msg(format!("Error call result"))); }; - let call_result_str = String::from_utf8(call_result).unwrap(); + let call_result_str = String::from_utf8(call_result)?; let serde_call_result: serde_json::Value = serde_json::from_str(&call_result_str) .map_err(|err| color_eyre::Report::msg(format!("serde json: {:?}", err)))?; println!("--------------"); println!(); - println!( - "{}", - serde_json::to_string_pretty(&serde_call_result).unwrap() - ); + println!("{}", serde_json::to_string_pretty(&serde_call_result)?); Ok(()) } } diff --git a/src/commands/execute_command/view_method/call_function_type/mod.rs b/src/commands/execute_command/view_method/call_function_type/mod.rs index efde0f679..e7e225b89 100644 --- a/src/commands/execute_command/view_method/call_function_type/mod.rs +++ b/src/commands/execute_command/view_method/call_function_type/mod.rs @@ -1,93 +1,31 @@ use dialoguer::Input; -/// Call view function -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCallFunctionView { - method_name: Option, - function_args: Option, - #[clap(subcommand)] - selected_block_id: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext)] pub struct CallFunctionView { method_name: String, - function_args: Vec, + function_args: String, + #[interactive_clap(subcommand)] selected_block_id: super::block_id::BlockId, } -impl CliCallFunctionView { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .selected_block_id - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(function_args) = &self.function_args { - args.push_front(function_args.to_owned()); - }; - if let Some(method_name) = &self.method_name { - args.push_front(method_name.to_string()); - }; - args - } -} - -impl From for CliCallFunctionView { - fn from(call_function_view: CallFunctionView) -> Self { - Self { - method_name: Some(call_function_view.method_name), - function_args: Some( - String::from_utf8(call_function_view.function_args).unwrap_or_default(), - ), - selected_block_id: Some(call_function_view.selected_block_id.into()), - } - } -} - -impl From for CallFunctionView { - fn from(item: CliCallFunctionView) -> Self { - let method_name: String = match item.method_name { - Some(cli_method_name) => cli_method_name, - None => CallFunctionView::input_method_name(), - }; - let function_args: Vec = match item.function_args { - Some(cli_args) => cli_args.into_bytes(), - None => CallFunctionView::input_function_args(), - }; - let selected_block_id: super::block_id::BlockId = match item.selected_block_id { - Some(cli_block_id) => cli_block_id.into(), - None => super::block_id::BlockId::choose_block_id(), - }; - Self { - method_name, - function_args, - selected_block_id, - } - } -} - impl CallFunctionView { - fn input_method_name() -> String { + fn input_method_name( + _context: &super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); - Input::new() + Ok(Input::new() .with_prompt("Enter a method name") - .interact_text() - .unwrap() + .interact_text()?) } - fn input_function_args() -> Vec { + fn input_function_args( + _context: &super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); - let input: String = Input::new() + Ok(Input::new() .with_prompt("Enter args for function") - .interact_text() - .unwrap(); - input.into_bytes() + .interact_text()?) } pub async fn process( @@ -100,7 +38,7 @@ impl CallFunctionView { contract_account_id, network_connection_config, self.method_name, - self.function_args, + self.function_args.into_bytes(), ) .await } diff --git a/src/commands/execute_command/view_method/contract/mod.rs b/src/commands/execute_command/view_method/contract/mod.rs new file mode 100644 index 000000000..eadcd7afb --- /dev/null +++ b/src/commands/execute_command/view_method/contract/mod.rs @@ -0,0 +1,85 @@ +use dialoguer::Input; + +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext)] +#[interactive_clap(skip_default_from_cli)] +pub struct Contract { + pub contract_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + /// вызов метода изменения + pub call: super::call_function_type::CallFunctionView, +} + +impl Contract { + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let contract_account_id = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.contract_account_id) + { + Some(contract_account_id) => match crate::common::get_account_state( + &connection_config, + contract_account_id.clone().into(), + )? { + Some(_) => contract_account_id, + None => { + println!("Account <{}> doesn't exist", contract_account_id); + Self::input_contract_account_id(&context)? + } + }, + None => Self::input_contract_account_id(&context)?, + }; + let call = super::call_function_type::CallFunctionView::from_cli( + optional_clap_variant.and_then(|clap_variant| match clap_variant.call { + Some(ClapNamedArgCallFunctionViewForContract::Call(cli_args)) => Some(cli_args), + None => None, + }), + context, + )?; + Ok(Self { + contract_account_id, + call, + }) + } +} + +impl Contract { + fn input_contract_account_id( + context: &super::operation_mode::online_mode::select_server::ExecuteViewMethodCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + loop { + let contract_account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("What is the account ID of the contract?") + .interact_text()?; + let contract_code_hash: near_primitives::hash::CryptoHash = + match crate::common::get_account_state( + &connection_config, + contract_account_id.clone().into(), + )? { + Some(account_view) => account_view.code_hash, + None => near_primitives::hash::CryptoHash::default(), + }; + if contract_code_hash == near_primitives::hash::CryptoHash::default() { + println!( + "Contract code is not deployed to this account <{}>.", + contract_account_id.to_string() + ) + } else { + break Ok(contract_account_id); + } + } + } + + pub async fn process( + self, + network_connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + self.call + .process(network_connection_config, self.contract_account_id.into()) + .await + } +} diff --git a/src/commands/execute_command/view_method/mod.rs b/src/commands/execute_command/view_method/mod.rs index b1ed42b6d..6734d386e 100644 --- a/src/commands/execute_command/view_method/mod.rs +++ b/src/commands/execute_command/view_method/mod.rs @@ -1,85 +1,4 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; - mod block_id; mod call_function_type; +mod contract; pub mod operation_mode; -mod receiver; - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliCallFunction { - /// Call view function - Call(self::call_function_type::CliCallFunctionView), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum CallFunction { - #[strum_discriminants(strum(message = "Call function"))] - Call(self::call_function_type::CallFunctionView), -} - -impl CliCallFunction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Call(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("call".to_owned()); - args - } - } - } -} - -impl From for CliCallFunction { - fn from(call_function: CallFunction) -> Self { - match call_function { - CallFunction::Call(call_function_action) => Self::Call(call_function_action.into()), - } - } -} - -impl From for CallFunction { - fn from(item: CliCallFunction) -> Self { - match item { - CliCallFunction::Call(cli_call_function_view) => { - CallFunction::Call(cli_call_function_view.into()) - } - } - } -} - -impl CallFunction { - pub fn choose_call_function() -> Self { - println!(); - let variants = CallFunctionDiscriminants::iter().collect::>(); - let commands = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Call your function") - .items(&commands) - .default(0) - .interact() - .unwrap(); - let cli_call = match variants[selection] { - CallFunctionDiscriminants::Call => CliCallFunction::Call(Default::default()), - }; - Self::from(cli_call) - } - - pub async fn process( - self, - network_connection_config: crate::common::ConnectionConfig, - contract_account_id: near_primitives::types::AccountId, - ) -> crate::CliResult { - match self { - Self::Call(call_function_action) => { - call_function_action - .process(network_connection_config, contract_account_id) - .await - } - } - } -} diff --git a/src/commands/execute_command/view_method/operation_mode/mod.rs b/src/commands/execute_command/view_method/operation_mode/mod.rs index 13282dc17..ca22b3604 100644 --- a/src/commands/execute_command/view_method/operation_mode/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/mod.rs @@ -1,110 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } + #[interactive_clap(named_arg)] + /// Prepare and, optionally, submit a new transaction with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - } - } -} - -impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs b/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs index 66d106b3c..0dd6b223d 100644 --- a/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs index 393ab0ed8..e7c4a2f50 100644 --- a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) - } - } - } +#[derive(Clone)] +pub struct ExecuteViewMethodCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for ExecuteViewMethodCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs index eaebccf6d..5b0cebde1 100644 --- a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs @@ -1,132 +1,67 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_to: Option, -} - -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a contract + pub contract: super::super::super::super::contract::Contract, } -#[derive(Debug, Clone)] -pub struct Server { - pub network_connection_config: crate::common::ConnectionConfig, - pub send_to: super::super::super::super::receiver::SendTo, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::ExecuteViewMethodCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a contract + pub contract: super::super::super::super::contract::Contract, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.network_connection_config.rpc_url().as_str(), - ) - .unwrap(), - ), - send_to: Some(server.send_to.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::ExecuteViewMethodCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_to: Some(server.send_to.into()), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn into_server( +impl Server { + pub async fn process( self, - network_connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_to = match self.send_to { - Some(cli_send_to) => super::super::super::super::receiver::SendTo::from( - cli_send_to, - network_connection_config.clone(), - )?, - None => super::super::super::super::receiver::SendTo::send_to( - network_connection_config.clone(), - )?, - }; - Ok(Server { - network_connection_config, - send_to, - }) + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + self.contract.process(connection_config).await } } -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = crate::common::ConnectionConfig::Custom { - url: url.inner.clone(), - }; - let send_to = match self.send_to { - Some(cli_send_to) => { - super::super::super::super::receiver::SendTo::from(cli_send_to, connection_config)? - } - None => super::super::super::super::receiver::SendTo::send_to(connection_config)?, - }; - Ok(Server { - network_connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, - send_to, - }) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl Server { pub async fn process(self) -> crate::CliResult { - self.send_to.process(self.network_connection_config).await + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + self.contract.process(connection_config).await } } diff --git a/src/commands/execute_command/view_method/receiver/mod.rs b/src/commands/execute_command/view_method/receiver/mod.rs index 3f2e3e742..f314fc955 100644 --- a/src/commands/execute_command/view_method/receiver/mod.rs +++ b/src/commands/execute_command/view_method/receiver/mod.rs @@ -151,7 +151,7 @@ impl Receiver { let contract_account_id: near_primitives::types::AccountId = Input::new() .with_prompt("What is the account ID of the contract?") .interact_text() - .unwrap(); + ?; let contract_code_hash: near_primitives::hash::CryptoHash = match crate::common::get_account_state( &connection_config, diff --git a/src/commands/login/operation_mode/mod.rs b/src/commands/login/operation_mode/mod.rs index abaa63c00..9ef8ff344 100644 --- a/src/commands/login/operation_mode/mod.rs +++ b/src/commands/login/operation_mode/mod.rs @@ -1,108 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), - }; - Self { mode } - } + #[interactive_clap(named_arg)] + ///Execute a change method with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl From for Mode { - fn from(item: CliMode) -> Self { - match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), - } - } -} - -impl Mode { - pub fn choose_mode() -> Self { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/login/operation_mode/online_mode/mod.rs b/src/commands/login/operation_mode/online_mode/mod.rs index 1f68d88a1..0dd6b223d 100644 --- a/src/commands/login/operation_mode/online_mode/mod.rs +++ b/src/commands/login/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) - } - None => self::select_server::SelectServer::choose_server(), - }; - Self { selected_server } - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/login/operation_mode/online_mode/select_server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/mod.rs index 45048a1a5..7f6c62b7b 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { - match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } - CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) - } - } - } +#[derive(Clone)] +pub struct LoginCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> Self { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol wallet url") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for LoginCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs index cb98a5a6a..a4e2ecf54 100644 --- a/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/login/operation_mode/online_mode/select_server/server/mod.rs @@ -2,130 +2,113 @@ use std::str::FromStr; use dialoguer::Input; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliServer {} - -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server {} + +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::LoginCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: crate::common::ConnectionConfig, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); +impl CustomServerContext { + fn _from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + url: scope.url.clone(), } - args } } -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl From for super::LoginCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.rpc_url().as_str(), - ) - .unwrap() - .inner, - ), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - std::collections::VecDeque::new() - } -} - -impl From for CliServer { - fn from(_: Server) -> Self { - Self {} +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl CliServer { - pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { - Server { connection_config } + pub async fn process(self) -> crate::CliResult { + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + login(connection_config).await } } -impl CliCustomServer { - pub fn into_server(self) -> Server { - let url: url::Url = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the wallet url?") - .interact_text() - .unwrap(), - }; - Server { - connection_config: crate::common::ConnectionConfig::Custom { url }, - } +impl Server { + pub async fn process( + self, + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + login(connection_config).await } } -impl Server { - pub async fn process(self) -> crate::CliResult { - let key_pair_properties: crate::common::KeyPairProperties = - crate::common::generate_keypair().await?; - let mut url: url::Url = self.connection_config.wallet_url().join("login/")?; - url.query_pairs_mut() - .append_pair("title", "NEAR CLI") - .append_pair("public_key", &key_pair_properties.public_key_str); - // Use `success_url` once capture mode is implemented - //.append_pair("success_url", "http://127.0.0.1:8080"); - println!( - "If your browser doesn't automatically open, please visit this URL:\n {}\n", - &url.as_str() - ); - open::that(url.as_ref()).ok(); - - let public_key: near_crypto::PublicKey = - near_crypto::PublicKey::from_str(&key_pair_properties.public_key_str)?; - - let account_id = get_account_from_cli(public_key, self.connection_config.clone()).await?; - // save_account(&account_id, key_pair_properties, self.connection_config).await? - crate::common::save_access_key_to_keychain( - Some(self.connection_config), - key_pair_properties.clone(), - &account_id.to_string(), - ) - .await - .map_err(|err| { - color_eyre::Report::msg(format!("Failed to save a file with access key: {}", err)) - })?; - Ok(()) - } +async fn login(connection_config: crate::common::ConnectionConfig) -> crate::CliResult { + let key_pair_properties: crate::common::KeyPairProperties = + crate::common::generate_keypair().await?; + let mut url: url::Url = connection_config.wallet_url().join("login/")?; + url.query_pairs_mut() + .append_pair("title", "NEAR CLI") + .append_pair("public_key", &key_pair_properties.public_key_str); + // Use `success_url` once capture mode is implemented + //.append_pair("success_url", "http://127.0.0.1:8080"); + println!( + "If your browser doesn't automatically open, please visit this URL:\n {}\n", + &url.as_str() + ); + // url.open(); + open::that(url.as_ref()).ok(); + + let public_key: near_crypto::PublicKey = + near_crypto::PublicKey::from_str(&key_pair_properties.public_key_str)?; + + let account_id = get_account_from_cli(public_key, connection_config.clone()).await?; + // save_account(&account_id, key_pair_properties, self.connection_config).await? + crate::common::save_access_key_to_keychain( + Some(connection_config), + key_pair_properties.clone(), + &account_id.to_string(), + ) + .await + .map_err(|err| { + color_eyre::Report::msg(format!("Failed to save a file with access key: {}", err)) + })?; + Ok(()) } async fn get_account_from_cli( public_key: near_crypto::PublicKey, network_connection_config: crate::common::ConnectionConfig, ) -> color_eyre::eyre::Result { - let account_id = input_account_id(); + let account_id = input_account_id()?; verify_account_id(account_id.clone(), public_key, network_connection_config) .await .map_err(|err| color_eyre::Report::msg(format!("Failed account ID: {:?}", err)))?; Ok(account_id) } -fn input_account_id() -> near_primitives::types::AccountId { - Input::new() +fn input_account_id() -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter account ID") - .interact_text() - .unwrap() + .interact_text()?) } fn rpc_client(selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 33006ef09..2bfcfdf51 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,5 +1,4 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod add_command; pub mod construct_transaction_command; @@ -11,188 +10,42 @@ pub mod transfer_command; pub mod utils_command; pub mod view_command; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliTopLevelCommand { - /// Use these to add access key, contract code, stake proposal, sub-account, implicit-account - Add(self::add_command::CliAddAction), - /// Prepare and, optionally, submit a new transaction - ConstructTransaction(self::construct_transaction_command::operation_mode::CliOperationMode), - /// Use these to delete access key, sub-account - Delete(self::delete_command::CliDeleteAction), - /// Execute function (contract method) - Execute(self::execute_command::CliOptionMethod), - /// Use these to generate static shell completions - GenerateShellCompletions(self::generate_shell_completions_command::CliGenerateShellCompletions), - /// Use these to login with wallet authorization - Login(self::login::operation_mode::CliOperationMode), - /// Use these to transfer tokens - Transfer(self::transfer_command::CliCurrency), - /// Helpers - Utils(self::utils_command::CliUtils), - /// View account, contract code, contract state, transaction, nonce, recent block hash - View(self::view_command::CliViewQueryRequest), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///Choose transaction action pub enum TopLevelCommand { #[strum_discriminants(strum(message = "Login with wallet authorization"))] + ///Use these to login with wallet authorization Login(self::login::operation_mode::OperationMode), #[strum_discriminants(strum( message = "View account, contract code, contract state, transaction, nonce, recent block hash" ))] + ///View account, contract code, contract state, transaction, nonce, recent block hash View(self::view_command::ViewQueryRequest), #[strum_discriminants(strum(message = "Transfer tokens"))] + ///Use these to transfer tokens Transfer(self::transfer_command::Currency), #[strum_discriminants(strum(message = "Execute function (contract method)"))] + ///Execute function (contract method) Execute(self::execute_command::OptionMethod), #[strum_discriminants(strum( message = "Add access key, contract code, stake proposal, sub-account, implicit-account" ))] + ///Use these to add access key, contract code, stake proposal, sub-account, implicit-account Add(self::add_command::AddAction), #[strum_discriminants(strum(message = "Delete access key, account"))] + ///Use these to delete access key, sub-account Delete(self::delete_command::DeleteAction), #[strum_discriminants(strum(message = "Construct a new transaction"))] + ///Prepare and, optionally, submit a new transaction ConstructTransaction(self::construct_transaction_command::operation_mode::OperationMode), #[strum_discriminants(strum(message = "Helpers"))] + ///Helpers Utils(self::utils_command::Utils), } -impl CliTopLevelCommand { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Login(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("login".to_owned()); - args - } - Self::Execute(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("execute".to_owned()); - args - } - Self::Add(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("add".to_owned()); - args - } - Self::Delete(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("delete".to_owned()); - args - } - Self::Transfer(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("transfer".to_owned()); - args - } - Self::View(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("view".to_owned()); - args - } - Self::ConstructTransaction(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("construct-transaction".to_owned()); - args - } - Self::Utils(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("utils".to_owned()); - args - } - Self::GenerateShellCompletions(_) => std::collections::VecDeque::new(), - } - } -} - -impl From for CliTopLevelCommand { - fn from(top_level_command: TopLevelCommand) -> Self { - match top_level_command { - TopLevelCommand::Login(operation_mode) => Self::Login(operation_mode.into()), - TopLevelCommand::Execute(option_method) => Self::Execute(option_method.into()), - TopLevelCommand::Add(add_action) => Self::Add(add_action.into()), - TopLevelCommand::Delete(delete_action) => Self::Delete(delete_action.into()), - TopLevelCommand::Transfer(currency) => Self::Transfer(currency.into()), - TopLevelCommand::View(view_query_request) => Self::View(view_query_request.into()), - TopLevelCommand::ConstructTransaction(operation_mode) => { - Self::ConstructTransaction(operation_mode.into()) - } - TopLevelCommand::Utils(utils) => Self::Utils(utils.into()), - } - } -} - -impl From for TopLevelCommand { - fn from(cli_top_level_command: CliTopLevelCommand) -> Self { - match cli_top_level_command { - CliTopLevelCommand::Add(cli_add_action) => { - TopLevelCommand::Add(self::add_command::AddAction::from(cli_add_action).unwrap()) - } - CliTopLevelCommand::ConstructTransaction(cli_operation_mode) => { - TopLevelCommand::ConstructTransaction( - self::construct_transaction_command::operation_mode::OperationMode::from( - cli_operation_mode, - ) - .unwrap(), - ) - } - CliTopLevelCommand::Delete(cli_delete_action) => TopLevelCommand::Delete( - self::delete_command::DeleteAction::from(cli_delete_action).unwrap(), - ), - CliTopLevelCommand::Execute(cli_option_method) => TopLevelCommand::Execute( - self::execute_command::OptionMethod::from(cli_option_method).unwrap(), - ), - CliTopLevelCommand::GenerateShellCompletions(_) => { - unreachable!("This variant is handled in the main function") - } - CliTopLevelCommand::Login(cli_option_method) => { - TopLevelCommand::Login(cli_option_method.into()) - } - CliTopLevelCommand::Transfer(cli_currency) => TopLevelCommand::Transfer( - self::transfer_command::Currency::from(cli_currency).unwrap(), - ), - CliTopLevelCommand::Utils(cli_util) => TopLevelCommand::Utils(cli_util.into()), - CliTopLevelCommand::View(cli_view_query_request) => { - TopLevelCommand::View(cli_view_query_request.into()) - } - } - } -} - impl TopLevelCommand { - pub fn choose_command() -> Self { - println!(); - let variants = TopLevelCommandDiscriminants::iter().collect::>(); - let commands = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Choose your action") - .items(&commands) - .default(0) - .interact() - .unwrap(); - let cli_top_level_command = match variants[selection] { - TopLevelCommandDiscriminants::Add => CliTopLevelCommand::Add(Default::default()), - TopLevelCommandDiscriminants::ConstructTransaction => { - CliTopLevelCommand::ConstructTransaction(Default::default()) - } - TopLevelCommandDiscriminants::Delete => CliTopLevelCommand::Delete(Default::default()), - TopLevelCommandDiscriminants::Execute => { - CliTopLevelCommand::Execute(Default::default()) - } - TopLevelCommandDiscriminants::Login => CliTopLevelCommand::Login(Default::default()), - TopLevelCommandDiscriminants::Transfer => { - CliTopLevelCommand::Transfer(Default::default()) - } - TopLevelCommandDiscriminants::Utils => CliTopLevelCommand::Utils(Default::default()), - TopLevelCommandDiscriminants::View => CliTopLevelCommand::View(Default::default()), - }; - Self::from(cli_top_level_command) - } - pub async fn process(self) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { signer_id: near_primitives::types::AccountId::test_account(), diff --git a/src/commands/transfer_command/mod.rs b/src/commands/transfer_command/mod.rs index ffcee6a74..8aede2f50 100644 --- a/src/commands/transfer_command/mod.rs +++ b/src/commands/transfer_command/mod.rs @@ -1,55 +1,17 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod operation_mode; mod receiver; mod sender; pub mod transfer_near_tokens_type; -/// инструмент выбора переводимой валюты -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCurrency { - #[clap(subcommand)] - currency_selection: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct Currency { + #[interactive_clap(subcommand)] currency_selection: CurrencySelection, } -impl CliCurrency { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.currency_selection - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliCurrency { - fn from(currency: Currency) -> Self { - Self { - currency_selection: Some(CliCurrencySelection::from(currency.currency_selection)), - } - } -} - -impl Currency { - pub fn from(item: CliCurrency) -> color_eyre::eyre::Result { - let currency_selection = match item.currency_selection { - Some(cli_currency_selection) => CurrencySelection::from(cli_currency_selection)?, - None => CurrencySelection::choose_currency()?, - }; - Ok(Self { currency_selection }) - } -} - impl Currency { pub async fn process( self, @@ -61,77 +23,23 @@ impl Currency { } } -#[derive(Debug, Clone, clap::Clap)] -enum CliCurrencySelection { - /// отправка трансфера в NEAR tokens - NEAR(self::operation_mode::CliOperationMode), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] -enum CurrencySelection { +#[interactive_clap(context = ())] +///What do you want to transfer? +pub enum CurrencySelection { + /// The transfer is carried out in NEAR tokens #[strum_discriminants(strum(message = "NEAR tokens"))] - NEAR(self::operation_mode::OperationMode), -} - -impl CliCurrencySelection { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::NEAR(operation_mode) => { - let mut args = operation_mode.to_cli_args(); - args.push_front("near".to_owned()); - args - } - } - } -} - -impl From for CliCurrencySelection { - fn from(currency_selection: CurrencySelection) -> Self { - match currency_selection { - CurrencySelection::NEAR(operation_mode) => { - Self::NEAR(self::operation_mode::CliOperationMode::from(operation_mode)) - } - } - } -} - -impl CurrencySelection { - fn from(item: CliCurrencySelection) -> color_eyre::eyre::Result { - match item { - CliCurrencySelection::NEAR(cli_operation_mode) => Ok(Self::NEAR( - self::operation_mode::OperationMode::from(cli_operation_mode)?, - )), - } - } + Near(self::operation_mode::OperationMode), } impl CurrencySelection { - fn choose_currency() -> color_eyre::eyre::Result { - println!(); - let variants = CurrencySelectionDiscriminants::iter().collect::>(); - let currencies = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_currency = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("What do you want to transfer?") - .items(¤cies) - .default(0) - .interact() - .unwrap(); - let cli_currency = match variants[selected_currency] { - CurrencySelectionDiscriminants::NEAR => CliCurrencySelection::NEAR(Default::default()), - }; - Ok(Self::from(cli_currency)?) - } - async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { match self { - Self::NEAR(operation_mode) => { + Self::Near(operation_mode) => { operation_mode .process(prepopulated_unsigned_transaction) .await diff --git a/src/commands/transfer_command/operation_mode/mod.rs b/src/commands/transfer_command/operation_mode/mod.rs index 2a3b9a12f..465378127 100644 --- a/src/commands/transfer_command/operation_mode/mod.rs +++ b/src/commands/transfer_command/operation_mode/mod.rs @@ -1,53 +1,15 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod offline_mode; mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { + #[interactive_clap(subcommand)] pub mode: Mode, } -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl OperationMode { - pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode)?, - None => Mode::choose_mode()?, - }; - Ok(Self { mode }) - } -} - impl OperationMode { pub async fn process( self, @@ -57,92 +19,23 @@ impl OperationMode { } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Prepare and, optionally, submit a new transaction with online mode - Network(self::online_mode::CliNetworkArgs), - /// Prepare and, optionally, submit a new transaction with offline mode - Offline(self::offline_mode::CliOfflineArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. +///Do you want to derive some information required for transaction construction automatically querying it online? pub enum Mode { + /// Prepare and, optionally, submit a new transaction with online mode #[strum_discriminants(strum(message = "Yes, I keep it simple"))] Network(self::online_mode::NetworkArgs), + /// Prepare and, optionally, submit a new transaction with offline mode #[strum_discriminants(strum( message = "No, I want to work in no-network (air-gapped) environment" ))] Offline(self::offline_mode::OfflineArgs), } -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - Self::Offline(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("offline".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - Mode::Offline(offline_args) => { - Self::Offline(self::offline_mode::CliOfflineArgs::from(offline_args)) - } - } - } -} - -impl Mode { - fn from(item: CliMode) -> color_eyre::eyre::Result { - match item { - CliMode::Network(cli_network_args) => Ok(Self::Network( - self::online_mode::NetworkArgs::from(cli_network_args)?, - )), - CliMode::Offline(cli_offline_args) => Ok(Self::Offline( - self::offline_mode::OfflineArgs::from(cli_offline_args)?, - )), - } - } -} - impl Mode { - fn choose_mode() -> color_eyre::eyre::Result { - println!(); - let variants = ModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt( - "To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed. - \nDo you want to derive some information required for transaction construction automatically querying it online?" - ) - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - ModeDiscriminants::Network => CliMode::Network(Default::default()), - ModeDiscriminants::Offline => CliMode::Offline(Default::default()), - }; - Ok(Self::from(cli_mode)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -161,3 +54,7 @@ impl Mode { } } } + +pub struct TransferCommandNetworkContext { + pub connection_config: Option, +} diff --git a/src/commands/transfer_command/operation_mode/offline_mode/mod.rs b/src/commands/transfer_command/operation_mode/offline_mode/mod.rs index 775982073..fa3df8ef1 100644 --- a/src/commands/transfer_command/operation_mode/offline_mode/mod.rs +++ b/src/commands/transfer_command/operation_mode/offline_mode/mod.rs @@ -1,56 +1,37 @@ -/// аргументы, необходимые для создания трансфера в offline mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOfflineArgs { - #[clap(subcommand)] - pub send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = OfflineArgsContext)] pub struct OfflineArgs { - send_from: super::online_mode::select_server::server::SendFrom, + #[interactive_clap(named_arg)] + account: super::super::sender::Sender, } -impl CliOfflineArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() +struct OfflineArgsContext {} + +impl OfflineArgsContext { + fn from_previous_context( + _previous_context: (), + _scope: &::InteractiveClapContextScope, + ) -> Self { + Self {} } } -impl From for CliOfflineArgs { - fn from(offline_args: OfflineArgs) -> Self { +impl From for super::TransferCommandNetworkContext { + fn from(_item: OfflineArgsContext) -> Self { Self { - send_from: Some(offline_args.send_from.into()), + connection_config: None, } } } -impl OfflineArgs { - pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { - let send_from = match item.send_from { - Some(cli_send_from) => { - super::online_mode::select_server::server::SendFrom::from(cli_send_from, None)? - } - None => super::online_mode::select_server::server::SendFrom::choose_send_from(None)?, - }; - Ok(Self { send_from }) - } -} - impl OfflineArgs { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { let selected_server_url = None; - self.send_from + self.account .process(prepopulated_unsigned_transaction, selected_server_url) .await } diff --git a/src/commands/transfer_command/operation_mode/online_mode/mod.rs b/src/commands/transfer_command/operation_mode/online_mode/mod.rs index 7c0e183ee..30f2f64d4 100644 --- a/src/commands/transfer_command/operation_mode/online_mode/mod.rs +++ b/src/commands/transfer_command/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl NetworkArgs { - pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server)? - } - None => self::select_server::SelectServer::choose_server()?, - }; - Ok(Self { selected_server }) - } -} - impl NetworkArgs { pub async fn process( self, diff --git a/src/commands/transfer_command/operation_mode/online_mode/select_server/mod.rs b/src/commands/transfer_command/operation_mode/online_mode/select_server/mod.rs index 838909edd..29fceb31e 100644 --- a/src/commands/transfer_command/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/transfer_command/operation_mode/online_mode/select_server/mod.rs @@ -1,129 +1,87 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl SelectServer { - pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { - match item { - CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( - cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, - )), - CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( - cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, - )), - CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( - cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, - )), - CliSelectServer::Custom(cli_custom_server) => { - Ok(Self::Custom(cli_custom_server.into_server()?)) +impl From for super::super::TransferCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") } + }; + Self { + connection_config: Some(connection_config), } } } impl SelectServer { - pub fn choose_server() -> color_eyre::eyre::Result { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), - }; - Ok(Self::from(cli_select_server)?) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, ) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Mainnet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } SelectServer::Betanet(server) => { - server.process(prepopulated_unsigned_transaction).await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server + .process(prepopulated_unsigned_transaction, connection_config) + .await?; } - SelectServer::Custom(server) => { - server.process(prepopulated_unsigned_transaction).await?; + SelectServer::Custom(custom_server) => { + custom_server + .process(prepopulated_unsigned_transaction) + .await?; } }) } diff --git a/src/commands/transfer_command/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/transfer_command/operation_mode/online_mode/select_server/server/mod.rs index c3a297881..5f25eb3a1 100644 --- a/src/commands/transfer_command/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/transfer_command/operation_mode/online_mode/select_server/server/mod.rs @@ -1,201 +1,75 @@ -use std::str::FromStr; - use dialoguer::Input; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_from: Option, -} - -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_from: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a sender + pub sender: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: Option, - pub send_from: SendFrom, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::super::super::TransferCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a sender + pub sender: super::super::super::super::sender::Sender, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.unwrap().rpc_url().as_str(), - ) - .unwrap(), - ), - send_from: Some(server.send_from.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::super::super::TransferCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_from: Some(server.send_from.into()), + connection_config: Some(crate::common::ConnectionConfig::from_custom_url(&item.url)), } } } -impl CliServer { - pub fn into_server( - self, - connection_config: crate::common::ConnectionConfig, - ) -> color_eyre::eyre::Result { - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, Some(connection_config.clone()))?, - None => SendFrom::choose_send_from(Some(connection_config.clone()))?, - }; - Ok(Server { - connection_config: Some(connection_config), - send_from, - }) - } -} - -impl CliCustomServer { - pub fn into_server(self) -> color_eyre::eyre::Result { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let connection_config = Some(crate::common::ConnectionConfig::Custom { url: url.inner }); - let send_from = match self.send_from { - Some(cli_send_from) => SendFrom::from(cli_send_from, connection_config.clone())?, - None => SendFrom::choose_send_from(connection_config.clone())?, - }; - Ok(Server { - connection_config, - send_from, - }) - } -} - impl Server { pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, + connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from - .process(prepopulated_unsigned_transaction, self.connection_config) + self.sender + .process(prepopulated_unsigned_transaction, Some(connection_config)) .await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a sender - Sender(crate::commands::transfer_command::sender::CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Sender(crate::commands::transfer_command::sender::Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Sender(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("sender".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Sender(sender) => Self::Sender(sender.into()), - } - } -} - -impl SendFrom { - pub fn from( - item: CliSendFrom, - connection_config: Option, - ) -> color_eyre::eyre::Result { - match item { - CliSendFrom::Sender(cli_sender) => Ok(Self::Sender( - crate::commands::transfer_command::sender::Sender::from( - cli_sender, - connection_config, - )?, - )), - } - } -} - -impl SendFrom { - pub fn choose_send_from( - connection_config: Option, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::Sender(Default::default()), - connection_config, - )?) +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - connection_config: Option, ) -> crate::CliResult { - match self { - SendFrom::Sender(sender) => { - sender - .process(prepopulated_unsigned_transaction, connection_config) - .await - } - } + let connection_config = Some(crate::common::ConnectionConfig::from_custom_url(&self.url)); + self.sender + .process(prepopulated_unsigned_transaction, connection_config) + .await } } diff --git a/src/commands/transfer_command/receiver/mod.rs b/src/commands/transfer_command/receiver/mod.rs index a174873f2..b7f8df21e 100644 --- a/src/commands/transfer_command/receiver/mod.rs +++ b/src/commands/transfer_command/receiver/mod.rs @@ -1,179 +1,48 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify a receiver - Receiver(CliReceiver), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Receiver(Receiver), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Receiver(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("receiver".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Receiver(receiver) => Self::Receiver(CliReceiver::from(receiver)), - } - } -} - -impl SendTo { - pub fn from( - item: CliSendTo, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliSendTo::Receiver(cli_receiver) => { - let receiver = Receiver::from(cli_receiver, connection_config, sender_account_id)?; - Ok(Self::Receiver(receiver)) - } - } - } -} - -impl SendTo { - pub fn send_to( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendTo::Receiver(Default::default()), - connection_config, - sender_account_id, - )?) - } - - pub async fn process( - self, - prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, - network_connection_config: Option, - ) -> crate::CliResult { - match self { - SendTo::Receiver(receiver) => { - receiver - .process(prepopulated_unsigned_transaction, network_connection_config) - .await - } - } - } -} - -/// данные о получателе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliReceiver { - receiver_account_id: Option, - #[clap(subcommand)] - transfer: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct Receiver { - pub receiver_account_id: near_primitives::types::AccountId, + #[interactive_clap(skip_default_from_cli)] + pub receiver_account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] pub transfer: super::transfer_near_tokens_type::Transfer, } -impl CliReceiver { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .transfer - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(receiver_account_id) = &self.receiver_account_id { - args.push_front(receiver_account_id.to_string()); - } - args - } -} - -impl From for CliReceiver { - fn from(receiver: Receiver) -> Self { - Self { - receiver_account_id: Some(receiver.receiver_account_id), - transfer: Some(super::transfer_near_tokens_type::CliTransfer::from( - receiver.transfer, - )), - } - } -} - impl Receiver { - fn from( - item: CliReceiver, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - let receiver_account_id: near_primitives::types::AccountId = match item.receiver_account_id - { - Some(cli_receiver_account_id) => match &connection_config { + fn from_cli_receiver_account_id( + optional_cli_receiver_account_id: Option, + context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + match optional_cli_receiver_account_id { + Some(cli_receiver_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_receiver_account_id.clone(), + &network_connection_config, + cli_receiver_account_id.clone().into(), )? { - Some(_) => cli_receiver_account_id, + Some(_) => Ok(cli_receiver_account_id), None => { - if !crate::common::is_64_len_hex(&cli_receiver_account_id) { - println!("Account <{}> doesn't exist", cli_receiver_account_id); - Receiver::input_receiver_account_id(connection_config.clone())? - } else { - cli_receiver_account_id - } + println!("Account <{}> doesn't exist", cli_receiver_account_id); + Self::input_receiver_account_id(&context) } }, - None => cli_receiver_account_id, + None => Ok(cli_receiver_account_id), }, - None => Receiver::input_receiver_account_id(connection_config.clone())?, - }; - let transfer: super::transfer_near_tokens_type::Transfer = match item.transfer { - Some(cli_transfer) => super::transfer_near_tokens_type::Transfer::from( - cli_transfer, - connection_config, - sender_account_id, - )?, - None => super::transfer_near_tokens_type::Transfer::choose_transfer_near( - connection_config, - sender_account_id, - )?, - }; - Ok(Self { - receiver_account_id, - transfer, - }) + None => Self::input_receiver_account_id(&context), + } } -} -impl Receiver { - fn input_receiver_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + pub fn input_receiver_account_id( + context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What is the account ID of the receiver?") - .interact_text() - .unwrap(); + .interact_text()?; if let Some(connection_config) = &connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -195,7 +64,7 @@ impl Receiver { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - receiver_id: self.receiver_account_id.clone(), + receiver_id: self.receiver_account_id.clone().into(), ..prepopulated_unsigned_transaction }; self.transfer diff --git a/src/commands/transfer_command/sender/mod.rs b/src/commands/transfer_command/sender/mod.rs index 07a48905a..e90754aed 100644 --- a/src/commands/transfer_command/sender/mod.rs +++ b/src/commands/transfer_command/sender/mod.rs @@ -1,95 +1,76 @@ use dialoguer::Input; -/// данные об отправителе транзакции -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::TransferCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] +pub struct Sender { + #[interactive_clap(skip_default_from_cli)] + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + ///Specify a receiver + pub receiver: super::receiver::Receiver, } -#[derive(Debug, Clone)] -pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, - pub send_to: super::receiver::SendTo, +struct SenderContext { + connection_config: Option, + sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); +impl SenderContext { + pub fn from_previous_context( + previous_context: super::operation_mode::TransferCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + connection_config: previous_context.connection_config.clone(), + sender_account_id: scope.sender_account_id.clone(), } - args } } -impl From for CliSender { - fn from(sender: Sender) -> Self { +impl From for crate::common::SignerContext { + fn from(item: SenderContext) -> Self { Self { - sender_account_id: Some(sender.sender_account_id), - send_to: Some(super::receiver::CliSendTo::from(sender.send_to)), + connection_config: item.connection_config, + signer_account_id: item.sender_account_id, } } } impl Sender { - pub fn from( - item: CliSender, - connection_config: Option, - ) -> color_eyre::eyre::Result { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => match &connection_config { + fn from_cli_sender_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::TransferCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match &context.connection_config { Some(network_connection_config) => match crate::common::get_account_state( - network_connection_config, - cli_sender_account_id.clone(), + &network_connection_config, + cli_sender_account_id.clone().into(), )? { - Some(_) => cli_sender_account_id, + Some(_) => Ok(cli_sender_account_id), None => { println!("Account <{}> doesn't exist", cli_sender_account_id); - Sender::input_sender_account_id(connection_config.clone())? + Sender::input_sender_account_id(&context) } }, - None => cli_sender_account_id, + None => Ok(cli_sender_account_id), }, - None => Sender::input_sender_account_id(connection_config.clone())?, - }; - let send_to: super::receiver::SendTo = match item.send_to { - Some(cli_send_to) => super::receiver::SendTo::from( - cli_send_to, - connection_config, - sender_account_id.clone(), - )?, - None => super::receiver::SendTo::send_to(connection_config, sender_account_id.clone())?, - }; - Ok(Self { - sender_account_id, - send_to, - }) + None => Self::input_sender_account_id(&context), + } } -} -impl Sender { fn input_sender_account_id( - connection_config: Option, - ) -> color_eyre::eyre::Result { + context: &super::operation_mode::TransferCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); loop { - let account_id: near_primitives::types::AccountId = Input::new() + let account_id: crate::types::account_id::AccountId = Input::new() .with_prompt("What is the account ID of the sender?") - .interact_text() - .unwrap(); + .interact_text()?; if let Some(connection_config) = &connection_config { if let Some(_) = - crate::common::get_account_state(connection_config, account_id.clone())? + crate::common::get_account_state(&connection_config, account_id.clone().into())? { break Ok(account_id); } else { @@ -107,10 +88,10 @@ impl Sender { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - signer_id: self.sender_account_id.clone(), + signer_id: self.sender_account_id.clone().into(), ..prepopulated_unsigned_transaction }; - self.send_to + self.receiver .process(unsigned_transaction, network_connection_config) .await } diff --git a/src/commands/transfer_command/transfer_near_tokens_type/mod.rs b/src/commands/transfer_command/transfer_near_tokens_type/mod.rs index abade5dac..70662eed9 100644 --- a/src/commands/transfer_command/transfer_near_tokens_type/mod.rs +++ b/src/commands/transfer_command/transfer_near_tokens_type/mod.rs @@ -1,68 +1,15 @@ use dialoguer::{console::Term, theme::ColorfulTheme, Input, Select}; +use strum::EnumDiscriminants; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliTransfer { - /// Enter an amount - Amount(CliTransferNEARTokensAction), -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] +#[interactive_clap(disable_strum_discriminants)] pub enum Transfer { + /// Enter an amount to transfer Amount(TransferNEARTokensAction), } -impl CliTransfer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Amount(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("amount".to_owned()); - args - } - } - } -} - -impl From for CliTransfer { - fn from(transfer: Transfer) -> Self { - match transfer { - Transfer::Amount(transfer_near_token_action) => { - Self::Amount(transfer_near_token_action.into()) - } - } - } -} - impl Transfer { - pub fn from( - item: CliTransfer, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - match item { - CliTransfer::Amount(cli_transfer_near_action) => { - Ok(Self::Amount(TransferNEARTokensAction::from( - cli_transfer_near_action, - connection_config, - sender_account_id, - )?)) - } - } - } -} - -impl Transfer { - pub fn choose_transfer_near( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, - ) -> color_eyre::eyre::Result { - Self::from( - CliTransfer::Amount(Default::default()), - connection_config, - sender_account_id, - ) - } - pub async fn process( self, prepopulated_unsigned_transaction: near_primitives::transaction::Transaction, @@ -100,6 +47,10 @@ pub struct TransferNEARTokensAction { crate::commands::construct_transaction_command::sign_transaction::SignTransaction, } +impl interactive_clap::ToCli for TransferNEARTokensAction { + type CliVariant = CliTransferNEARTokensAction; +} + impl CliTransferNEARTokensAction { pub fn to_cli_args(&self) -> std::collections::VecDeque { let mut args = self @@ -124,22 +75,28 @@ impl From for CliTransferNEARTokensAction { } impl TransferNEARTokensAction { - fn from( - item: CliTransferNEARTokensAction, - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + pub fn from_cli( + optional_clap_variant: Option< + ::CliVariant, + >, + context: crate::common::SignerContext, ) -> color_eyre::eyre::Result { - let amount: crate::common::TransferAmount = match item.amount { + let amount: crate::common::TransferAmount = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.amount) + { Some(cli_amount) => crate::common::TransferAmount::from_unchecked(cli_amount), - None => TransferNEARTokensAction::input_amount( - connection_config.clone(), - sender_account_id.clone(), - )?, - }; - let sign_option = match item.sign_option { - Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config.clone(), sender_account_id.clone())?, - None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_sign_option(connection_config, sender_account_id)?, + None => TransferNEARTokensAction::input_amount(&context)?, }; + + let sign_option = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.sign_option) + { + Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from_cli(Some(cli_sign_transaction), context)?, + None => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::choose_variant(context)?, + }; + Ok(Self { amount, sign_option, @@ -149,21 +106,22 @@ impl TransferNEARTokensAction { impl TransferNEARTokensAction { fn input_amount( - connection_config: Option, - sender_account_id: near_primitives::types::AccountId, + context: &crate::common::SignerContext, ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let sender_account_id = context.signer_account_id.clone(); match connection_config { Some(connection_config) => { let account_transfer_allowance = crate::common::get_account_transfer_allowance( &connection_config, - sender_account_id, + sender_account_id.into(), )?; println! {"{}", &account_transfer_allowance}; loop { let input_amount: crate::common::NearBalance = Input::new() .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") .interact_text() - .unwrap(); + ?; if let Ok(transfer_amount) = crate::common::TransferAmount::from( input_amount.clone(), &account_transfer_allowance, @@ -182,8 +140,7 @@ impl TransferNEARTokensAction { .with_prompt("Do you want to keep this amount for the transfer?") .items(&choose_input) .default(0) - .interact_on_opt(&Term::stderr()) - .unwrap(); + .interact_on_opt(&Term::stderr())?; match select_choose_input { Some(0) => { break Ok(crate::common::TransferAmount::from_unchecked( @@ -200,7 +157,7 @@ impl TransferNEARTokensAction { let input_amount: crate::common::NearBalance = Input::new() .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") .interact_text() - .unwrap(); + ?; Ok(crate::common::TransferAmount::from_unchecked(input_amount)) } } diff --git a/src/commands/utils_command/mod.rs b/src/commands/utils_command/mod.rs index 798c73ed1..7181e6cee 100644 --- a/src/commands/utils_command/mod.rs +++ b/src/commands/utils_command/mod.rs @@ -11,50 +11,13 @@ mod sign_transaction_subcommand_with_secret_key; mod sign_transaction_with_ledger_subcommand; mod view_serialized_transaction; -/// набор утилит-помощников -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliUtils { - #[clap(subcommand)] - util: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct Utils { + #[interactive_clap(subcommand)] pub util: Util, } -impl CliUtils { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.util - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliUtils { - fn from(utils: Utils) -> Self { - Self { - util: Some(utils.util.into()), - } - } -} - -impl From for Utils { - fn from(item: CliUtils) -> Self { - let util = match item.util { - Some(cli_util) => Util::from(cli_util), - None => Util::choose_util(), - }; - Self { util } - } -} - impl Utils { pub async fn process(self) -> crate::CliResult { self.util.process().await @@ -62,7 +25,7 @@ impl Utils { } #[derive(Debug, Clone, clap::Clap)] -enum CliUtil { +pub enum CliUtil { /// It generates a random key pair GenerateKeypair(self::generate_keypair_subcommand::CliGenerateKeypair), /// Предоставьте данные для подписания данных с помощью private key @@ -114,6 +77,10 @@ pub enum Util { SendSignedTransaction(self::send_signed_transaction::operation_mode::OperationMode), } +impl interactive_clap::ToCli for Util { + type CliVariant = CliUtil; +} + impl CliUtil { pub fn to_cli_args(&self) -> std::collections::VecDeque { match self { @@ -184,46 +151,54 @@ impl From for CliUtil { } } -impl From for Util { - fn from(item: CliUtil) -> Self { - match item { - CliUtil::GenerateKeypair(generate_keypair) => Util::GenerateKeypair(generate_keypair), - CliUtil::SignTransactionPrivateKey(cli_sign_transaction) => { +impl Util { + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: (), + ) -> color_eyre::eyre::Result { + match optional_clap_variant { + Some(CliUtil::GenerateKeypair(generate_keypair)) => { + Ok(Util::GenerateKeypair(generate_keypair)) + } + Some(CliUtil::SignTransactionPrivateKey(cli_sign_transaction)) => { let sign_transaction = self::sign_transaction_subcommand_with_secret_key::SignTransactionPrivateKey::from(cli_sign_transaction); - Util::SignTransactionPrivateKey(sign_transaction) + Ok(Util::SignTransactionPrivateKey(sign_transaction)) } #[cfg(feature = "ledger")] - CliUtil::SignTransactionWithLedger(cli_sign_transaction_with_ledger) => { + Some(CliUtil::SignTransactionWithLedger(cli_sign_transaction_with_ledger)) => { let sign_transaction = self::sign_transaction_with_ledger_subcommand::SignTransactionWithLedger::from( cli_sign_transaction_with_ledger, ); - Util::SignTransactionWithLedger(sign_transaction) + Ok(Util::SignTransactionWithLedger(sign_transaction)) } - CliUtil::CombineTransactionSignature(cli_combine_transaction) => { + Some(CliUtil::CombineTransactionSignature(cli_combine_transaction)) => { let combine_transaction = self::combine_transaction_subcommand_with_signature::CombineTransactionSignature::from(cli_combine_transaction); - Util::CombineTransactionSignature(combine_transaction) + Ok(Util::CombineTransactionSignature(combine_transaction)) } - CliUtil::ViewSerializedTransaction(cli_view_serialized_transaction) => { + Some(CliUtil::ViewSerializedTransaction(cli_view_serialized_transaction)) => { let view_serialized_transaction = self::view_serialized_transaction::ViewSerializedTransaction::from( cli_view_serialized_transaction, ); - Util::ViewSerializedTransaction(view_serialized_transaction) + Ok(Util::ViewSerializedTransaction(view_serialized_transaction)) } #[cfg(feature = "ledger")] - CliUtil::LedgerPublicKey(ledger_publickey) => Util::LedgerPublicKey(ledger_publickey), - CliUtil::SendSignedTransaction(cli_operation_mode) => { - Util::SendSignedTransaction(cli_operation_mode.into()) + Some(CliUtil::LedgerPublicKey(ledger_publickey)) => { + Ok(Util::LedgerPublicKey(ledger_publickey)) + } + Some(CliUtil::SendSignedTransaction(cli_operation_mode)) => { + Ok(Util::SendSignedTransaction(cli_operation_mode.into())) } + None => Self::choose_variant(context), } } } impl Util { - fn choose_util() -> Self { + fn choose_variant(context: ()) -> color_eyre::eyre::Result { println!(); let variants = UtilDiscriminants::iter().collect::>(); let utils = variants @@ -261,7 +236,7 @@ impl Util { CliUtil::SendSignedTransaction(Default::default()) } }; - Self::from(cli_util) + Ok(Self::from_cli(Some(cli_util), context)?) } pub async fn process(self) -> crate::CliResult { diff --git a/src/commands/view_command/mod.rs b/src/commands/view_command/mod.rs index 39ac57ef9..e45b83705 100644 --- a/src/commands/view_command/mod.rs +++ b/src/commands/view_command/mod.rs @@ -1,5 +1,4 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod view_account; mod view_contract_code; @@ -8,207 +7,45 @@ mod view_nonce; mod view_recent_block_hash; mod view_transaction_status; -/// инструмент выбора to view -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliViewQueryRequest { - #[clap(subcommand)] - query: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct ViewQueryRequest { + #[interactive_clap(subcommand)] pub query: QueryRequest, } -impl CliViewQueryRequest { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let args = self - .query - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args - } -} - -impl From for CliViewQueryRequest { - fn from(view_query_request: ViewQueryRequest) -> Self { - Self { - query: Some(view_query_request.query.into()), - } - } -} - -impl From for ViewQueryRequest { - fn from(item: CliViewQueryRequest) -> Self { - let query = match item.query { - Some(cli_query_request) => QueryRequest::from(cli_query_request), - None => QueryRequest::choose_query_request(), - }; - ViewQueryRequest { query } - } -} - impl ViewQueryRequest { pub async fn process(self) -> crate::CliResult { self.query.process().await } } -#[derive(Debug, Clone, clap::Clap)] -pub enum CliQueryRequest { - /// View properties for an account - AccountSummary(self::view_account::operation_mode::CliOperationMode), - /// View a contract code - ContractCode(self::view_contract_code::operation_mode::CliOperationMode), - /// View a contract state - ContractState(self::view_contract_state::operation_mode::CliOperationMode), - /// View a transaction status - Transaction(self::view_transaction_status::operation_mode::CliOperationMode), - /// View a nonce for a public key - Nonce(self::view_nonce::operation_mode::CliOperationMode), - /// View recent block hash for this network - RecentBlockHash(self::view_recent_block_hash::operation_mode::CliOperationMode), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = ())] +///Сhoose what you want to view pub enum QueryRequest { #[strum_discriminants(strum(message = "View properties for an account"))] + /// View properties for an account AccountSummary(self::view_account::operation_mode::OperationMode), #[strum_discriminants(strum(message = "View a contract code"))] + /// View a contract code ContractCode(self::view_contract_code::operation_mode::OperationMode), #[strum_discriminants(strum(message = "View a contract state"))] + /// View a contract state ContractState(self::view_contract_state::operation_mode::OperationMode), #[strum_discriminants(strum(message = "View a transaction status"))] + /// View a transaction status Transaction(self::view_transaction_status::operation_mode::OperationMode), #[strum_discriminants(strum(message = "View a nonce for a public key"))] + /// View a nonce for a public key Nonce(self::view_nonce::operation_mode::OperationMode), #[strum_discriminants(strum(message = "View recent block hash for this network"))] + /// View recent block hash for this network RecentBlockHash(self::view_recent_block_hash::operation_mode::OperationMode), } -impl CliQueryRequest { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::AccountSummary(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account-summary".to_owned()); - args - } - Self::ContractCode(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("contract-code".to_owned()); - args - } - Self::ContractState(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("contract-state".to_owned()); - args - } - Self::Transaction(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("transaction".to_owned()); - args - } - Self::Nonce(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("nonce".to_owned()); - args - } - Self::RecentBlockHash(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("recent-block-hash".to_owned()); - args - } - } - } -} - -impl From for CliQueryRequest { - fn from(query_request: QueryRequest) -> Self { - match query_request { - QueryRequest::AccountSummary(operation_mode) => { - Self::AccountSummary(operation_mode.into()) - } - QueryRequest::ContractCode(operation_mode) => Self::ContractCode(operation_mode.into()), - QueryRequest::ContractState(operation_mode) => { - Self::ContractState(operation_mode.into()) - } - QueryRequest::Transaction(operation_mode) => Self::Transaction(operation_mode.into()), - QueryRequest::Nonce(operation_mode) => Self::Nonce(operation_mode.into()), - QueryRequest::RecentBlockHash(operation_mode) => { - Self::RecentBlockHash(operation_mode.into()) - } - } - } -} - -impl From for QueryRequest { - fn from(item: CliQueryRequest) -> Self { - match item { - CliQueryRequest::AccountSummary(cli_operation_mode) => { - QueryRequest::AccountSummary(cli_operation_mode.into()) - } - CliQueryRequest::ContractCode(cli_operation_mode) => { - QueryRequest::ContractCode(cli_operation_mode.into()) - } - CliQueryRequest::ContractState(cli_operation_mode) => { - QueryRequest::ContractState(cli_operation_mode.into()) - } - CliQueryRequest::Transaction(cli_operation_mode) => { - QueryRequest::Transaction(cli_operation_mode.into()) - } - CliQueryRequest::Nonce(cli_operation_mode) => { - QueryRequest::Nonce(cli_operation_mode.into()) - } - CliQueryRequest::RecentBlockHash(cli_operation_mode) => { - QueryRequest::RecentBlockHash(cli_operation_mode.into()) - } - } - } -} - impl QueryRequest { - fn choose_query_request() -> Self { - println!(); - let variants = QueryRequestDiscriminants::iter().collect::>(); - let requests = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_request = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Сhoose what you want to view") - .items(&requests) - .default(0) - .interact() - .unwrap(); - let cli_request = match variants[selected_request] { - QueryRequestDiscriminants::AccountSummary => { - CliQueryRequest::AccountSummary(Default::default()) - } - QueryRequestDiscriminants::ContractCode => { - CliQueryRequest::ContractCode(Default::default()) - } - QueryRequestDiscriminants::ContractState => { - CliQueryRequest::ContractState(Default::default()) - } - QueryRequestDiscriminants::Transaction => { - CliQueryRequest::Transaction(Default::default()) - } - QueryRequestDiscriminants::Nonce => CliQueryRequest::Nonce(Default::default()), - QueryRequestDiscriminants::RecentBlockHash => { - CliQueryRequest::RecentBlockHash(Default::default()) - } - }; - Self::from(cli_request) - } - pub async fn process(self) -> crate::CliResult { match self { QueryRequest::AccountSummary(operation_mode) => operation_mode.process().await, diff --git a/src/commands/view_command/view_account/block_id/block_id_hash/mod.rs b/src/commands/view_command/view_account/block_id/block_id_hash/mod.rs index b8cfdd8fd..11a4af474 100644 --- a/src/commands/view_command/view_account/block_id/block_id_hash/mod.rs +++ b/src/commands/view_command/view_account/block_id/block_id_hash/mod.rs @@ -4,55 +4,29 @@ use crate::common::{display_access_key_list, display_account_info, ConnectionCon use near_primitives::types::{AccountId, BlockId, BlockReference}; -/// Specify the block_id hash for this account to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHash { - block_id_hash: Option, -} - -#[derive(Debug, Clone)] +// /// Specify the block_id hash for this account to view +// #[derive(Debug, Default, Clone, clap::Clap)] +// pub struct CliBlockIdHash { +// block_id_hash: Option, +// } + +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::super::operation_mode::online_mode::select_server::ViewAccountSummaryCommandNetworkContext)] pub struct BlockIdHash { - block_id_hash: near_primitives::hash::CryptoHash, -} - -impl CliBlockIdHash { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_hash) = &self.block_id_hash { - args.push_front(block_id_hash.to_string()); - } - args - } -} - -impl From for CliBlockIdHash { - fn from(block_id_hash: BlockIdHash) -> Self { - Self { - block_id_hash: Some(block_id_hash.block_id_hash), - } - } -} - -impl From for BlockIdHash { - fn from(item: CliBlockIdHash) -> Self { - let block_id_hash: near_primitives::hash::CryptoHash = match item.block_id_hash { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHash::input_block_id_hash(), - }; - Self { block_id_hash } - } + block_id_hash: crate::types::crypto_hash::CryptoHash, } impl BlockIdHash { - pub fn input_block_id_hash() -> near_primitives::hash::CryptoHash { - Input::new() + pub fn input_block_id_hash( + _context: &super::super::operation_mode::online_mode::select_server::ViewAccountSummaryCommandNetworkContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Type the block ID hash for this account") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process(self, account_id: AccountId, conf: ConnectionConfig) -> crate::CliResult { - let block_ref = BlockReference::BlockId(BlockId::Hash(self.block_id_hash.clone())); + let block_ref = BlockReference::BlockId(BlockId::Hash(self.block_id_hash.clone().into())); display_account_info(account_id.clone(), &conf, block_ref.clone()).await?; display_access_key_list(account_id, &conf, block_ref).await?; Ok(()) diff --git a/src/commands/view_command/view_account/block_id/block_id_height/mod.rs b/src/commands/view_command/view_account/block_id/block_id_height/mod.rs index ac7edb06e..8430e1479 100644 --- a/src/commands/view_command/view_account/block_id/block_id_height/mod.rs +++ b/src/commands/view_command/view_account/block_id/block_id_height/mod.rs @@ -3,51 +3,19 @@ use dialoguer::Input; use crate::common::{display_access_key_list, display_account_info, ConnectionConfig}; use near_primitives::types::{AccountId, BlockId, BlockReference}; -/// Specify the block_id height for this account to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHeight { - block_id_height: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::super::operation_mode::online_mode::select_server::ViewAccountSummaryCommandNetworkContext)] pub struct BlockIdHeight { block_id_height: near_primitives::types::BlockHeight, } -impl CliBlockIdHeight { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_height) = &self.block_id_height { - args.push_front(block_id_height.to_string()); - } - args - } -} - -impl From for CliBlockIdHeight { - fn from(block_id_height: BlockIdHeight) -> Self { - Self { - block_id_height: Some(block_id_height.block_id_height), - } - } -} - -impl From for BlockIdHeight { - fn from(item: CliBlockIdHeight) -> Self { - let block_id_height: near_primitives::types::BlockHeight = match item.block_id_height { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHeight::input_block_id_height(), - }; - Self { block_id_height } - } -} - impl BlockIdHeight { - pub fn input_block_id_height() -> near_primitives::types::BlockHeight { - Input::new() + pub fn input_block_id_height( + _context: &super::super::operation_mode::online_mode::select_server::ViewAccountSummaryCommandNetworkContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Type the block ID height for this account") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process(self, account_id: AccountId, conf: ConnectionConfig) -> crate::CliResult { diff --git a/src/commands/view_command/view_account/block_id/mod.rs b/src/commands/view_command/view_account/block_id/mod.rs index 79c82154f..370d13e03 100644 --- a/src/commands/view_command/view_account/block_id/mod.rs +++ b/src/commands/view_command/view_account/block_id/mod.rs @@ -1,5 +1,4 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; use crate::common::{display_access_key_list, display_account_info, ConnectionConfig}; use near_primitives::types::{AccountId, Finality}; @@ -7,95 +6,23 @@ use near_primitives::types::{AccountId, Finality}; mod block_id_hash; mod block_id_height; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliBlockId { - /// Specify a block ID final to view this account - AtFinalBlock, - /// Specify a block ID height to view this account - AtBlockHeight(self::block_id_height::CliBlockIdHeight), - /// Specify a block ID hash to view this account - AtBlockHash(self::block_id_hash::CliBlockIdHash), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewAccountSummaryCommandNetworkContext)] +///Choose Block ID pub enum BlockId { #[strum_discriminants(strum(message = "View this account at final block"))] + /// Specify a block ID final to view this account AtFinalBlock, #[strum_discriminants(strum(message = "View this account at block heigt"))] + /// Specify a block ID height to view this account AtBlockHeight(self::block_id_height::BlockIdHeight), #[strum_discriminants(strum(message = "View this account at block hash"))] + /// Specify a block ID hash to view this account AtBlockHash(self::block_id_hash::BlockIdHash), } -impl CliBlockId { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::AtFinalBlock => { - let mut args = std::collections::VecDeque::new(); - args.push_front("at-final-block".to_owned()); - args - } - Self::AtBlockHeight(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-height".to_owned()); - args - } - Self::AtBlockHash(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-hash".to_owned()); - args - } - } - } -} - -impl From for CliBlockId { - fn from(block_id: BlockId) -> Self { - match block_id { - BlockId::AtFinalBlock => Self::AtFinalBlock, - BlockId::AtBlockHeight(block_id_height) => Self::AtBlockHeight(block_id_height.into()), - BlockId::AtBlockHash(block_id_hash) => Self::AtBlockHash(block_id_hash.into()), - } - } -} - -impl From for BlockId { - fn from(item: CliBlockId) -> Self { - match item { - CliBlockId::AtFinalBlock => Self::AtFinalBlock, - CliBlockId::AtBlockHeight(cli_block_id_height) => { - Self::AtBlockHeight(cli_block_id_height.into()) - } - CliBlockId::AtBlockHash(cli_block_id_hash) => { - Self::AtBlockHash(cli_block_id_hash.into()) - } - } - } -} - impl BlockId { - pub fn choose_block_id() -> Self { - println!(); - let variants = BlockIdDiscriminants::iter().collect::>(); - let blocks = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Choose your action") - .items(&blocks) - .default(0) - .interact() - .unwrap(); - let cli_block_id = match variants[selection] { - BlockIdDiscriminants::AtFinalBlock => CliBlockId::AtFinalBlock, - BlockIdDiscriminants::AtBlockHeight => CliBlockId::AtBlockHeight(Default::default()), - BlockIdDiscriminants::AtBlockHash => CliBlockId::AtBlockHash(Default::default()), - }; - Self::from(cli_block_id) - } - pub async fn process(self, account_id: AccountId, conf: ConnectionConfig) -> crate::CliResult { println!(); match self { @@ -107,5 +34,6 @@ impl BlockId { Ok(()) } } + // Ok(()) } } diff --git a/src/commands/view_command/view_account/operation_mode/mod.rs b/src/commands/view_command/view_account/operation_mode/mod.rs index abaa63c00..ca22b3604 100644 --- a/src/commands/view_command/view_account/operation_mode/mod.rs +++ b/src/commands/view_command/view_account/operation_mode/mod.rs @@ -1,108 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), - }; - Self { mode } - } + #[interactive_clap(named_arg)] + /// Prepare and, optionally, submit a new transaction with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl From for Mode { - fn from(item: CliMode) -> Self { - match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), - } - } -} - -impl Mode { - pub fn choose_mode() -> Self { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/view_command/view_account/operation_mode/online_mode/mod.rs b/src/commands/view_command/view_account/operation_mode/online_mode/mod.rs index 1f68d88a1..0dd6b223d 100644 --- a/src/commands/view_command/view_account/operation_mode/online_mode/mod.rs +++ b/src/commands/view_command/view_account/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) - } - None => self::select_server::SelectServer::choose_server(), - }; - Self { selected_server } - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/view_command/view_account/operation_mode/online_mode/select_server/mod.rs b/src/commands/view_command/view_account/operation_mode/online_mode/select_server/mod.rs index e3fb9dc3c..06af1382c 100644 --- a/src/commands/view_command/view_account/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/view_command/view_account/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { - match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } - CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) - } - } - } +#[derive(Clone)] +pub struct ViewAccountSummaryCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> Self { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for ViewAccountSummaryCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/view_command/view_account/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/view_command/view_account/operation_mode/online_mode/select_server/server/mod.rs index fc2e49a2d..f931c575f 100644 --- a/src/commands/view_command/view_account/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/view_command/view_account/operation_mode/online_mode/select_server/server/mod.rs @@ -1,119 +1,67 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify an account + pub account: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::ViewAccountSummaryCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify an account + pub account: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: crate::common::ConnectionConfig, - pub send_to: super::super::super::super::sender::SendTo, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.rpc_url().as_str(), - ) - .unwrap(), - ), - send_to: Some(server.send_to.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::ViewAccountSummaryCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_to: Some(server.send_to.into()), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::sender::SendTo::send_to(), - }; - Server { - connection_config, - send_to, - } +impl Server { + pub async fn process( + self, + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + self.account.process(connection_config).await } } -impl CliCustomServer { - pub fn into_server(self) -> Server { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::sender::SendTo::send_to(), - }; - Server { - connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, - send_to, - } +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl Server { pub async fn process(self) -> crate::CliResult { - self.send_to.process(self.connection_config).await + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + self.account.process(connection_config).await } } diff --git a/src/commands/view_command/view_account/sender/mod.rs b/src/commands/view_command/view_account/sender/mod.rs index 51c8c7a29..a327a7516 100644 --- a/src/commands/view_command/view_account/sender/mod.rs +++ b/src/commands/view_command/view_account/sender/mod.rs @@ -1,128 +1,21 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify an account - Account(CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Account(Sender), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Account(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Account(sender) => Self::Account(sender.into()), - } - } -} - -impl From for SendTo { - fn from(item: CliSendTo) -> Self { - match item { - CliSendTo::Account(cli_sender) => { - let sender = Sender::from(cli_sender); - Self::Account(sender) - } - } - } -} - -impl SendTo { - pub fn send_to() -> Self { - Self::from(CliSendTo::Account(Default::default())) - } - - pub async fn process( - self, - network_connection_config: crate::common::ConnectionConfig, - ) -> crate::CliResult { - match self { - SendTo::Account(sender) => sender.process(network_connection_config).await, - } - } -} - -/// Specify the account to be view -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - selected_block_id: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewAccountSummaryCommandNetworkContext)] pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, + pub account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] selected_block_id: super::block_id::BlockId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .selected_block_id - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); - }; - args - } -} - -impl From for CliSender { - fn from(sender: Sender) -> Self { - Self { - sender_account_id: Some(sender.sender_account_id), - selected_block_id: Some(sender.selected_block_id.into()), - } - } -} - -impl From for Sender { - fn from(item: CliSender) -> Self { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), - }; - let selected_block_id: super::block_id::BlockId = match item.selected_block_id { - Some(cli_block_id) => cli_block_id.into(), - None => super::block_id::BlockId::choose_block_id(), - }; - Self { - sender_account_id, - selected_block_id, - } - } -} - impl Sender { - pub fn input_sender_account_id() -> near_primitives::types::AccountId { + pub fn input_account_id( + _context: &super::operation_mode::online_mode::select_server::ViewAccountSummaryCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); - Input::new() + Ok(Input::new() .with_prompt("What Account ID do you need to view?") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process( @@ -130,7 +23,7 @@ impl Sender { network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { self.selected_block_id - .process(self.sender_account_id, network_connection_config) + .process(self.account_id.into(), network_connection_config) .await } } diff --git a/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs b/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs index 8e91b5545..7c9c3cd66 100644 --- a/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs +++ b/src/commands/view_command/view_contract_code/block_id/block_id_hash/mod.rs @@ -1,51 +1,19 @@ use dialoguer::Input; use std::io::Write; -/// Specify the block_id hash for this contract to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHash { - block_id_hash: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct BlockIdHash { - block_id_hash: near_primitives::hash::CryptoHash, -} - -impl CliBlockIdHash { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_hash) = &self.block_id_hash { - args.push_front(block_id_hash.to_string()); - } - args - } -} - -impl From for CliBlockIdHash { - fn from(block_id_hash: BlockIdHash) -> Self { - Self { - block_id_hash: Some(block_id_hash.block_id_hash), - } - } -} - -impl From for BlockIdHash { - fn from(item: CliBlockIdHash) -> Self { - let block_id_hash: near_primitives::hash::CryptoHash = match item.block_id_hash { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHash::input_block_id_hash(), - }; - Self { block_id_hash } - } + block_id_hash: crate::types::crypto_hash::CryptoHash, } impl BlockIdHash { - pub fn input_block_id_hash() -> near_primitives::hash::CryptoHash { - Input::new() - .with_prompt("Type the block ID hash for this contract") - .interact_text() - .unwrap() + pub fn input_block_id_hash( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("Type the block ID hash for this account") + .interact_text()?) } fn rpc_client(&self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { @@ -62,7 +30,7 @@ impl BlockIdHash { .rpc_client(network_connection_config.archival_rpc_url().as_str()) .query(near_jsonrpc_primitives::types::query::RpcQueryRequest { block_reference: near_primitives::types::BlockReference::BlockId( - near_primitives::types::BlockId::Hash(self.block_id_hash.clone()), + near_primitives::types::BlockId::Hash(self.block_id_hash.clone().into()), ), request: near_primitives::views::QueryRequest::ViewCode { account_id: contract_id, diff --git a/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs b/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs index a041ce7a9..5dc0eda76 100644 --- a/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs +++ b/src/commands/view_command/view_contract_code/block_id/block_id_height/mod.rs @@ -1,51 +1,19 @@ use dialoguer::Input; use std::io::Write; -/// Specify the block_id height for this contract to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHeight { - block_id_height: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct BlockIdHeight { block_id_height: near_primitives::types::BlockHeight, } -impl CliBlockIdHeight { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_height) = &self.block_id_height { - args.push_front(block_id_height.to_string()); - } - args - } -} - -impl From for CliBlockIdHeight { - fn from(block_id_height: BlockIdHeight) -> Self { - Self { - block_id_height: Some(block_id_height.block_id_height), - } - } -} - -impl From for BlockIdHeight { - fn from(item: CliBlockIdHeight) -> Self { - let block_id_height: near_primitives::types::BlockHeight = match item.block_id_height { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHeight::input_block_id_height(), - }; - Self { block_id_height } - } -} - impl BlockIdHeight { - pub fn input_block_id_height() -> near_primitives::types::BlockHeight { - Input::new() - .with_prompt("Type the block ID height for this contract") - .interact_text() - .unwrap() + pub fn input_block_id_height( + _context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("Type the block ID height for this account") + .interact_text()?) } fn rpc_client(&self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { diff --git a/src/commands/view_command/view_contract_code/block_id/mod.rs b/src/commands/view_command/view_contract_code/block_id/mod.rs index ef2ba3b89..4fdd3e8d0 100644 --- a/src/commands/view_command/view_contract_code/block_id/mod.rs +++ b/src/commands/view_command/view_contract_code/block_id/mod.rs @@ -1,99 +1,26 @@ -use dialoguer::{theme::ColorfulTheme, Select}; use std::io::Write; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod block_id_hash; mod block_id_height; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliBlockId { - /// Specify a block ID final to view this contract - AtFinalBlock, - /// Specify a block ID height to view this contract - AtBlockHeight(self::block_id_height::CliBlockIdHeight), - /// Specify a block ID hash to view this contract - AtBlockHash(self::block_id_hash::CliBlockIdHash), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///Choose Block ID pub enum BlockId { #[strum_discriminants(strum(message = "View this contract at final block"))] + /// Specify a block ID final to view this contract AtFinalBlock, #[strum_discriminants(strum(message = "View this contract at block heigt"))] + /// Specify a block ID height to view this contract AtBlockHeight(self::block_id_height::BlockIdHeight), #[strum_discriminants(strum(message = "View this contract at block hash"))] + /// Specify a block ID hash to view this contract AtBlockHash(self::block_id_hash::BlockIdHash), } -impl CliBlockId { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::AtFinalBlock => { - let mut args = std::collections::VecDeque::new(); - args.push_front("at-final-block".to_owned()); - args - } - Self::AtBlockHeight(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-height".to_owned()); - args - } - Self::AtBlockHash(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-hash".to_owned()); - args - } - } - } -} - -impl From for CliBlockId { - fn from(block_id: BlockId) -> Self { - match block_id { - BlockId::AtFinalBlock => Self::AtFinalBlock, - BlockId::AtBlockHeight(block_id_height) => Self::AtBlockHeight(block_id_height.into()), - BlockId::AtBlockHash(block_id_hash) => Self::AtBlockHash(block_id_hash.into()), - } - } -} - -impl From for BlockId { - fn from(item: CliBlockId) -> Self { - match item { - CliBlockId::AtFinalBlock => Self::AtFinalBlock, - CliBlockId::AtBlockHeight(cli_block_id_height) => { - Self::AtBlockHeight(cli_block_id_height.into()) - } - CliBlockId::AtBlockHash(cli_block_id_hash) => { - Self::AtBlockHash(cli_block_id_hash.into()) - } - } - } -} - impl BlockId { - pub fn choose_block_id() -> Self { - println!(); - let variants = BlockIdDiscriminants::iter().collect::>(); - let blocks = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Choose your action") - .items(&blocks) - .default(0) - .interact() - .unwrap(); - let cli_block_id = match variants[selection] { - BlockIdDiscriminants::AtFinalBlock => CliBlockId::AtFinalBlock, - BlockIdDiscriminants::AtBlockHeight => CliBlockId::AtBlockHeight(Default::default()), - BlockIdDiscriminants::AtBlockHash => CliBlockId::AtBlockHash(Default::default()), - }; - Self::from(cli_block_id) - } - pub async fn process( self, contract_id: near_primitives::types::AccountId, diff --git a/src/commands/view_command/view_contract_code/contract/download_mode/download_contract/mod.rs b/src/commands/view_command/view_contract_code/contract/download_mode/download_contract/mod.rs index 580a6cae0..3b9354f27 100644 --- a/src/commands/view_command/view_contract_code/contract/download_mode/download_contract/mod.rs +++ b/src/commands/view_command/view_contract_code/contract/download_mode/download_contract/mod.rs @@ -1,74 +1,24 @@ use dialoguer::Input; -// download contract file -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliContractFile { - file_path: Option, - #[clap(subcommand)] - selected_block_id: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct ContractFile { - pub file_path: Option, + pub file_path: crate::types::path_buf::PathBuf, + #[interactive_clap(subcommand)] pub selected_block_id: super::super::super::block_id::BlockId, } -impl CliContractFile { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .selected_block_id - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(file_path) = &self.file_path { - args.push_front(file_path.as_path().display().to_string()); - }; - args - } -} - -impl From for CliContractFile { - fn from(contract_file: ContractFile) -> Self { - Self { - file_path: contract_file.file_path, - selected_block_id: Some(contract_file.selected_block_id.into()), - } - } -} - -impl ContractFile { - pub fn from(item: CliContractFile, contract_id: &str) -> Self { - let file_path = match item.file_path { - Some(cli_file_path) => Some(cli_file_path), - None => ContractFile::input_file_path(contract_id), - }; - let selected_block_id: super::super::super::block_id::BlockId = match item.selected_block_id - { - Some(cli_block_id) => cli_block_id.into(), - None => super::super::super::block_id::BlockId::choose_block_id(), - }; - ContractFile { - file_path, - selected_block_id, - } - } -} - impl ContractFile { - fn input_file_path(contract_id: &str) -> Option { + fn input_file_path( + context: &crate::common::SignerContext, + ) -> color_eyre::eyre::Result { println!(); + let contract_account_id = context.signer_account_id.clone(); let input_file_path: String = Input::new() .with_prompt("Where to download the contract file?") - .with_initial_text(format!("{}.wasm", contract_id)) - .interact_text() - .unwrap(); - Some(input_file_path.into()) + .with_initial_text(format!("{}.wasm", contract_account_id)) + .interact_text()?; + Ok(std::path::PathBuf::from(input_file_path).into()) } pub async fn process( @@ -77,7 +27,11 @@ impl ContractFile { network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { self.selected_block_id - .process(contract_id, network_connection_config, self.file_path) + .process( + contract_id, + network_connection_config, + Some(self.file_path.into()), + ) .await } } diff --git a/src/commands/view_command/view_contract_code/contract/download_mode/hash_contract/mod.rs b/src/commands/view_command/view_contract_code/contract/download_mode/hash_contract/mod.rs index 2bbda3da3..3ce199061 100644 --- a/src/commands/view_command/view_contract_code/contract/download_mode/hash_contract/mod.rs +++ b/src/commands/view_command/view_contract_code/contract/download_mode/hash_contract/mod.rs @@ -1,50 +1,10 @@ -// view a contract hash -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliContractHash { - #[clap(subcommand)] - selected_block_id: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = crate::common::SignerContext)] pub struct ContractHash { + #[interactive_clap(subcommand)] pub selected_block_id: super::super::super::block_id::BlockId, } -impl CliContractHash { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let args = self - .selected_block_id - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args - } -} - -impl From for CliContractHash { - fn from(contract_hash: ContractHash) -> Self { - Self { - selected_block_id: Some(contract_hash.selected_block_id.into()), - } - } -} - -impl ContractHash { - pub fn from(item: CliContractHash) -> Self { - let selected_block_id: super::super::super::block_id::BlockId = match item.selected_block_id - { - Some(cli_block_id) => cli_block_id.into(), - None => super::super::super::block_id::BlockId::choose_block_id(), - }; - ContractHash { selected_block_id } - } -} - impl ContractHash { pub async fn process( self, diff --git a/src/commands/view_command/view_contract_code/contract/download_mode/mod.rs b/src/commands/view_command/view_contract_code/contract/download_mode/mod.rs index 0a466172f..d37d8e59e 100644 --- a/src/commands/view_command/view_contract_code/contract/download_mode/mod.rs +++ b/src/commands/view_command/view_contract_code/contract/download_mode/mod.rs @@ -1,86 +1,22 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod download_contract; mod hash_contract; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliDownloadMode { - /// Download a contract file - Download(self::download_contract::CliContractFile), - /// View a contract hash - Hash(self::hash_contract::CliContractHash), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = crate::common::SignerContext)] +///To view contract code you will need to choose next action pub enum DownloadMode { #[strum_discriminants(strum(message = "Download a contract file"))] + /// Download a contract file Download(self::download_contract::ContractFile), #[strum_discriminants(strum(message = "View a contract hash"))] + /// View a contract hash Hash(self::hash_contract::ContractHash), } -impl CliDownloadMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Download(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("download".to_owned()); - args - } - Self::Hash(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("hash".to_owned()); - args - } - } - } -} - -impl From for CliDownloadMode { - fn from(download_mode: DownloadMode) -> Self { - match download_mode { - DownloadMode::Download(contract_file) => Self::Download(contract_file.into()), - DownloadMode::Hash(contract_hash) => Self::Hash(contract_hash.into()), - } - } -} - -impl DownloadMode { - pub fn from(item: CliDownloadMode, contract_id: &str) -> Self { - match item { - CliDownloadMode::Download(cli_contract_file) => DownloadMode::Download( - self::download_contract::ContractFile::from(cli_contract_file, contract_id), - ), - CliDownloadMode::Hash(cli_contract_hash) => { - DownloadMode::Hash(self::hash_contract::ContractHash::from(cli_contract_hash)) - } - } - } -} - impl DownloadMode { - pub fn choose_download_mode(contract_id: &str) -> Self { - println!(); - let variants = DownloadModeDiscriminants::iter().collect::>(); - let modes = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_mode = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("To view contract code you will need to choose next action") - .items(&modes) - .default(0) - .interact() - .unwrap(); - let cli_mode = match variants[selected_mode] { - DownloadModeDiscriminants::Download => CliDownloadMode::Download(Default::default()), - DownloadModeDiscriminants::Hash => CliDownloadMode::Hash(Default::default()), - }; - Self::from(cli_mode, contract_id) - } - pub async fn process( self, contract_id: near_primitives::types::AccountId, diff --git a/src/commands/view_command/view_contract_code/contract/mod.rs b/src/commands/view_command/view_contract_code/contract/mod.rs index 26c7b6914..67d9d3b2e 100644 --- a/src/commands/view_command/view_contract_code/contract/mod.rs +++ b/src/commands/view_command/view_contract_code/contract/mod.rs @@ -2,133 +2,78 @@ use dialoguer::Input; mod download_mode; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify a contract - Contract(CliContract), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Contract(Contract), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Contract(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("contract".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Contract(contract) => Self::Contract(contract.into()), - } - } -} - -impl From for SendTo { - fn from(item: CliSendTo) -> Self { - match item { - CliSendTo::Contract(cli_sender) => { - let sender = Contract::from(cli_sender); - Self::Contract(sender) - } - } - } -} - -impl SendTo { - pub fn send_to() -> Self { - Self::from(CliSendTo::Contract(Default::default())) - } - - pub async fn process( - self, - network_connection_config: crate::common::ConnectionConfig, - ) -> crate::CliResult { - match self { - SendTo::Contract(sender) => sender.process(network_connection_config).await, - } - } -} - -/// Specify a contract -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliContract { - pub contract_id: Option, - #[clap(subcommand)] - download_mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::operation_mode::online_mode::select_server::ViewContractCodeCommandNetworkContext)] +#[interactive_clap(output_context = crate::common::SignerContext)] pub struct Contract { - pub contract_id: near_primitives::types::AccountId, + #[interactive_clap(skip_default_from_cli)] + pub contract_account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] pub download_mode: self::download_mode::DownloadMode, } -impl CliContract { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .download_mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(contract_id) = &self.contract_id { - args.push_front(contract_id.to_string()); - }; - args - } +struct ContractContext { + connection_config: Option, + contract_account_id: crate::types::account_id::AccountId, } -impl From for CliContract { - fn from(contract: Contract) -> Self { +impl ContractContext { + pub fn from_previous_context( + previous_context: super::operation_mode::online_mode::select_server::ViewContractCodeCommandNetworkContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - contract_id: Some(contract.contract_id), - download_mode: Some(contract.download_mode.into()), + connection_config: Some(previous_context.connection_config.clone()), + contract_account_id: scope.contract_account_id.clone(), } } } -impl From for Contract { - fn from(item: CliContract) -> Self { - let contract_id: near_primitives::types::AccountId = match item.contract_id { - Some(cli_contract_id) => cli_contract_id, - None => Contract::input_contract_id(), - }; - let download_mode = match item.download_mode { - Some(cli_download_mode) => { - self::download_mode::DownloadMode::from(cli_download_mode, &contract_id.to_string()) - } - None => { - self::download_mode::DownloadMode::choose_download_mode(&contract_id.to_string()) - } - }; +impl From for crate::common::SignerContext { + fn from(item: ContractContext) -> Self { Self { - contract_id, - download_mode, + connection_config: item.connection_config, + signer_account_id: item.contract_account_id, } } } impl Contract { - pub fn input_contract_id() -> near_primitives::types::AccountId { - println!(); - Input::new() - .with_prompt("What contract do you need to view?") - .interact_text() - .unwrap() + fn from_cli_contract_account_id( + optional_cli_sender_account_id: Option, + context: &super::operation_mode::online_mode::select_server::ViewContractCodeCommandNetworkContext, + ) -> color_eyre::eyre::Result { + match optional_cli_sender_account_id { + Some(cli_sender_account_id) => match crate::common::get_account_state( + &context.connection_config, + cli_sender_account_id.clone().into(), + )? { + Some(_) => Ok(cli_sender_account_id), + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Self::input_contract_account_id(&context) + } + }, + None => Self::input_contract_account_id(&context), + } + } + + pub fn input_contract_account_id( + context: &super::operation_mode::online_mode::select_server::ViewContractCodeCommandNetworkContext, + ) -> color_eyre::eyre::Result { + loop { + let account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("What contract do you need to view?") + .interact_text()?; + if let Some(_) = crate::common::get_account_state( + &context.connection_config, + account_id.clone().into(), + )? { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id.to_string()); + }; + } } pub async fn process( @@ -136,7 +81,7 @@ impl Contract { network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { self.download_mode - .process(self.contract_id, network_connection_config) + .process(self.contract_account_id.into(), network_connection_config) .await } } diff --git a/src/commands/view_command/view_contract_code/operation_mode/mod.rs b/src/commands/view_command/view_contract_code/operation_mode/mod.rs index abaa63c00..ca22b3604 100644 --- a/src/commands/view_command/view_contract_code/operation_mode/mod.rs +++ b/src/commands/view_command/view_contract_code/operation_mode/mod.rs @@ -1,108 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), - }; - Self { mode } - } + #[interactive_clap(named_arg)] + /// Prepare and, optionally, submit a new transaction with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl From for Mode { - fn from(item: CliMode) -> Self { - match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), - } - } -} - -impl Mode { - pub fn choose_mode() -> Self { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/view_command/view_contract_code/operation_mode/online_mode/mod.rs b/src/commands/view_command/view_contract_code/operation_mode/online_mode/mod.rs index 1f68d88a1..0dd6b223d 100644 --- a/src/commands/view_command/view_contract_code/operation_mode/online_mode/mod.rs +++ b/src/commands/view_command/view_contract_code/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) - } - None => self::select_server::SelectServer::choose_server(), - }; - Self { selected_server } - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/mod.rs b/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/mod.rs index e3fb9dc3c..43de07d5d 100644 --- a/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { - match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } - CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) - } - } - } +#[derive(Clone)] +pub struct ViewContractCodeCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> Self { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for ViewContractCodeCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/server/mod.rs index ed20fed52..62f89b02e 100644 --- a/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/view_command/view_contract_code/operation_mode/online_mode/select_server/server/mod.rs @@ -1,119 +1,67 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a contract + pub contract: super::super::super::super::contract::Contract, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::ViewContractCodeCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a contract + pub contract: super::super::super::super::contract::Contract, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: crate::common::ConnectionConfig, - pub send_to: super::super::super::super::contract::SendTo, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.rpc_url().as_str(), - ) - .unwrap(), - ), - send_to: Some(server.send_to.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::ViewContractCodeCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_to: Some(server.send_to.into()), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::contract::SendTo::send_to(), - }; - Server { - connection_config, - send_to, - } +impl Server { + pub async fn process( + self, + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + self.contract.process(connection_config).await } } -impl CliCustomServer { - pub fn into_server(self) -> Server { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::contract::SendTo::send_to(), - }; - Server { - connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, - send_to, - } +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl Server { pub async fn process(self) -> crate::CliResult { - self.send_to.process(self.connection_config).await + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + self.contract.process(connection_config).await } } diff --git a/src/commands/view_command/view_contract_state/block_id/block_id_hash/mod.rs b/src/commands/view_command/view_contract_state/block_id/block_id_hash/mod.rs index 73f4f200a..1cb640071 100644 --- a/src/commands/view_command/view_contract_state/block_id/block_id_hash/mod.rs +++ b/src/commands/view_command/view_contract_state/block_id/block_id_hash/mod.rs @@ -1,50 +1,18 @@ use dialoguer::Input; -/// Specify the block_id hash for this contract to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHash { - block_id_hash: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext)] pub struct BlockIdHash { - block_id_hash: near_primitives::hash::CryptoHash, -} - -impl CliBlockIdHash { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_hash) = &self.block_id_hash { - args.push_front(block_id_hash.to_string()); - } - args - } -} - -impl From for CliBlockIdHash { - fn from(block_id_hash: BlockIdHash) -> Self { - Self { - block_id_hash: Some(block_id_hash.block_id_hash), - } - } -} - -impl From for BlockIdHash { - fn from(item: CliBlockIdHash) -> Self { - let block_id_hash: near_primitives::hash::CryptoHash = match item.block_id_hash { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHash::input_block_id_hash(), - }; - Self { block_id_hash } - } + block_id_hash: crate::types::crypto_hash::CryptoHash, } impl BlockIdHash { - pub fn input_block_id_hash() -> near_primitives::hash::CryptoHash { - Input::new() - .with_prompt("Type the block ID hash for this contract") - .interact_text() - .unwrap() + pub fn input_block_id_hash( + _context: &super::super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("Type the block ID hash for this account") + .interact_text()?) } fn rpc_client(&self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { @@ -60,7 +28,7 @@ impl BlockIdHash { .rpc_client(network_connection_config.archival_rpc_url().as_str()) .query(near_jsonrpc_primitives::types::query::RpcQueryRequest { block_reference: near_primitives::types::BlockReference::BlockId( - near_primitives::types::BlockId::Hash(self.block_id_hash.clone()), + near_primitives::types::BlockId::Hash(self.block_id_hash.clone().into()), ), request: near_primitives::views::QueryRequest::ViewState { account_id: sender_account_id, diff --git a/src/commands/view_command/view_contract_state/block_id/block_id_height/mod.rs b/src/commands/view_command/view_contract_state/block_id/block_id_height/mod.rs index ddd3a8482..14addd812 100644 --- a/src/commands/view_command/view_contract_state/block_id/block_id_height/mod.rs +++ b/src/commands/view_command/view_contract_state/block_id/block_id_height/mod.rs @@ -1,50 +1,18 @@ use dialoguer::Input; -/// Specify the block_id height for this contract to view -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliBlockIdHeight { - block_id_height: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext)] pub struct BlockIdHeight { block_id_height: near_primitives::types::BlockHeight, } -impl CliBlockIdHeight { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(block_id_height) = &self.block_id_height { - args.push_front(block_id_height.to_string()); - } - args - } -} - -impl From for CliBlockIdHeight { - fn from(block_id_height: BlockIdHeight) -> Self { - Self { - block_id_height: Some(block_id_height.block_id_height), - } - } -} - -impl From for BlockIdHeight { - fn from(item: CliBlockIdHeight) -> Self { - let block_id_height: near_primitives::types::BlockHeight = match item.block_id_height { - Some(cli_block_id_hash) => cli_block_id_hash, - None => BlockIdHeight::input_block_id_height(), - }; - Self { block_id_height } - } -} - impl BlockIdHeight { - pub fn input_block_id_height() -> near_primitives::types::BlockHeight { - Input::new() - .with_prompt("Type the block ID height for this contract") - .interact_text() - .unwrap() + pub fn input_block_id_height( + _context: &super::super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("Type the block ID height for this account") + .interact_text()?) } fn rpc_client(&self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { diff --git a/src/commands/view_command/view_contract_state/block_id/mod.rs b/src/commands/view_command/view_contract_state/block_id/mod.rs index 2e88e01b6..49d2c0da8 100644 --- a/src/commands/view_command/view_contract_state/block_id/mod.rs +++ b/src/commands/view_command/view_contract_state/block_id/mod.rs @@ -1,98 +1,25 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod block_id_hash; mod block_id_height; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliBlockId { - /// Specify a block ID final to view this contract - AtFinalBlock, - /// Specify a block ID height to view this contract - AtBlockHeight(self::block_id_height::CliBlockIdHeight), - /// Specify a block ID hash to view this contract - AtBlockHash(self::block_id_hash::CliBlockIdHash), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext)] +///Choose Block ID pub enum BlockId { - #[strum_discriminants(strum(message = "View state this contract at final block"))] + #[strum_discriminants(strum(message = "View this contract at final block"))] + /// Specify a block ID final to view this contract AtFinalBlock, - #[strum_discriminants(strum(message = "View state this contract at block heigt"))] + #[strum_discriminants(strum(message = "View this contract at block heigt"))] + /// Specify a block ID height to view this contract AtBlockHeight(self::block_id_height::BlockIdHeight), - #[strum_discriminants(strum(message = "View state this contract at block hash"))] + #[strum_discriminants(strum(message = "View this contract at block hash"))] + /// Specify a block ID hash to view this contract AtBlockHash(self::block_id_hash::BlockIdHash), } -impl CliBlockId { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::AtFinalBlock => { - let mut args = std::collections::VecDeque::new(); - args.push_front("at-final-block".to_owned()); - args - } - Self::AtBlockHeight(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-height".to_owned()); - args - } - Self::AtBlockHash(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("at-block-hash".to_owned()); - args - } - } - } -} - -impl From for CliBlockId { - fn from(block_id: BlockId) -> Self { - match block_id { - BlockId::AtFinalBlock => Self::AtFinalBlock, - BlockId::AtBlockHeight(block_id_height) => Self::AtBlockHeight(block_id_height.into()), - BlockId::AtBlockHash(block_id_hash) => Self::AtBlockHash(block_id_hash.into()), - } - } -} - -impl From for BlockId { - fn from(item: CliBlockId) -> Self { - match item { - CliBlockId::AtFinalBlock => Self::AtFinalBlock, - CliBlockId::AtBlockHeight(cli_block_id_height) => { - Self::AtBlockHeight(cli_block_id_height.into()) - } - CliBlockId::AtBlockHash(cli_block_id_hash) => { - Self::AtBlockHash(cli_block_id_hash.into()) - } - } - } -} - impl BlockId { - pub fn choose_block_id() -> Self { - println!(); - let variants = BlockIdDiscriminants::iter().collect::>(); - let blocks = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Choose your action") - .items(&blocks) - .default(0) - .interact() - .unwrap(); - let cli_block_id = match variants[selection] { - BlockIdDiscriminants::AtFinalBlock => CliBlockId::AtFinalBlock, - BlockIdDiscriminants::AtBlockHeight => CliBlockId::AtBlockHeight(Default::default()), - BlockIdDiscriminants::AtBlockHash => CliBlockId::AtBlockHash(Default::default()), - }; - Self::from(cli_block_id) - } - pub async fn process( self, sender_account_id: near_primitives::types::AccountId, diff --git a/src/commands/view_command/view_contract_state/operation_mode/mod.rs b/src/commands/view_command/view_contract_state/operation_mode/mod.rs index abaa63c00..ca22b3604 100644 --- a/src/commands/view_command/view_contract_state/operation_mode/mod.rs +++ b/src/commands/view_command/view_contract_state/operation_mode/mod.rs @@ -1,108 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), - }; - Self { mode } - } + #[interactive_clap(named_arg)] + /// Prepare and, optionally, submit a new transaction with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl From for Mode { - fn from(item: CliMode) -> Self { - match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), - } - } -} - -impl Mode { - pub fn choose_mode() -> Self { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/view_command/view_contract_state/operation_mode/online_mode/mod.rs b/src/commands/view_command/view_contract_state/operation_mode/online_mode/mod.rs index 1f68d88a1..0dd6b223d 100644 --- a/src/commands/view_command/view_contract_state/operation_mode/online_mode/mod.rs +++ b/src/commands/view_command/view_contract_state/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) - } - None => self::select_server::SelectServer::choose_server(), - }; - Self { selected_server } - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/mod.rs b/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/mod.rs index e3fb9dc3c..8d990415f 100644 --- a/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { - match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } - CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) - } - } - } +#[derive(Clone)] +pub struct ViewContractStateCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> Self { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for ViewContractStateCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/server/mod.rs index fc2e49a2d..b28e80131 100644 --- a/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/view_command/view_contract_state/operation_mode/online_mode/select_server/server/mod.rs @@ -1,119 +1,67 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a contract + pub account: super::super::super::super::sender::Sender, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::ViewContractCodeCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a contract + pub account: super::super::super::super::sender::Sender, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: crate::common::ConnectionConfig, - pub send_to: super::super::super::super::sender::SendTo, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.rpc_url().as_str(), - ) - .unwrap(), - ), - send_to: Some(server.send_to.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::ViewContractStateCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_to: Some(server.send_to.into()), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::sender::SendTo::send_to(), - }; - Server { - connection_config, - send_to, - } +impl Server { + pub async fn process( + self, + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + self.account.process(connection_config).await } } -impl CliCustomServer { - pub fn into_server(self) -> Server { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::sender::SendTo::send_to(), - }; - Server { - connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, - send_to, - } +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl Server { pub async fn process(self) -> crate::CliResult { - self.send_to.process(self.connection_config).await + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + self.account.process(connection_config).await } } diff --git a/src/commands/view_command/view_contract_state/sender/mod.rs b/src/commands/view_command/view_contract_state/sender/mod.rs index 25187b55e..a10c6900d 100644 --- a/src/commands/view_command/view_contract_state/sender/mod.rs +++ b/src/commands/view_command/view_contract_state/sender/mod.rs @@ -1,128 +1,66 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify an account - Account(CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Account(Sender), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Account(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Account(sender) => Self::Account(sender.into()), - } - } -} - -impl From for SendTo { - fn from(item: CliSendTo) -> Self { - match item { - CliSendTo::Account(cli_sender) => { - let sender = Sender::from(cli_sender); - Self::Account(sender) - } - } - } -} - -impl SendTo { - pub fn send_to() -> Self { - Self::from(CliSendTo::Account(Default::default())) - } - - pub async fn process( - self, - network_connection_config: crate::common::ConnectionConfig, - ) -> crate::CliResult { - match self { - SendTo::Account(sender) => sender.process(network_connection_config).await, - } - } -} - -/// Specify the account to be view -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliSender { - pub sender_account_id: Option, - #[clap(subcommand)] - selected_block_id: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext)] +#[interactive_clap(skip_default_from_cli)] pub struct Sender { - pub sender_account_id: near_primitives::types::AccountId, + pub sender_account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] selected_block_id: super::block_id::BlockId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .selected_block_id - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(sender_account_id) = &self.sender_account_id { - args.push_front(sender_account_id.to_string()); - }; - args - } -} - -impl From for CliSender { - fn from(sender: Sender) -> Self { - Self { - sender_account_id: Some(sender.sender_account_id), - selected_block_id: Some(sender.selected_block_id.into()), - } - } -} - -impl From for Sender { - fn from(item: CliSender) -> Self { - let sender_account_id: near_primitives::types::AccountId = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), +impl Sender { + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let sender_account_id = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.sender_account_id) + { + Some(sender_account_id) => match crate::common::get_account_state( + &connection_config, + sender_account_id.clone().into(), + )? { + Some(_) => sender_account_id, + None => { + println!("Contract <{}> doesn't exist", sender_account_id); + Self::input_sender_account_id(&context)? + } + }, + None => Self::input_sender_account_id(&context)?, }; - let selected_block_id: super::block_id::BlockId = match item.selected_block_id { - Some(cli_block_id) => cli_block_id.into(), - None => super::block_id::BlockId::choose_block_id(), + let selected_block_id: super::block_id::BlockId = match optional_clap_variant + .and_then(|clap_variant| clap_variant.selected_block_id) + { + Some(cli_block_id) => super::block_id::BlockId::from_cli(Some(cli_block_id), context)?, + None => super::block_id::BlockId::choose_variant(context)?, }; - Self { + Ok(Self { sender_account_id, selected_block_id, - } + }) } } impl Sender { - pub fn input_sender_account_id() -> near_primitives::types::AccountId { - println!(); - Input::new() - .with_prompt("Enter your account ID to view your contract status") - .interact_text() - .unwrap() + pub fn input_sender_account_id( + context: &super::operation_mode::online_mode::select_server::ViewContractStateCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + loop { + let account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("What contract do you need to view?") + .interact_text()?; + if let Some(_) = + crate::common::get_account_state(&connection_config, account_id.clone().into())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id.to_string()); + }; + } } pub async fn process( @@ -130,7 +68,7 @@ impl Sender { network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { self.selected_block_id - .process(self.sender_account_id, network_connection_config) + .process(self.sender_account_id.into(), network_connection_config) .await } } diff --git a/src/commands/view_command/view_nonce/account/mod.rs b/src/commands/view_command/view_nonce/account/mod.rs index 59449220f..613e03e7c 100644 --- a/src/commands/view_command/view_nonce/account/mod.rs +++ b/src/commands/view_command/view_nonce/account/mod.rs @@ -1,128 +1,67 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendTo { - /// Specify an account - Account(CliAccount), -} - -#[derive(Debug, Clone)] -pub enum SendTo { - Account(Account), -} - -impl CliSendTo { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Account(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("account".to_owned()); - args - } - } - } -} - -impl From for CliSendTo { - fn from(send_to: SendTo) -> Self { - match send_to { - SendTo::Account(account) => Self::Account(account.into()), - } - } -} - -impl From for SendTo { - fn from(item: CliSendTo) -> Self { - match item { - CliSendTo::Account(cli_account) => { - let account = Account::from(cli_account); - Self::Account(account) - } - } - } -} - -impl SendTo { - pub fn send_to() -> Self { - Self::from(CliSendTo::Account(Default::default())) - } - - pub async fn process( - self, - network_connection_config: crate::common::ConnectionConfig, - ) -> crate::CliResult { - match self { - SendTo::Account(account) => account.process(network_connection_config).await, - } - } -} - -/// Specify account to view the nonce for public key -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliAccount { - account_id: Option, - #[clap(subcommand)] - public_key: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewNonceCommandNetworkContext)] +#[interactive_clap(skip_default_from_cli)] pub struct Account { - account_id: near_primitives::types::AccountId, - pub public_key: super::public_key::AccessKey, -} - -impl CliAccount { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .public_key - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(account_id) = &self.account_id { - args.push_front(account_id.to_string()); - }; - args - } -} - -impl From for CliAccount { - fn from(account: Account) -> Self { - Self { - account_id: Some(account.account_id), - public_key: Some(account.public_key.into()), - } - } + account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + pub public_key: super::public_key::AccessKeyType, } -impl From for Account { - fn from(item: CliAccount) -> Self { - let account_id: near_primitives::types::AccountId = match item.account_id { - Some(cli_account_id) => cli_account_id, - None => Account::input_account_id(), - }; - let public_key = match item.public_key { - Some(cli_public_key) => super::public_key::AccessKey::from(cli_public_key), - None => super::public_key::AccessKey::choose_key(), +impl Account { + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: super::operation_mode::online_mode::select_server::ViewNonceCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let account_id = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.account_id) + { + Some(account_id) => match crate::common::get_account_state( + &connection_config, + account_id.clone().into(), + )? { + Some(_) => account_id, + None => { + println!("Contract <{}> doesn't exist", account_id); + Self::input_account_id(&context)? + } + }, + None => Self::input_account_id(&context)?, }; - Self { + let public_key = super::public_key::AccessKeyType::from_cli( + optional_clap_variant.and_then(|clap_variant| match clap_variant.public_key { + Some(ClapNamedArgAccessKeyTypeForAccount::PublicKey(cli_args)) => Some(cli_args), + None => None, + }), + context, + )?; + Ok(Self { account_id, public_key, - } + }) } } impl Account { - fn input_account_id() -> near_primitives::types::AccountId { - println!(); - Input::new() - .with_prompt("Enter your account ID") - .interact_text() - .unwrap() + fn input_account_id( + context: &super::operation_mode::online_mode::select_server::ViewNonceCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + loop { + let account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("Enter your account ID") + .interact_text()?; + if let Some(_) = + crate::common::get_account_state(&connection_config, account_id.clone().into())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id.to_string()); + }; + } } pub async fn process( @@ -130,7 +69,7 @@ impl Account { network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { self.public_key - .process(self.account_id, network_connection_config) + .process(self.account_id.into(), network_connection_config) .await } } diff --git a/src/commands/view_command/view_nonce/operation_mode/mod.rs b/src/commands/view_command/view_nonce/operation_mode/mod.rs index abaa63c00..ca22b3604 100644 --- a/src/commands/view_command/view_nonce/operation_mode/mod.rs +++ b/src/commands/view_command/view_nonce/operation_mode/mod.rs @@ -1,108 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), - }; - Self { mode } - } + #[interactive_clap(named_arg)] + /// Prepare and, optionally, submit a new transaction with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl From for Mode { - fn from(item: CliMode) -> Self { - match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), - } - } -} - -impl Mode { - pub fn choose_mode() -> Self { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/view_command/view_nonce/operation_mode/online_mode/mod.rs b/src/commands/view_command/view_nonce/operation_mode/online_mode/mod.rs index 1f68d88a1..0dd6b223d 100644 --- a/src/commands/view_command/view_nonce/operation_mode/online_mode/mod.rs +++ b/src/commands/view_command/view_nonce/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) - } - None => self::select_server::SelectServer::choose_server(), - }; - Self { selected_server } - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/mod.rs b/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/mod.rs index e3fb9dc3c..3cda2c0c1 100644 --- a/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { - match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } - CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) - } - } - } +#[derive(Clone)] +pub struct ViewNonceCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> Self { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for ViewNonceCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/server/mod.rs index 556536992..9c8874652 100644 --- a/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/view_command/view_nonce/operation_mode/online_mode/select_server/server/mod.rs @@ -1,119 +1,67 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify an account + pub account: super::super::super::super::account::Account, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - send_to: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::ViewContractCodeCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify an account + pub account: super::super::super::super::account::Account, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: crate::common::ConnectionConfig, - pub send_to: super::super::super::super::account::SendTo, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.rpc_url().as_str(), - ) - .unwrap(), - ), - send_to: Some(server.send_to.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.send_to - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::ViewNonceCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - send_to: Some(server.send_to.into()), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::account::SendTo::send_to(), - }; - Server { - connection_config, - send_to, - } +impl Server { + pub async fn process( + self, + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + self.account.process(connection_config).await } } -impl CliCustomServer { - pub fn into_server(self) -> Server { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let send_to = match self.send_to { - Some(cli_send_to) => cli_send_to.into(), - None => super::super::super::super::account::SendTo::send_to(), - }; - Server { - connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, - send_to, - } +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl Server { pub async fn process(self) -> crate::CliResult { - self.send_to.process(self.connection_config).await + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + self.account.process(connection_config).await } } diff --git a/src/commands/view_command/view_nonce/public_key/mod.rs b/src/commands/view_command/view_nonce/public_key/mod.rs index 5529718fc..37a5b857a 100644 --- a/src/commands/view_command/view_nonce/public_key/mod.rs +++ b/src/commands/view_command/view_nonce/public_key/mod.rs @@ -1,111 +1,18 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliAccessKey { - /// Specify public key - PublicKey(CliAccessKeyType), -} - -#[derive(Debug, Clone)] -pub enum AccessKey { - PublicKey(AccessKeyType), -} - -impl CliAccessKey { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::PublicKey(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("public-key".to_owned()); - args - } - } - } -} - -impl From for CliAccessKey { - fn from(access_key: AccessKey) -> Self { - match access_key { - AccessKey::PublicKey(access_key_type) => Self::PublicKey(access_key_type.into()), - } - } -} - -impl From for AccessKey { - fn from(item: CliAccessKey) -> Self { - match item { - CliAccessKey::PublicKey(cli_delete_access_key_type) => { - Self::PublicKey(cli_delete_access_key_type.into()) - } - } - } -} - -impl AccessKey { - pub fn choose_key() -> Self { - Self::from(CliAccessKey::PublicKey(Default::default())) - } - - pub async fn process( - self, - account_id: near_primitives::types::AccountId, - network_connection_config: crate::common::ConnectionConfig, - ) -> crate::CliResult { - match self { - AccessKey::PublicKey(access_key_type) => { - access_key_type - .process(account_id, network_connection_config) - .await - } - } - } -} - -/// Specify the access key to be deleted -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliAccessKeyType { - public_key: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewNonceCommandNetworkContext)] pub struct AccessKeyType { - pub public_key: near_crypto::PublicKey, -} - -impl CliAccessKeyType { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(public_key) = &self.public_key { - args.push_front(public_key.to_string()); - } - args - } -} - -impl From for CliAccessKeyType { - fn from(access_key_type: AccessKeyType) -> Self { - Self { - public_key: access_key_type.public_key.into(), - } - } -} - -impl From for AccessKeyType { - fn from(item: CliAccessKeyType) -> Self { - let public_key: near_crypto::PublicKey = match item.public_key { - Some(cli_public_key) => cli_public_key, - None => AccessKeyType::input_public_key(), - }; - Self { public_key } - } + pub public_key: crate::types::public_key::PublicKey, } impl AccessKeyType { - pub fn input_public_key() -> near_crypto::PublicKey { - Input::new() + fn input_public_key( + _context: &super::operation_mode::online_mode::select_server::ViewNonceCommandNetworkContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() .with_prompt("Enter a public key for this access key") - .interact_text() - .unwrap() + .interact_text()?) } fn rpc_client(self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { @@ -124,7 +31,7 @@ impl AccessKeyType { block_reference: near_primitives::types::Finality::Final.into(), request: near_primitives::views::QueryRequest::ViewAccessKey { account_id, - public_key: public_key.clone(), + public_key: public_key.clone().into(), }, }) .await diff --git a/src/commands/view_command/view_recent_block_hash/operation_mode/mod.rs b/src/commands/view_command/view_recent_block_hash/operation_mode/mod.rs index abaa63c00..ca22b3604 100644 --- a/src/commands/view_command/view_recent_block_hash/operation_mode/mod.rs +++ b/src/commands/view_command/view_recent_block_hash/operation_mode/mod.rs @@ -1,108 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), - }; - Self { mode } - } + #[interactive_clap(named_arg)] + /// Prepare and, optionally, submit a new transaction with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl From for Mode { - fn from(item: CliMode) -> Self { - match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), - } - } -} - -impl Mode { - pub fn choose_mode() -> Self { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/mod.rs b/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/mod.rs index 1f68d88a1..0dd6b223d 100644 --- a/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/mod.rs +++ b/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) - } - None => self::select_server::SelectServer::choose_server(), - }; - Self { selected_server } - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/mod.rs b/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/mod.rs index e3fb9dc3c..4b2a28b1d 100644 --- a/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { - match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } - CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) - } - } - } +#[derive(Clone)] +pub struct ViewRecentBlockHashCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> Self { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for ViewRecentBlockHashCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/server/mod.rs index 2c5d3a84f..978a51c83 100644 --- a/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/view_command/view_recent_block_hash/operation_mode/online_mode/select_server/server/mod.rs @@ -1,104 +1,79 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer {} +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server {} -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::ViewRecentBlockHashCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: crate::common::ConnectionConfig, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); +impl CustomServerContext { + fn _from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + url: scope.url.clone(), } - args } } -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl From for super::ViewRecentBlockHashCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.rpc_url().as_str(), - ) - .unwrap(), - ), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - std::collections::VecDeque::new() - } -} - -impl From for CliServer { - fn from(_: Server) -> Self { - Self {} +impl Server { + pub async fn process( + self, + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + recent_block_hash_status(connection_config).await } } -impl CliServer { - pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { - Server { connection_config } +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl CliCustomServer { - pub fn into_server(self) -> Server { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - Server { - connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, - } + pub async fn process(self) -> crate::CliResult { + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + recent_block_hash_status(connection_config).await } } -impl Server { - pub async fn process(self) -> crate::CliResult { - let status = near_jsonrpc_client::new_client(self.connection_config.rpc_url().as_str()) - .status() - .await - .map_err(|err| { - color_eyre::Report::msg(format!( - "Failed to fetch public key information for nonce: {:?}", - err - )) - })?; - println!( - "recent block hash: {:?}", - status.sync_info.latest_block_hash - ); - Ok(()) - } +async fn recent_block_hash_status( + connection_config: crate::common::ConnectionConfig, +) -> crate::CliResult { + let status = near_jsonrpc_client::new_client(connection_config.rpc_url().as_str()) + .status() + .await + .map_err(|err| { + color_eyre::Report::msg(format!( + "Failed to fetch public key information for nonce: {:?}", + err + )) + })?; + println!( + "recent block hash: {:?}", + status.sync_info.latest_block_hash + ); + Ok(()) } diff --git a/src/commands/view_command/view_transaction_status/operation_mode/mod.rs b/src/commands/view_command/view_transaction_status/operation_mode/mod.rs index abaa63c00..ca22b3604 100644 --- a/src/commands/view_command/view_transaction_status/operation_mode/mod.rs +++ b/src/commands/view_command/view_transaction_status/operation_mode/mod.rs @@ -1,108 +1,15 @@ -use strum::{EnumDiscriminants, EnumIter, EnumMessage}; - pub mod online_mode; -/// инструмент выбора режима online/offline -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliOperationMode { - #[clap(subcommand)] - mode: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct OperationMode { - pub mode: Mode, -} - -impl CliOperationMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.mode - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliOperationMode { - fn from(item: OperationMode) -> Self { - Self { - mode: Some(item.mode.into()), - } - } -} - -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { - let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), - }; - Self { mode } - } + #[interactive_clap(named_arg)] + /// Prepare and, optionally, submit a new transaction with online mode + pub network: self::online_mode::NetworkArgs, } impl OperationMode { pub async fn process(self) -> crate::CliResult { - self.mode.process().await - } -} - -#[derive(Debug, Clone, clap::Clap)] -pub enum CliMode { - /// Execute a change method with online mode - Network(self::online_mode::CliNetworkArgs), -} - -#[derive(Debug, Clone, EnumDiscriminants)] -#[strum_discriminants(derive(EnumMessage, EnumIter))] -pub enum Mode { - #[strum_discriminants(strum(message = "Yes, I keep it simple"))] - Network(self::online_mode::NetworkArgs), -} - -impl CliMode { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Network(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("network".to_owned()); - args - } - } - } -} - -impl From for CliMode { - fn from(mode: Mode) -> Self { - match mode { - Mode::Network(network_args) => { - Self::Network(self::online_mode::CliNetworkArgs::from(network_args)) - } - } - } -} - -impl From for Mode { - fn from(item: CliMode) -> Self { - match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), - } - } -} - -impl Mode { - pub fn choose_mode() -> Self { - Self::from(CliMode::Network(Default::default())) - } - - pub async fn process(self) -> crate::CliResult { - match self { - Self::Network(network_args) => network_args.process().await, - } + self.network.process().await } } diff --git a/src/commands/view_command/view_transaction_status/operation_mode/online_mode/mod.rs b/src/commands/view_command/view_transaction_status/operation_mode/online_mode/mod.rs index 1f68d88a1..0dd6b223d 100644 --- a/src/commands/view_command/view_transaction_status/operation_mode/online_mode/mod.rs +++ b/src/commands/view_command/view_transaction_status/operation_mode/online_mode/mod.rs @@ -1,51 +1,12 @@ pub mod select_server; -/// аргументы, необходимые для создания транзакции в online mode -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliNetworkArgs { - #[clap(subcommand)] - selected_server: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] pub struct NetworkArgs { + #[interactive_clap(subcommand)] selected_server: self::select_server::SelectServer, } -impl CliNetworkArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.selected_server - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliNetworkArgs { - fn from(network_args: NetworkArgs) -> Self { - Self { - selected_server: Some(network_args.selected_server.into()), - } - } -} - -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { - let selected_server = match item.selected_server { - Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) - } - None => self::select_server::SelectServer::choose_server(), - }; - Self { selected_server } - } -} - impl NetworkArgs { pub async fn process(self) -> crate::CliResult { self.selected_server.process().await diff --git a/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/mod.rs b/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/mod.rs index e3fb9dc3c..631c51df7 100644 --- a/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/mod.rs @@ -1,126 +1,79 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod server; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSelectServer { - /// предоставление данных для сервера https://rpc.testnet.near.org - Testnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.mainnet.near.org - Mainnet(self::server::CliServer), - /// предоставление данных для сервера https://rpc.betanet.near.org - Betanet(self::server::CliServer), - /// предоставление данных для сервера, указанного вручную - Custom(self::server::CliCustomServer), -} - -#[derive(Debug, Clone, EnumDiscriminants)] +#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] +#[interactive_clap(input_context = ())] +#[interactive_clap(output_context = SelectServerContext)] +///Select NEAR protocol RPC server pub enum SelectServer { + /// Provide data for the server https://rpc.testnet.near.org #[strum_discriminants(strum(message = "Testnet"))] Testnet(self::server::Server), + /// Provide data for the server https://rpc.mainnet.near.org #[strum_discriminants(strum(message = "Mainnet"))] Mainnet(self::server::Server), + /// Provide data for the server https://rpc.betanet.near.org #[strum_discriminants(strum(message = "Betanet"))] Betanet(self::server::Server), + /// Provide data for a manually specified server #[strum_discriminants(strum(message = "Custom"))] - Custom(self::server::Server), + Custom(self::server::CustomServer), } -impl CliSelectServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Testnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("testnet".to_owned()); - args - } - Self::Mainnet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("mainnet".to_owned()); - args - } - Self::Betanet(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("betanet".to_owned()); - args - } - Self::Custom(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("custom".to_owned()); - args - } - } - } +#[derive(Clone)] +pub struct SelectServerContext { + selected_server: SelectServerDiscriminants, } -impl From for CliSelectServer { - fn from(select_server: SelectServer) -> Self { - match select_server { - SelectServer::Testnet(server) => Self::Testnet(server.into()), - SelectServer::Mainnet(server) => Self::Mainnet(server.into()), - SelectServer::Betanet(server) => Self::Betanet(server.into()), - SelectServer::Custom(server) => Self::Custom(server.into()), +impl SelectServerContext { + fn from_previous_context( + _previous_context: (), + scope: &::InteractiveClapContextScope, + ) -> Self { + Self { + selected_server: scope.clone(), } } } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { - match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } - CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) - } - } - } +#[derive(Clone)] +pub struct ViewTransactionCommandNetworkContext { + pub connection_config: crate::common::ConnectionConfig, } -impl SelectServer { - pub fn choose_server() -> Self { - println!(); - let variants = SelectServerDiscriminants::iter().collect::>(); - let servers = variants - .iter() - .map(|p| p.get_message().unwrap().to_owned()) - .collect::>(); - let selected_server = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("Select NEAR protocol RPC server:") - .items(&servers) - .default(0) - .interact() - .unwrap(); - let cli_select_server = match variants[selected_server] { - SelectServerDiscriminants::Testnet => CliSelectServer::Testnet(Default::default()), - SelectServerDiscriminants::Mainnet => CliSelectServer::Mainnet(Default::default()), - SelectServerDiscriminants::Betanet => CliSelectServer::Betanet(Default::default()), - SelectServerDiscriminants::Custom => CliSelectServer::Custom(Default::default()), +impl From for ViewTransactionCommandNetworkContext { + fn from(item: SelectServerContext) -> Self { + let connection_config = match item.selected_server { + SelectServerDiscriminants::Testnet => crate::common::ConnectionConfig::Testnet, + SelectServerDiscriminants::Mainnet => crate::common::ConnectionConfig::Mainnet, + SelectServerDiscriminants::Betanet => crate::common::ConnectionConfig::Betanet, + SelectServerDiscriminants::Custom => { + unreachable!("Network context should not be constructed from Custom variant") + } }; - Self::from(cli_select_server) + Self { connection_config } } +} +impl SelectServer { pub async fn process(self) -> crate::CliResult { Ok(match self { SelectServer::Testnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Testnet; + server.process(connection_config).await?; } SelectServer::Mainnet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Mainnet; + server.process(connection_config).await?; } SelectServer::Betanet(server) => { - server.process().await?; + let connection_config = crate::common::ConnectionConfig::Betanet; + server.process(connection_config).await?; } - SelectServer::Custom(server) => { - server.process().await?; + SelectServer::Custom(custom_server) => { + custom_server.process().await?; } }) } diff --git a/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/server/mod.rs index 0c67a9c45..6f5f1218f 100644 --- a/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/view_command/view_transaction_status/operation_mode/online_mode/select_server/server/mod.rs @@ -1,121 +1,67 @@ use dialoguer::Input; -use std::str::FromStr; -/// предустановленный RPC-сервер -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliServer { - #[clap(subcommand)] - pub transaction_status: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::SelectServerContext)] +pub struct Server { + #[interactive_clap(named_arg)] + ///Specify a transaction Status + pub transaction_hash: super::super::super::super::transaction::TransactionType, } -/// данные для custom server -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliCustomServer { - #[clap(long)] - pub url: Option, - #[clap(subcommand)] - transaction_status: Option, +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(input_context = super::SelectServerContext)] +#[interactive_clap(output_context = super::ViewTransactionCommandNetworkContext)] +pub struct CustomServer { + #[interactive_clap(long)] + pub url: crate::common::AvailableRpcServerUrl, + #[interactive_clap(named_arg)] + ///Specify a transaction Status + pub transaction_hash: super::super::super::super::transaction::TransactionType, } -#[derive(Debug, Clone)] -pub struct Server { - pub connection_config: crate::common::ConnectionConfig, - pub transaction_status: super::super::super::super::transaction::Transaction, +struct CustomServerContext { + pub url: crate::common::AvailableRpcServerUrl, } -impl CliCustomServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .transaction_status - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(url) = &self.url { - args.push_front(url.to_string()); - args.push_front("--url".to_string()); - } - args - } -} - -impl From for CliCustomServer { - fn from(server: Server) -> Self { +impl CustomServerContext { + fn from_previous_context( + _previous_context: super::SelectServerContext, + scope: &::InteractiveClapContextScope, + ) -> Self { Self { - url: Some( - crate::common::AvailableRpcServerUrl::from_str( - server.connection_config.rpc_url().as_str(), - ) - .unwrap(), - ), - transaction_status: Some(server.transaction_status.into()), + url: scope.url.clone(), } } } -impl CliServer { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - self.transaction_status - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default() - } -} - -impl From for CliServer { - fn from(server: Server) -> Self { +impl From for super::ViewTransactionCommandNetworkContext { + fn from(item: CustomServerContext) -> Self { Self { - transaction_status: Some(server.transaction_status.into()), + connection_config: crate::common::ConnectionConfig::from_custom_url(&item.url), } } } -impl CliServer { - pub fn into_server(self, connection_config: crate::common::ConnectionConfig) -> Server { - let transaction_status = match self.transaction_status { - Some(cli_transaction_status) => cli_transaction_status.into(), - None => super::super::super::super::transaction::Transaction::transaction(), - }; - Server { - connection_config, - transaction_status, - } +impl Server { + pub async fn process( + self, + connection_config: crate::common::ConnectionConfig, + ) -> crate::CliResult { + self.transaction_hash.process(connection_config).await } } -impl CliCustomServer { - pub fn into_server(self) -> Server { - let url: crate::common::AvailableRpcServerUrl = match self.url { - Some(url) => url, - None => Input::new() - .with_prompt("What is the RPC endpoint?") - .interact_text() - .unwrap(), - }; - let transaction_status = match self.transaction_status { - Some(cli_transaction_status) => cli_transaction_status.into(), - None => super::super::super::super::transaction::Transaction::transaction(), - }; - Server { - connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, - transaction_status, - } +impl CustomServer { + pub fn input_url( + _context: &super::SelectServerContext, + ) -> color_eyre::eyre::Result { + Ok(Input::new() + .with_prompt("What is the RPC endpoint?") + .interact_text()?) } -} -impl Server { pub async fn process(self) -> crate::CliResult { - self.transaction_status - .process(self.connection_config) - .await + let connection_config = crate::common::ConnectionConfig::from_custom_url(&self.url); + self.transaction_hash.process(connection_config).await } } diff --git a/src/commands/view_command/view_transaction_status/signer/mod.rs b/src/commands/view_command/view_transaction_status/signer/mod.rs index 59a9de443..98410960e 100644 --- a/src/commands/view_command/view_transaction_status/signer/mod.rs +++ b/src/commands/view_command/view_transaction_status/signer/mod.rs @@ -1,113 +1,55 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliSendFrom { - /// Specify a signer - Signer(CliSender), -} - -#[derive(Debug, Clone)] -pub enum SendFrom { - Signer(Sender), -} - -impl CliSendFrom { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::Signer(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("signer".to_owned()); - args - } - } - } -} - -impl From for CliSendFrom { - fn from(send_from: SendFrom) -> Self { - match send_from { - SendFrom::Signer(sender) => Self::Signer(sender.into()), - } - } -} - -impl From for SendFrom { - fn from(item: CliSendFrom) -> Self { - match item { - CliSendFrom::Signer(cli_sender) => { - let sender = Sender::from(cli_sender); - Self::Signer(sender) - } - } - } -} - -impl SendFrom { - pub fn send_from() -> Self { - Self::from(CliSendFrom::Signer(Default::default())) - } - - pub async fn process( - self, - network_connection_config: crate::common::ConnectionConfig, - transaction_hash: String, - ) -> crate::CliResult { - match self { - SendFrom::Signer(sender) => { - sender - .process(network_connection_config, transaction_hash) - .await - } - } - } -} - -/// Specify the account that signed the transaction -#[derive(Debug, Default, Clone, clap::Clap)] -pub struct CliSender { - pub account_id: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewTransactionCommandNetworkContext)] +#[interactive_clap(skip_default_from_cli)] pub struct Sender { - pub account_id: near_primitives::types::AccountId, + pub sender_account_id: crate::types::account_id::AccountId, } -impl CliSender { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = std::collections::VecDeque::new(); - if let Some(account_id) = &self.account_id { - args.push_front(account_id.to_string()); - } - args - } -} - -impl From for CliSender { - fn from(sender: Sender) -> Self { - Self { - account_id: Some(sender.account_id), - } - } -} - -impl From for Sender { - fn from(item: CliSender) -> Self { - let account_id: near_primitives::types::AccountId = match item.account_id { - Some(cli_account_id) => cli_account_id, - None => Sender::input_sender_account_id(), +impl Sender { + pub fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: super::operation_mode::online_mode::select_server::ViewTransactionCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + let sender_account_id = match optional_clap_variant + .clone() + .and_then(|clap_variant| clap_variant.sender_account_id) + { + Some(sender_account_id) => match crate::common::get_account_state( + &connection_config, + sender_account_id.clone().into(), + )? { + Some(_) => sender_account_id, + None => { + println!("Contract <{}> doesn't exist", sender_account_id); + Self::input_sender_account_id(&context)? + } + }, + None => Self::input_sender_account_id(&context)?, }; - Self { account_id } + Ok(Self { sender_account_id }) } } impl Sender { - pub fn input_sender_account_id() -> near_primitives::types::AccountId { - println!(); - Input::new() - .with_prompt("Specify the account that signed the transaction") - .interact_text() - .unwrap() + pub fn input_sender_account_id( + context: &super::operation_mode::online_mode::select_server::ViewTransactionCommandNetworkContext, + ) -> color_eyre::eyre::Result { + let connection_config = context.connection_config.clone(); + loop { + let account_id: crate::types::account_id::AccountId = Input::new() + .with_prompt("Specify the account that signed the transaction") + .interact_text()?; + if let Some(_) = + crate::common::get_account_state(&connection_config, account_id.clone().into())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id.to_string()); + }; + } } fn rpc_client(&self, selected_server_url: &str) -> near_jsonrpc_client::JsonRpcClient { @@ -119,10 +61,10 @@ impl Sender { network_connection_config: crate::common::ConnectionConfig, transaction_hash: String, ) -> crate::CliResult { - let account_id = self.account_id.clone(); + let account_id = self.sender_account_id.clone(); let query_view_transaction_status = self .rpc_client(network_connection_config.archival_rpc_url().as_str()) - .tx(transaction_hash, account_id) + .tx(transaction_hash, account_id.into()) .await .map_err(|err| { color_eyre::Report::msg(format!( diff --git a/src/commands/view_command/view_transaction_status/transaction/mod.rs b/src/commands/view_command/view_transaction_status/transaction/mod.rs index e697228fe..3958d7eb8 100644 --- a/src/commands/view_command/view_transaction_status/transaction/mod.rs +++ b/src/commands/view_command/view_transaction_status/transaction/mod.rs @@ -1,138 +1,28 @@ use dialoguer::Input; -#[derive(Debug, Clone, clap::Clap)] -pub enum CliTransaction { - /// Specify a transaction - TransactionHash(CliTransactionType), -} - -#[derive(Debug, Clone)] -pub enum Transaction { - TransactionHash(TransactionType), -} - -impl CliTransaction { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - match self { - Self::TransactionHash(subcommand) => { - let mut args = subcommand.to_cli_args(); - args.push_front("transaction-hash".to_owned()); - args - } - } - } -} - -impl From for CliTransaction { - fn from(transaction: Transaction) -> Self { - match transaction { - Transaction::TransactionHash(transaction_type) => { - Self::TransactionHash(transaction_type.into()) - } - } - } -} - -impl From for Transaction { - fn from(item: CliTransaction) -> Self { - match item { - CliTransaction::TransactionHash(cli_transaction_type) => { - Transaction::TransactionHash(cli_transaction_type.into()) - } - } - } -} - -impl Transaction { - pub fn transaction() -> Self { - Self::from(CliTransaction::TransactionHash(Default::default())) - } - - pub async fn process( - self, - network_connection_config: crate::common::ConnectionConfig, - ) -> crate::CliResult { - match self { - Transaction::TransactionHash(transaction_type) => { - transaction_type.process(network_connection_config).await - } - } - } -} - -/// Specify the transaction to be view -#[derive(Debug, Default, Clone, clap::Clap)] -#[clap( - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands) -)] -pub struct CliTransactionType { - pub transaction_hash: Option, - #[clap(subcommand)] - send_from: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = super::operation_mode::online_mode::select_server::ViewTransactionCommandNetworkContext)] pub struct TransactionType { pub transaction_hash: String, - send_from: super::signer::SendFrom, -} - -impl CliTransactionType { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .send_from - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - if let Some(transaction_hash) = &self.transaction_hash { - args.push_front(transaction_hash.to_string()); - }; - args - } -} - -impl From for CliTransactionType { - fn from(transaction_type: TransactionType) -> Self { - Self { - transaction_hash: Some(transaction_type.transaction_hash), - send_from: Some(transaction_type.send_from.into()), - } - } -} - -impl From for TransactionType { - fn from(item: CliTransactionType) -> Self { - let transaction_hash = match item.transaction_hash { - Some(cli_transaction_hash) => cli_transaction_hash, - None => TransactionType::input_transaction_hash(), - }; - let send_from = match item.send_from { - Some(cli_send_from) => super::signer::SendFrom::from(cli_send_from), - None => super::signer::SendFrom::send_from(), - }; - Self { - transaction_hash, - send_from, - } - } + #[interactive_clap(named_arg)] + signer: super::signer::Sender, } impl TransactionType { - fn input_transaction_hash() -> String { + fn input_transaction_hash( + _context: &super::operation_mode::online_mode::select_server::ViewTransactionCommandNetworkContext, + ) -> color_eyre::eyre::Result { println!(); - Input::new() + Ok(Input::new() .with_prompt("Enter the hash of the transaction you need to view") - .interact_text() - .unwrap() + .interact_text()?) } pub async fn process( self, network_connection_config: crate::common::ConnectionConfig, ) -> crate::CliResult { - self.send_from + self.signer .process(network_connection_config, self.transaction_hash) .await } diff --git a/src/common.rs b/src/common.rs index c50b71e8d..07a6e3c75 100644 --- a/src/common.rs +++ b/src/common.rs @@ -12,6 +12,39 @@ use near_primitives::{ pub type CliResult = color_eyre::eyre::Result<()>; +use dialoguer::{theme::ColorfulTheme, Select}; +use strum::{EnumMessage, IntoEnumIterator}; +pub fn prompt_variant(prompt: &str) -> T +where + T: IntoEnumIterator + EnumMessage, + T: Copy + Clone, +{ + let variants = T::iter().collect::>(); + let actions = variants + .iter() + .map(|p| { + p.get_message() + .unwrap_or_else(|| "error[This entry does not have an option message!!]") + .to_owned() + }) + .collect::>(); + + let selected = Select::with_theme(&ColorfulTheme::default()) + .with_prompt(prompt) + .items(&actions) + .default(0) + .interact() + .unwrap(); + + variants[selected] +} + +#[derive(Debug, Clone)] +pub struct SignerContext { + pub connection_config: Option, + pub signer_account_id: crate::types::account_id::AccountId, +} + #[derive( Debug, Clone, @@ -111,6 +144,10 @@ impl std::fmt::Display for AvailableRpcServerUrl { } } +impl interactive_clap::ToCli for AvailableRpcServerUrl { + type CliVariant = AvailableRpcServerUrl; +} + const ONE_NEAR: u128 = 10u128.pow(24); #[derive(Debug, Clone, Default, PartialEq, PartialOrd)] @@ -193,6 +230,10 @@ impl std::str::FromStr for NearBalance { } } +impl interactive_clap::ToCli for NearBalance { + type CliVariant = NearBalance; +} + const ONE_TERA_GAS: u64 = 10u64.pow(12); const ONE_GIGA_GAS: u64 = 10u64.pow(9); @@ -277,11 +318,19 @@ impl NearGas { } } +impl interactive_clap::ToCli for NearGas { + type CliVariant = NearGas; +} + #[derive(Debug, Clone, Default, PartialEq, PartialOrd)] pub struct TransferAmount { amount: NearBalance, } +impl interactive_clap::ToCli for TransferAmount { + type CliVariant = NearBalance; +} + impl std::fmt::Display for TransferAmount { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.amount) @@ -326,6 +375,12 @@ pub enum ConnectionConfig { } impl ConnectionConfig { + pub fn from_custom_url(custom_url: &AvailableRpcServerUrl) -> Self { + Self::Custom { + url: custom_url.inner.clone(), + } + } + pub fn rpc_url(&self) -> url::Url { match self { Self::Testnet => crate::consts::TESTNET_API_SERVER_URL.parse().unwrap(), diff --git a/src/main.rs b/src/main.rs index eb3a5fb48..66de11d25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,58 +6,15 @@ use common::{try_external_subcommand_execution, CliResult}; mod commands; mod common; mod consts; +mod types; -/// near-cli is a toolbox for interacting with NEAR protocol -#[derive(Debug, Clap)] -#[clap( - version, - author, - about, - setting(clap::AppSettings::ColoredHelp), - setting(clap::AppSettings::DisableHelpSubcommand), - setting(clap::AppSettings::VersionlessSubcommands), - // setting(clap::AppSettings::NextLineHelp) -)] -struct CliArgs { - #[clap(subcommand)] - top_level_command: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] +#[interactive_clap(context = ())] struct Args { + #[interactive_clap(subcommand)] top_level_command: self::commands::TopLevelCommand, } -impl CliArgs { - pub fn to_cli_args(&self) -> std::collections::VecDeque { - let mut args = self - .top_level_command - .as_ref() - .map(|subcommand| subcommand.to_cli_args()) - .unwrap_or_default(); - args.push_front("./near-cli".to_owned()); - args - } -} - -impl From for CliArgs { - fn from(cli_args: Args) -> Self { - Self { - top_level_command: Some(cli_args.top_level_command.into()), - } - } -} - -impl From for Args { - fn from(cli_args: CliArgs) -> Self { - let top_level_command = match cli_args.top_level_command { - Some(cli_subcommand) => self::commands::TopLevelCommand::from(cli_subcommand), - None => self::commands::TopLevelCommand::choose_command(), - }; - Self { top_level_command } - } -} - impl Args { async fn process(self) -> CliResult { self.top_level_command.process().await @@ -67,34 +24,23 @@ impl Args { fn main() -> CliResult { color_eyre::install()?; - let cli = match CliArgs::try_parse() { - Ok(cli) => cli, - Err(error) => { - if matches!( - error.kind, - clap::ErrorKind::UnknownArgument | clap::ErrorKind::InvalidSubcommand - ) { - return try_external_subcommand_execution(); - } - return Err(color_eyre::eyre::eyre!(error)); - } - }; + let cli = CliArgs::parse(); - if let Some(self::commands::CliTopLevelCommand::GenerateShellCompletions(subcommand)) = - cli.top_level_command - { - subcommand.process(); - return Ok(()); - } + // if let Some(self::commands::CliTopLevelCommand::GenerateShellCompletions(subcommand)) = + // cli.top_level_command + // { + // subcommand.process(); + // return Ok(()); + // } - let args = Args::from(cli); + let args = Args::from_cli(Some(cli), ())?; let completed_cli = CliArgs::from(args.clone()); let process_result = actix::System::new().block_on(args.process()); println!( - "Your console command:\n{}", + "Your console command:\n./near-cli {}", shell_words::join(&completed_cli.to_cli_args()) ); diff --git a/src/types/account_id.rs b/src/types/account_id.rs new file mode 100644 index 000000000..e9b68727f --- /dev/null +++ b/src/types/account_id.rs @@ -0,0 +1,36 @@ +#[derive(Eq, Ord, Hash, Clone, Debug, PartialEq, PartialOrd)] +pub struct AccountId(pub near_primitives::types::AccountId); + +impl From for near_primitives::types::AccountId { + fn from(account_id: AccountId) -> Self { + account_id.0 + } +} + +impl std::fmt::Display for AccountId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::str::FromStr for AccountId { + type Err = near_primitives::account::id::ParseAccountError; + + fn from_str(account_id: &str) -> Result { + let account_id = near_primitives::types::AccountId::from_str(account_id)?; + Ok(Self(account_id)) + } +} + +impl AsRef for AccountId +where + Box: AsRef, +{ + fn as_ref(&self) -> &T { + self.0.as_ref() + } +} + +impl interactive_clap::ToCli for AccountId { + type CliVariant = AccountId; +} diff --git a/src/types/crypto_hash.rs b/src/types/crypto_hash.rs new file mode 100644 index 000000000..03118d03e --- /dev/null +++ b/src/types/crypto_hash.rs @@ -0,0 +1,27 @@ +#[derive(Debug, Default, Clone)] +pub struct CryptoHash(pub near_primitives::hash::CryptoHash); + +impl From for near_primitives::hash::CryptoHash { + fn from(item: CryptoHash) -> Self { + item.0 + } +} + +impl std::fmt::Display for CryptoHash { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::str::FromStr for CryptoHash { + type Err = Box; + + fn from_str(s: &str) -> Result { + let crypto_hash = near_primitives::hash::CryptoHash::from_str(s)?; + Ok(Self(crypto_hash)) + } +} + +impl interactive_clap::ToCli for CryptoHash { + type CliVariant = CryptoHash; +} diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 000000000..c79a68182 --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1,8 @@ +pub mod account_id; +pub mod crypto_hash; +pub mod path_buf; +pub mod public_key; +pub mod secret_key; +pub mod signature; +pub mod slip10; +pub mod vec_string; diff --git a/src/types/path_buf.rs b/src/types/path_buf.rs new file mode 100644 index 000000000..6e057e656 --- /dev/null +++ b/src/types/path_buf.rs @@ -0,0 +1,33 @@ +#[derive(Debug, Default, Clone)] +pub struct PathBuf(pub std::path::PathBuf); + +impl From for std::path::PathBuf { + fn from(path_buf: PathBuf) -> Self { + path_buf.0 + } +} + +impl From for PathBuf { + fn from(path_buf: std::path::PathBuf) -> Self { + Self(path_buf) + } +} + +impl std::fmt::Display for PathBuf { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.display()) + } +} + +impl std::str::FromStr for PathBuf { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + let path_buf = std::path::PathBuf::from_str(s)?; + Ok(Self(path_buf)) + } +} + +impl interactive_clap::ToCli for PathBuf { + type CliVariant = PathBuf; +} diff --git a/src/types/public_key.rs b/src/types/public_key.rs new file mode 100644 index 000000000..259c08fa6 --- /dev/null +++ b/src/types/public_key.rs @@ -0,0 +1,33 @@ +#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] +pub struct PublicKey(pub near_crypto::PublicKey); + +impl std::fmt::Display for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::str::FromStr for PublicKey { + type Err = near_crypto::ParseKeyError; + + fn from_str(s: &str) -> Result { + let public_key = near_crypto::PublicKey::from_str(s)?; + Ok(Self(public_key)) + } +} + +impl From for near_crypto::PublicKey { + fn from(item: PublicKey) -> Self { + item.0 + } +} + +impl From for PublicKey { + fn from(item: near_crypto::PublicKey) -> Self { + Self(item) + } +} + +impl interactive_clap::ToCli for PublicKey { + type CliVariant = PublicKey; +} diff --git a/src/types/secret_key.rs b/src/types/secret_key.rs new file mode 100644 index 000000000..50e27c31c --- /dev/null +++ b/src/types/secret_key.rs @@ -0,0 +1,27 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SecretKey(pub near_crypto::SecretKey); + +impl std::fmt::Display for SecretKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::str::FromStr for SecretKey { + type Err = near_crypto::ParseKeyError; + + fn from_str(s: &str) -> Result { + let public_key = near_crypto::SecretKey::from_str(s)?; + Ok(Self(public_key)) + } +} + +impl From for near_crypto::SecretKey { + fn from(item: SecretKey) -> Self { + item.0 + } +} + +impl interactive_clap::ToCli for SecretKey { + type CliVariant = SecretKey; +} diff --git a/src/types/signature.rs b/src/types/signature.rs new file mode 100644 index 000000000..49d5f189a --- /dev/null +++ b/src/types/signature.rs @@ -0,0 +1,23 @@ +#[derive(Debug, Clone)] +pub struct Signature(pub near_crypto::Signature); + +impl std::fmt::Display for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::str::FromStr for Signature { + type Err = near_crypto::ParseSignatureError; + + fn from_str(s: &str) -> Result { + let signature = near_crypto::Signature::from_str(s)?; + Ok(Self(signature)) + } +} + +impl From for near_crypto::Signature { + fn from(item: Signature) -> Self { + item.0 + } +} diff --git a/src/types/slip10.rs b/src/types/slip10.rs new file mode 100644 index 000000000..b6a9c508e --- /dev/null +++ b/src/types/slip10.rs @@ -0,0 +1,23 @@ +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct BIP32Path(pub slip10::BIP32Path); + +impl std::fmt::Display for BIP32Path { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::str::FromStr for BIP32Path { + type Err = slip10::Error; + + fn from_str(s: &str) -> Result { + let bip32path = slip10::BIP32Path::from_str(s)?; + Ok(Self(bip32path)) + } +} + +impl From for slip10::BIP32Path { + fn from(item: BIP32Path) -> Self { + item.0 + } +} diff --git a/src/types/vec_string.rs b/src/types/vec_string.rs new file mode 100644 index 000000000..1c6783882 --- /dev/null +++ b/src/types/vec_string.rs @@ -0,0 +1,31 @@ +#[derive(Debug, Default, Clone)] +pub struct VecString(pub Vec); + +impl std::fmt::Display for VecString { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl std::str::FromStr for VecString { + type Err = color_eyre::eyre::ErrReport; + + fn from_str(s: &str) -> Result { + let vec_str: Vec = s + .trim_matches(|p| p == '[' || p == ']') + .split(",") + .map(|str| str.trim().to_string()) + .collect(); + Ok(Self(vec_str)) + } +} + +impl From for Vec { + fn from(item: VecString) -> Self { + item.0 + } +} + +impl interactive_clap::ToCli for VecString { + type CliVariant = VecString; +}