From 6255bd2e06e50582b0608e74be30bd2f5e901f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 20 Jun 2023 17:14:10 +0200 Subject: [PATCH] Clearing output, changing directories --- drink-cli/src/app_state.rs | 18 +++--- drink-cli/src/cli.rs | 9 ++- drink-cli/src/executor.rs | 99 +++++++++++++++++++++++++++++-- drink-cli/src/main.rs | 105 --------------------------------- drink-cli/src/ui/footer.rs | 34 ++++++++--- drink-cli/src/ui/layout.rs | 1 - drink-cli/src/ui/mod.rs | 6 +- drink-cli/src/ui/output.rs | 15 +---- drink-cli/src/ui/print.rs | 46 +++++++++++++++ drink-cli/src/ui/user_input.rs | 15 ++++- 10 files changed, 199 insertions(+), 149 deletions(-) create mode 100644 drink-cli/src/ui/print.rs diff --git a/drink-cli/src/app_state.rs b/drink-cli/src/app_state.rs index 127a3eb..00745d8 100644 --- a/drink-cli/src/app_state.rs +++ b/drink-cli/src/app_state.rs @@ -1,5 +1,7 @@ use std::{env, path::PathBuf}; +use drink::Sandbox; +use ratatui::text::Line; use sp_runtime::AccountId32; #[derive(Clone, Eq, PartialEq, Hash, Debug, Default)] @@ -9,26 +11,21 @@ pub struct ChainInfo { pub current_contract_address: Option, } -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] pub enum Mode { + #[default] Managing, Editing, } -impl Default for Mode { - fn default() -> Self { - Mode::Managing - } -} - -#[derive(Clone, Eq, PartialEq, Hash, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] pub struct UiState { pub pwd: PathBuf, pub mode: Mode, pub user_input: String, - pub output: Vec, + pub output: Vec>, pub output_offset: u16, } @@ -44,8 +41,9 @@ impl Default for UiState { } } -#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)] +#[derive(Default)] pub struct AppState { + pub sandbox: Sandbox, pub chain_info: ChainInfo, pub ui_state: UiState, } diff --git a/drink-cli/src/cli.rs b/drink-cli/src/cli.rs index 9a81fb9..c6c6ca3 100644 --- a/drink-cli/src/cli.rs +++ b/drink-cli/src/cli.rs @@ -2,14 +2,19 @@ use clap::Parser; #[derive(Parser)] pub enum CliCommand { + #[clap(alias = "c")] + Clear, + #[clap(alias = "cd")] + ChangeDir { + path: String, + }, + #[clap(alias = "b")] Build, #[clap(alias = "d")] Deploy, CallGet, CallFlip, - #[clap(alias = "e")] - Exit, } #[cfg(test)] diff --git a/drink-cli/src/executor.rs b/drink-cli/src/executor.rs index 83521f6..0b9ce1f 100644 --- a/drink-cli/src/executor.rs +++ b/drink-cli/src/executor.rs @@ -1,12 +1,101 @@ +use std::{env, process::Command}; + use anyhow::Result; +use clap::Parser; +use sp_runtime::AccountId32; -use crate::app_state::AppState; +use crate::{app_state::AppState, cli::CliCommand}; pub fn execute(app_state: &mut AppState) -> Result<()> { let command = app_state.ui_state.user_input.clone(); - app_state - .ui_state - .output - .push(format!("Executing: {}", command)); + app_state.print_command(&command); + + let command = command + .split_ascii_whitespace() + .map(|a| a.trim()) + .collect::>(); + let cli_command = match CliCommand::try_parse_from([vec![""], command].concat()) { + Ok(cli_command) => cli_command, + Err(_) => { + app_state.print_error("Invalid command"); + return Ok(()); + } + }; + + match cli_command { + CliCommand::Clear => { + app_state.ui_state.output.clear(); + app_state.ui_state.output_offset = 0; + } + CliCommand::ChangeDir { path } => { + let target_dir = app_state.ui_state.pwd.join(path); + match env::set_current_dir(target_dir) { + Ok(_) => { + app_state.ui_state.pwd = + env::current_dir().expect("Failed to get current directory"); + app_state.print("Directory changed"); + } + Err(err) => app_state.print_error(&err.to_string()), + } + } + + CliCommand::Build => { + build_contract(app_state); + } + CliCommand::Deploy => { + // account_id = Some(deploy_contract(&mut sandbox)); + app_state.chain_info.deployed_contracts += 1; + app_state.chain_info.current_contract_address = Some(AccountId32::new([0; 32])); + } + CliCommand::CallGet => { + // let account_id = match account_id { + // Some(ref account_id) => account_id.clone(), + // None => { + // eprintln!("Contract not deployed"); + // continue; + // } + // }; + // + // let result = sandbox.call_contract(account_id, "get".to_string()); + // println!("Contract called successfully.\n\n{result}") + } + CliCommand::CallFlip => {} + } + Ok(()) } + +fn build_contract(app_state: &mut AppState) { + let output = Command::new("cargo-contract") + .arg("contract") + .arg("build") + .arg("--release") + .output() + .expect("Failed to execute 'cargo contract' command"); + + if output.status.success() { + app_state.print("Contract built successfully"); + } else { + let stderr = String::from_utf8_lossy(&output.stderr); + app_state.print_error(&format!( + "Error executing 'cargo contract' command:\n{stderr}" + )); + } +} + +// fn deploy_contract(sandbox: &mut Sandbox) -> AccountId32 { +// println!("Deploying contract..."); +// +// let contract_bytes_path = env::current_dir() +// .expect("Failed to get current directory") +// .join(CONTRACT_DIR) +// .join("target/ink/example.wasm"); +// let contract_bytes = std::fs::read(contract_bytes_path).expect("Failed to read contract bytes"); +// +// let account_id = sandbox.deploy_contract(contract_bytes); +// +// println!("Contract deployed successfully"); +// +// account_id +// } +// diff --git a/drink-cli/src/main.rs b/drink-cli/src/main.rs index a6ab3ae..726fa5b 100644 --- a/drink-cli/src/main.rs +++ b/drink-cli/src/main.rs @@ -1,8 +1,4 @@ -use std::{env, process::Command}; - use anyhow::Result; -use drink::Sandbox; -use sp_runtime::AccountId32; use crate::ui::run_ui; @@ -11,107 +7,6 @@ mod cli; mod executor; mod ui; -const CONTRACT_DIR: &str = "../example/"; - fn main() -> Result<()> { run_ui() } - -// fn main() -> Result<(), Box> { -// let mut sandbox = Sandbox::new(); -// let mut account_id = None; -// -// loop { -// println!(); -// -// let mut user_input = String::new(); -// io::stdin() -// .read_line(&mut user_input) -// .expect("Failed to get user input"); -// -// let cli_command = match CliCommand::try_parse_from(["", user_input.trim()]) { -// Ok(cli_command) => cli_command, -// Err(_) => { -// eprintln!("Invalid command"); -// continue; -// } -// }; -// -// match cli_command { -// CliCommand::Build => { -// build_contract(); -// } -// CliCommand::Deploy => { -// account_id = Some(deploy_contract(&mut sandbox)); -// } -// CliCommand::CallGet => { -// let account_id = match account_id { -// Some(ref account_id) => account_id.clone(), -// None => { -// eprintln!("Contract not deployed"); -// continue; -// } -// }; -// -// let result = sandbox.call_contract(account_id, "get".to_string()); -// println!("Contract called successfully.\n\n{result}") -// } -// CliCommand::CallFlip => { -// let account_id = match account_id { -// Some(ref account_id) => account_id.clone(), -// None => { -// eprintln!("Contract not deployed"); -// continue; -// } -// }; -// -// let result = sandbox.call_contract(account_id, "flip".to_string()); -// println!("Contract called successfully.\n\n{result}") -// } -// CliCommand::Exit => { -// println!("Exit"); -// break; -// } -// } -// } -// -// Ok(()) -// } - -fn build_contract() { - println!("Building contract..."); - - let current_dir = env::current_dir().expect("Failed to get current directory"); - let contract_path = current_dir.join(CONTRACT_DIR); - env::set_current_dir(contract_path).expect("Failed to change directory"); - - let output = Command::new("cargo-contract") - .arg("contract") - .arg("build") - .arg("--release") - .output() - .expect("Failed to execute 'cargo contract' command"); - - if output.status.success() { - println!("Contract built successfully"); - } else { - let stderr = String::from_utf8_lossy(&output.stderr); - eprintln!("Error executing 'cargo contract' command:\n{}", stderr); - } -} - -fn deploy_contract(sandbox: &mut Sandbox) -> AccountId32 { - println!("Deploying contract..."); - - let contract_bytes_path = env::current_dir() - .expect("Failed to get current directory") - .join(CONTRACT_DIR) - .join("target/ink/example.wasm"); - let contract_bytes = std::fs::read(contract_bytes_path).expect("Failed to read contract bytes"); - - let account_id = sandbox.deploy_contract(contract_bytes); - - println!("Contract deployed successfully"); - - account_id -} diff --git a/drink-cli/src/ui/footer.rs b/drink-cli/src/ui/footer.rs index d83e6a1..2b5bda1 100644 --- a/drink-cli/src/ui/footer.rs +++ b/drink-cli/src/ui/footer.rs @@ -1,5 +1,7 @@ use ratatui::{ layout::Alignment, + style::{Color, Modifier, Style}, + text::{Line, Span}, widgets::{Block, BorderType, Borders, Paragraph, Widget}, }; @@ -11,12 +13,30 @@ pub(super) fn build(app_state: &AppState) -> impl Widget { .borders(Borders::ALL) .border_type(BorderType::Rounded); - let instruction = match app_state.ui_state.mode { - Mode::Managing => "Press 'q' to quit. Press `i` to enter editing mode", - Mode::Editing => "Press 'Esc' to quit editing mode", - }; + let instruction: Line = match app_state.ui_state.mode { + Mode::Managing => vec![ + Span::raw("Press "), + Span::styled("'q'", Style::default().fg(Color::Yellow)), + Span::raw(" to quit. Press "), + Span::styled("'i'", Style::default().fg(Color::Yellow)), + Span::raw(" to enter editing mode"), + ], + Mode::Editing => vec![ + Span::raw("Press "), + Span::styled("'Esc'", Style::default().fg(Color::Yellow)), + Span::raw(" to quit editing mode"), + ], + } + .into(); - Paragraph::new(format!("{instruction}\nMade by Aleph Zero Foundation")) - .alignment(Alignment::Center) - .block(base) + Paragraph::new(vec![ + instruction, + Span::styled( + "Made by Aleph Zero Foundation", + Style::default().add_modifier(Modifier::ITALIC), + ) + .into(), + ]) + .alignment(Alignment::Center) + .block(base) } diff --git a/drink-cli/src/ui/layout.rs b/drink-cli/src/ui/layout.rs index 89db71c..91f52f3 100644 --- a/drink-cli/src/ui/layout.rs +++ b/drink-cli/src/ui/layout.rs @@ -1,7 +1,6 @@ use ratatui::{ backend::Backend, layout::{Constraint, Direction, Layout}, - widgets::{Block, BorderType, Borders}, Frame, }; diff --git a/drink-cli/src/ui/mod.rs b/drink-cli/src/ui/mod.rs index 98a5eb5..3fbb7d1 100644 --- a/drink-cli/src/ui/mod.rs +++ b/drink-cli/src/ui/mod.rs @@ -2,9 +2,10 @@ mod current_env; mod footer; mod layout; mod output; +mod print; mod user_input; -use std::{io, io::Stdout, thread::sleep}; +use std::{io, io::Stdout}; use anyhow::{anyhow, Result}; use crossterm::{ @@ -15,11 +16,10 @@ use crossterm::{ }; use layout::layout; use ratatui::backend::CrosstermBackend; -use sp_runtime::Saturating; use crate::{ app_state::{ - AppState, Mode, + AppState, Mode::{Editing, Managing}, }, executor::execute, diff --git a/drink-cli/src/ui/output.rs b/drink-cli/src/ui/output.rs index 07d324c..0aac13f 100644 --- a/drink-cli/src/ui/output.rs +++ b/drink-cli/src/ui/output.rs @@ -1,7 +1,4 @@ -use ratatui::{ - text::Line, - widgets::{Block, BorderType, Borders, Padding, Paragraph, Widget}, -}; +use ratatui::widgets::{Block, BorderType, Borders, Padding, Paragraph, Widget}; use crate::app_state::AppState; @@ -12,15 +9,7 @@ pub(super) fn build(app_state: &AppState) -> impl Widget { .border_type(BorderType::Rounded) .padding(Padding::horizontal(1)); - let output = app_state - .ui_state - .output - .iter() - .rev() - .map(|s| Line::from(s.clone())) - .collect::>(); - - Paragraph::new(output) + Paragraph::new(app_state.ui_state.output.clone()) .block(block) .scroll((app_state.ui_state.output_offset, 0)) } diff --git a/drink-cli/src/ui/print.rs b/drink-cli/src/ui/print.rs new file mode 100644 index 0000000..9a1d0b4 --- /dev/null +++ b/drink-cli/src/ui/print.rs @@ -0,0 +1,46 @@ +use ratatui::{ + style::{Color, Modifier, Style}, + text::Span, +}; + +use crate::app_state::AppState; + +impl AppState { + pub fn print_command(&mut self, command: &str) { + self.ui_state.output.push("".into()); + self.ui_state.output.push( + Span::styled( + format!("Executing `{command}`"), + Style::default() + .fg(Color::Blue) + .add_modifier(Modifier::BOLD) + .add_modifier(Modifier::ITALIC), + ) + .into(), + ); + } + + pub fn print(&mut self, msg: &str) { + self.ui_state.output.push( + Span::styled( + msg.to_string(), + Style::default() + .fg(Color::White) + .add_modifier(Modifier::BOLD), + ) + .into(), + ); + } + + pub fn print_error(&mut self, err: &str) { + for line in err.split('\n') { + self.ui_state.output.push( + Span::styled( + line.to_string(), + Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), + ) + .into(), + ) + } + } +} diff --git a/drink-cli/src/ui/user_input.rs b/drink-cli/src/ui/user_input.rs index 30d8216..e09833b 100644 --- a/drink-cli/src/ui/user_input.rs +++ b/drink-cli/src/ui/user_input.rs @@ -1,12 +1,21 @@ -use ratatui::widgets::{Block, BorderType, Borders, Paragraph, Widget}; +use ratatui::{ + style::{Color, Style}, + widgets::{Block, BorderType, Borders, Paragraph, Widget}, +}; -use crate::app_state::AppState; +use crate::app_state::{AppState, Mode}; pub(super) fn build(app_state: &mut AppState) -> impl Widget { + let mut style = Style::default(); + if app_state.ui_state.mode != Mode::Editing { + style = style.fg(Color::DarkGray); + } + let block = Block::default() .title("User input") .borders(Borders::ALL) - .border_type(BorderType::Rounded); + .border_type(BorderType::Rounded) + .style(style); Paragraph::new(app_state.ui_state.user_input.clone()).block(block) }