pub mod account;
pub mod address_book;
pub mod balances;
pub mod config;
pub mod send_message;
pub mod setup;
pub mod sign_message;
pub mod transaction;
use crate::utils::{Handle, Inquire};
use crate::disk::{AddressBook, DiskInterface};
use crate::network::{Network, NetworkStore};
use account::AccountActions;
use clap::Subcommand;
use config::ConfigActions;
use inquire::{Select, Text};
use setup::{get_setup_menu, setup_inquire_and_handle};
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter};
use transaction::TransactionActions;
#[derive(Subcommand, Display, Debug, EnumIter)]
#[allow(clippy::large_enum_variant)]
pub enum Action {
#[command(hide = true)]
Setup,
#[command(alias = "bal")]
Assets,
#[command(alias = "acc")]
Account {
#[command(subcommand)]
action: Option<AccountActions>,
},
#[command(alias = "ab")]
AddressBook {
#[command(subcommand)]
action: Option<address_book::AddressBookActions>,
},
#[command(alias = "tx")]
Transaction {
#[command(subcommand)]
action: Option<TransactionActions>,
},
#[command(alias = "sm")]
SignMessage { message: Option<String> },
#[command(alias = "send")]
SendMessage {
to: Option<String>,
msg: Option<String>,
},
#[command(alias = "cfg")]
Config {
#[command(subcommand)]
action: Option<config::ConfigActions>,
},
}
impl Action {
pub fn get_menu() -> Vec<Action> {
let mut options: Vec<Action> = Action::iter().collect();
let setup_menu = get_setup_menu();
if setup_menu.is_empty() {
options.remove(0);
}
options
}
pub fn get_menu_str() -> Vec<String> {
Action::get_menu()
.into_iter()
.map(|action| format!("{action}"))
.collect()
}
}
impl Inquire for Action {
fn inquire(_: &()) -> Option<Action> {
let options: Vec<Action> = Action::get_menu();
inquire::Select::new("Choose subcommand:", options)
.with_formatter(&|a| format!("{a}"))
.prompt()
.ok()
}
}
impl Handle for Action {
fn handle(&self, _carry_on: ()) {
match self {
Action::Setup => setup_inquire_and_handle(),
Action::Assets => {
balances::get_all_balances();
}
Action::Account { action } => {
AccountActions::handle_optn_inquire(action, ());
}
Action::AddressBook { action } => {
address_book::AddressBookActions::handle_optn_inquire(action, ())
}
Action::Transaction { action } => {
TransactionActions::handle_optn_inquire(action, ());
}
Action::SignMessage { message } => {
let message = if let Some(message) = message {
message.clone()
} else {
Text::new("Enter the message to sign:")
.prompt()
.expect("must enter message to sign")
};
sign_message::sign_message(message);
}
Action::SendMessage { to, msg, network } => {
let to = match to {
Some(addr) if !addr.is_empty() => addr.clone(),
_ => {
let choice = Select::new(
"Select recipient method:",
vec!["Enter manually", "Choose from address book"],
)
.prompt()
.expect("❌ Must select a method");
if choice == "Enter manually" {
Text::new("Enter recipient address:")
.prompt()
.expect("❌ Must enter recipient address")
} else {
let address_book = AddressBook::load();
let addresses = address_book.list().to_vec();
if addresses.is_empty() {
println!("⚠️ Address book is empty, please enter manually.");
Text::new("Enter recipient address:")
.prompt()
.expect("❌ Must enter recipient address")
} else {
let selected = Select::new("Select recipient:", addresses)
.prompt()
.expect("❌ Must select an address");
selected.address.to_string()
}
}
}
};
let msg = match msg {
Some(m) if !m.is_empty() => m.clone(),
_ => Text::new("Enter message:")
.prompt()
.expect("❌ Must enter a message"),
};
let network = network.clone().or_else(|| {
let networks = NetworkStore::load().networks;
Select::new("Select a network:", networks).prompt().ok()
});
send_message::handle_send_message(to, msg, network);
}
Action::Config { action } => {
ConfigActions::handle_optn_inquire(action, ());
}
}
}
}