diff --git a/apps/framework-cli/src/cli/display.rs b/apps/framework-cli/src/cli/display.rs index b9ef41dfe..7f96288d4 100644 --- a/apps/framework-cli/src/cli/display.rs +++ b/apps/framework-cli/src/cli/display.rs @@ -2,6 +2,7 @@ use console::style; use lazy_static::lazy_static; use spinners::{Spinner, Spinners}; use std::sync::{Arc, RwLock}; +use tokio::macros::support::Future; /// # Display Module /// Standardizes the way we display messages to the user in the CLI. This module @@ -187,3 +188,70 @@ where sp.stop_with_newline(); res } + +pub async fn with_spinner_async(message: &str, f: F) -> R +where + F: Future, +{ + let mut sp = Spinner::new(Spinners::Dots9, message.into()); + let res = f.await; + sp.stop_with_newline(); + res +} + +#[cfg(test)] +mod tests { + use crate::cli::routines::RoutineFailure; + + #[test] + fn test_with_spinner() { + use super::*; + use std::thread; + use std::time::Duration; + + let _ = with_spinner("Test delay for one second", || { + thread::sleep(Duration::from_secs(1)); + Ok(()) + }) + .map_err(|err| { + RoutineFailure::new( + Message::new("Failed".to_string(), "to execute a delay".to_string()), + err, + ) + }); + show_message!( + MessageType::Info, + Message { + action: "SUCCESS".to_string(), + details: "Successfully executed a one second delay".to_string(), + } + ); + } + + #[tokio::test] + async fn simple_test_with_spinner_async() -> Result<(), RoutineFailure> { + use super::*; + use crate::cli::routines::RoutineFailure; + use tokio::time::{sleep, Duration}; + + let result = with_spinner_async("Test delay", async { + sleep(Duration::from_secs(15)).await; + Ok(()) + }) + .await + .map_err(|err| { + RoutineFailure::new( + Message::new("Failed".to_string(), "to execute a delay".to_string()), + err, + ) + }); + show_message!( + MessageType::Info, + Message { + action: "SUCCESS".to_string(), + details: "Successfully executed a delay".to_string(), + } + ); + result + } +} diff --git a/apps/framework-cli/src/cli/local_webserver.rs b/apps/framework-cli/src/cli/local_webserver.rs index caaf9705b..91fed12fd 100644 --- a/apps/framework-cli/src/cli/local_webserver.rs +++ b/apps/framework-cli/src/cli/local_webserver.rs @@ -171,7 +171,7 @@ async fn ingest_route( Ok(Response::new(Full::new(response_bytes))) } Err(e) => { - println!("Error: {:?}", e); + debug!("Error: {:?}", e); Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(Full::new(Bytes::from("Error"))) diff --git a/apps/framework-cli/src/cli/routines.rs b/apps/framework-cli/src/cli/routines.rs index 33d5f8430..49d560229 100644 --- a/apps/framework-cli/src/cli/routines.rs +++ b/apps/framework-cli/src/cli/routines.rs @@ -86,10 +86,10 @@ use std::{io::Error, path::PathBuf}; use log::debug; use tokio::sync::RwLock; +use super::display::with_spinner_async; use super::local_webserver::Webserver; use super::watcher::FileWatcher; use super::{Message, MessageType}; - use crate::framework::controller::RouteMeta; use crate::framework::schema::process_schema_file; use crate::infrastructure::console::post_current_state_to_console; @@ -265,29 +265,34 @@ async fn initialize_project_state( let producer = redpanda::create_producer(project.redpanda_config.clone()); info!("Starting schema directory crawl..."); - let crawl_result = - process_schemas_in_dir(&schema_dir, project, &configured_client, route_table).await; - - let _ = post_current_state_to_console( - project, - &configured_client, - &producer, - route_table.clone(), - project.console_config.clone(), - ) - .await; - - match crawl_result { - Ok(_) => { - info!("Schema directory crawl completed successfully"); - Ok(()) - } - Err(e) => { - debug!("Schema directory crawl failed"); - debug!("Error: {:?}", e); - Err(e) + + with_spinner_async("Processing schema file", async { + let crawl_result = + process_schemas_in_dir(&schema_dir, project, &configured_client, route_table).await; + + let _ = post_current_state_to_console( + project, + &configured_client, + &producer, + route_table.clone(), + project.console_config.clone(), + ) + .await; + + match crawl_result { + Ok(_) => { + info!("Schema directory crawl completed successfully"); + Ok(()) + } + Err(e) => { + debug!("Schema directory crawl failed"); + debug!("Error: {:?}", e); + Err(e) + } } - } + }) + .await?; + Ok(()) } #[async_recursion] diff --git a/apps/framework-cli/src/cli/watcher.rs b/apps/framework-cli/src/cli/watcher.rs index f8fc39697..9e41e8aef 100644 --- a/apps/framework-cli/src/cli/watcher.rs +++ b/apps/framework-cli/src/cli/watcher.rs @@ -7,7 +7,7 @@ use std::{ use notify::{event::ModifyKind, Config, RecommendedWatcher, RecursiveMode, Watcher}; use tokio::sync::RwLock; -use super::display::{Message, MessageType}; +use super::display::{with_spinner_async, Message, MessageType}; use crate::infrastructure::stream::redpanda; use crate::{ framework::controller::{remove_table_and_topics_from_schema_file_path, RouteMeta}, @@ -99,7 +99,30 @@ async fn create_framework_objects_from_schema_file_path( if let Some(ext) = schema_file_path.extension() { if ext == "prisma" && schema_file_path.to_str().unwrap().contains(SCHEMAS_DIR) { - process_schema_file(schema_file_path, project, configured_client, route_table).await?; + with_spinner_async("Processing schema file", async { + let result = + process_schema_file(schema_file_path, project, configured_client, route_table) + .await; + match result { + Ok(_) => { + show_message!(MessageType::Info, { + Message { + action: "Schema".to_string(), + details: "file processed".to_string(), + } + }); + } + Err(e) => { + show_message!(MessageType::Error, { + Message { + action: "Schema".to_string(), + details: format!("file failed to process: {}", e), + } + }); + } + } + }) + .await } } else { info!("No primsa extension found. Likely created unsupported file type") @@ -188,7 +211,7 @@ impl FileWatcher { tokio::spawn(async move { if let Err(error) = watch(&project, route_table).await { - println!("Error: {error:?}"); + debug!("Error: {error:?}"); } }); diff --git a/apps/framework-cli/src/utilities/package_managers.rs b/apps/framework-cli/src/utilities/package_managers.rs index 608aa23f1..913f7aa8f 100644 --- a/apps/framework-cli/src/utilities/package_managers.rs +++ b/apps/framework-cli/src/utilities/package_managers.rs @@ -3,7 +3,7 @@ use std::{fmt, path::PathBuf, process::Command}; use home::home_dir; -use log::debug; +use log::{debug, error}; pub fn get_root() -> Result { let result = Command::new("npm").arg("root").arg("-g").output()?; @@ -61,7 +61,14 @@ pub fn install_packages( command.arg("install"); let output = command.output()?; // We should explore not using output here and instead using spawn. - println!("{}", String::from_utf8(output.stdout).unwrap()); + match String::from_utf8(output.stdout) { + Ok(val) => { + debug!("{}", val); + } + Err(e) => { + error!("Error: {:?}", e); + } + } Ok(()) } @@ -76,7 +83,14 @@ pub fn run_build( command.arg("build"); let output = command.output()?; // We should explore not using output here and instead using spawn. - println!("{}", String::from_utf8(output.stdout).unwrap()); + match String::from_utf8(output.stdout) { + Ok(val) => { + debug!("{}", val); + } + Err(e) => { + error!("Error: {:?}", e); + } + } Ok(()) } @@ -98,7 +112,39 @@ pub fn link_sdk( } let output = command.output()?; // We should explore not using output here and instead using spawn. - println!("{}", String::from_utf8(output.stdout).unwrap()); + match String::from_utf8(output.stdout) { + Ok(val) => { + debug!("{}", val); + } + Err(e) => { + error!("Error: {:?}", e); + } + } Ok(()) } + +#[cfg(test)] +mod tests { + #[test] + fn test_output_of_command() -> Result<(), std::io::Error> { + //! Test to demonstrate the use of command and output handling + //! Note: this test will fail if npm isn't installed. + use super::*; + let mut command = Command::new("npm"); + command.arg("version"); + let output = command.output()?; + + assert!(output.status.success()); + match String::from_utf8(output.stdout) { + Ok(val) => { + assert!(val.len() > 0); + } + Err(_e) => { + assert!(false) + } + } + + Ok(()) + } +}