diff --git a/Cargo.toml b/Cargo.toml index af2cb0a..455735c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,4 @@ uuid = { version = "1.3.0", features = [ ] } dotenvy = "0.15.6" lightning-invoice = "0.21.0" + diff --git a/src/cli.rs b/src/cli.rs index 7d701a1..f0849a8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,6 +3,56 @@ use clap::{Parser, Subcommand}; use crate::types::{Kind, Status}; use uuid::Uuid; +/// Check range simple version for just a single value +fn check_fiat_range(s: &str) -> Result { + match s.parse::() { + Ok(val) => Ok(val), + Err(_e) => Err(String::from("Error on parsing sats value")), + } +} + +// // Check range with two values value +// fn check_fiat_range(s: &str) -> Result { +// if s.contains('-') { + +// let min : u32; +// let max : u32; + +// // Get values from CLI +// let values : Vec<&str> = s.split('-').collect(); + +// // Check if more than two values +// if values.len() > 2 { return Err( String::from("Error")) }; + +// // Get ranged command +// if let Err(e) = values[0].parse::() { +// return Err(String::from("Error on parsing, check if you write a digit!")) +// } else { +// min = values[0].parse().unwrap(); +// } + +// if let Err(e) = values[1].parse::() { +// return Err(String::from("Error on parsing, check if you write a digit!")) +// } else { +// max = values[1].parse().unwrap(); +// } + +// // Check min below max +// if min >= max { return Err( String::from("Range of values must be 100-200 for example...")) }; + +// println!("{},{}",min,max); + +// Ok(s.to_string()) +// } +// else{ +// match s.parse::(){ +// Ok(_) => Ok(s.to_string()), +// Err(e) => Err(String::from("Error on parsing sats value")), +// } +// } + +// } + #[derive(Parser)] #[command( name = "mostro-cli", @@ -51,14 +101,15 @@ pub enum Commands { #[arg(value_enum)] #[arg(short, long)] kind: Option, - /// Sats amount + /// Sats amount - leave empty for market price #[arg(short, long)] - amount: u32, + amount: Option, /// Currency selected #[arg(short = 'c', long)] fiat_code: String, /// Fiat amount #[arg(short, long)] + #[clap(value_parser=check_fiat_range)] fiat_amount: u32, /// Payment method #[arg(short = 'm', long)] diff --git a/src/main.rs b/src/main.rs index 902da7d..e82186c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use clap::Parser; use dotenvy::{dotenv, var}; use nostr_sdk::prelude::*; use std::env::set_var; +use std::io::{stdin, stdout, BufRead, Write}; pub mod cli; pub mod error; @@ -171,6 +172,7 @@ async fn main() -> Result<()> { }) => { let mostro_key = XOnlyPublicKey::from_bech32(pubkey)?; + // Create new order for mostro let order_content = Content::Order(Order::new( None, kind.unwrap(), @@ -184,6 +186,29 @@ async fn main() -> Result<()> { None, )); + // Print order preview + let ord_preview = print_order_preview(order_content.clone()).unwrap(); + println!("{ord_preview}"); + let mut user_input = String::new(); + let _input = stdin(); + print!("Check your order! Is it correct? (Y/n) > "); + stdout().flush()?; + + let mut answer = stdin().lock(); + answer.read_line(&mut user_input)?; + + match user_input.to_lowercase().as_str().trim_end() { + "y" | "" => {} + "n" => { + println!("Ok you have cancelled the order, create another one please"); + std::process::exit(0); + } + &_ => { + println!("Can't get what you're sayin!"); + std::process::exit(0); + } + }; + // Create fiat sent message let message = Message::new(0, None, Action::Order, Some(order_content)) .as_json() diff --git a/src/pretty_table.rs b/src/pretty_table.rs index 6a04aeb..acb6095 100644 --- a/src/pretty_table.rs +++ b/src/pretty_table.rs @@ -1,9 +1,68 @@ -use crate::types::Order; +use crate::types::{Content, Order}; use anyhow::Result; use chrono::NaiveDateTime; use comfy_table::presets::UTF8_FULL; use comfy_table::*; +pub fn print_order_preview(ord: Content) -> Result { + let single_order = match ord { + Content::Order(o) => o, + _ => return Err("Error".to_string()), + }; + + let mut table = Table::new(); + + table + .load_preset(UTF8_FULL) + .set_content_arrangement(ContentArrangement::Dynamic) + .set_width(160) + .set_header(vec![ + Cell::new("Buy/Sell") + .add_attribute(Attribute::Bold) + .set_alignment(CellAlignment::Center), + Cell::new("Sats Amount") + .add_attribute(Attribute::Bold) + .set_alignment(CellAlignment::Center), + Cell::new("Fiat Code") + .add_attribute(Attribute::Bold) + .set_alignment(CellAlignment::Center), + Cell::new("Fiat Amount") + .add_attribute(Attribute::Bold) + .set_alignment(CellAlignment::Center), + Cell::new("Payment method") + .add_attribute(Attribute::Bold) + .set_alignment(CellAlignment::Center), + Cell::new("Premium %") + .add_attribute(Attribute::Bold) + .set_alignment(CellAlignment::Center), + ]); + + //Table rows + let r = Row::from(vec![ + match single_order.kind { + crate::types::Kind::Buy => Cell::new(single_order.kind.to_string()) + .fg(Color::Green) + .set_alignment(CellAlignment::Center), + crate::types::Kind::Sell => Cell::new(single_order.kind.to_string()) + .fg(Color::Red) + .set_alignment(CellAlignment::Center), + }, + if single_order.amount.is_none() { + Cell::new("market price").set_alignment(CellAlignment::Center) + } else { + Cell::new(single_order.amount.unwrap()).set_alignment(CellAlignment::Center) + }, + Cell::new(single_order.fiat_code.to_string()).set_alignment(CellAlignment::Center), + Cell::new(single_order.fiat_amount.to_string()).set_alignment(CellAlignment::Center), + Cell::new(single_order.payment_method.to_string()).set_alignment(CellAlignment::Center), + Cell::new(single_order.prime.to_string()).set_alignment(CellAlignment::Center), + ]); + + table.add_row(r); + + Ok(table.to_string()) +} + pub fn print_message_list(dm_list: Vec<(String, String)>) -> Result { let mut table = Table::new(); @@ -111,7 +170,8 @@ pub fn print_orders_table(orders_table: Vec) -> Result { }, Cell::new(single_order.id.unwrap()).set_alignment(CellAlignment::Center), Cell::new(single_order.status.to_string()).set_alignment(CellAlignment::Center), - Cell::new(single_order.amount.to_string()).set_alignment(CellAlignment::Center), + Cell::new(single_order.amount.unwrap().to_string()) + .set_alignment(CellAlignment::Center), Cell::new(single_order.fiat_code.to_string()).set_alignment(CellAlignment::Center), Cell::new(single_order.fiat_amount.to_string()) .set_alignment(CellAlignment::Center), diff --git a/src/types.rs b/src/types.rs index 5ea9a50..d591df5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -96,7 +96,7 @@ pub struct Message { } /// Message content -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub enum Content { Order(Order), PaymentRequest(String), @@ -171,13 +171,13 @@ impl Message { } /// Mostro Order -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct Order { #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, pub kind: Kind, pub status: Status, - pub amount: u32, + pub amount: Option, pub fiat_code: String, pub fiat_amount: u32, pub payment_method: String, @@ -195,7 +195,7 @@ impl Order { id: Option, kind: Kind, status: Status, - amount: u32, + amount: Option, fiat_code: String, fiat_amount: u32, payment_method: String, diff --git a/src/util.rs b/src/util.rs index a2eeb98..002cf9e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -71,6 +71,8 @@ pub async fn connect_nostr() -> Result { for r in relays.into_iter() { client.add_relay(r, None).await?; } + let opts = Options::new().wait_for_connection(true); + client.update_opts(opts); // Connect to relays and keep connection alive client.connect().await; @@ -99,7 +101,9 @@ pub async fn send_order_id_cmd( }, ) = notification { - println!("Message correctly sent to Mostro! Check messages with getdm command"); + println!( + "Message correctly sent to Mostro! Check messages with getdm or listorders command" + ); break; } }