diff --git a/crates/client/src/commander.rs b/crates/client/src/commander.rs index a0891488..8b6f358c 100644 --- a/crates/client/src/commander.rs +++ b/crates/client/src/commander.rs @@ -378,22 +378,30 @@ impl Commander { let cargo_thread = std::thread::spawn(move || -> Result<(), Error> { let output = Self::expand_package(&name); + // release progress bar loop here as the expansion is the longest part + // further we cannot release after parsing as parsing can panic which will not + // release the lock + c_mutex.store(false, std::sync::atomic::Ordering::SeqCst); + let output = output.expect("Program expansion failed"); if output.status.success() { - let code = String::from_utf8(output.stdout).unwrap(); + let code = String::from_utf8(output.stdout).expect("Reading stdout failed"); let idl_program = idl::parse_to_idl_program(name, &code)?; - let mut vec = c_shared_mutex.lock().unwrap(); - let mut vec_fuzzer = c_shared_mutex_fuzzer.lock().unwrap(); + let mut vec = c_shared_mutex + .lock() + .expect("Acquire IdlProgram lock failed"); + let mut vec_fuzzer = c_shared_mutex_fuzzer + .lock() + .expect("Acquire Fuzzer data lock failed"); vec.push(idl_program); vec_fuzzer.push((code, lib_path)); - c_mutex.store(false, std::sync::atomic::Ordering::SeqCst); Ok(()) } else { - let error_text = String::from_utf8(output.stderr).unwrap(); - c_mutex.store(false, std::sync::atomic::Ordering::SeqCst); + let error_text = + String::from_utf8(output.stderr).expect("Reading stderr failed"); Err(Error::ReadProgramCodeFailed(error_text)) } }); @@ -428,7 +436,7 @@ impl Commander { /// # Returns /// - Returns a `std::process::Output` object containing the outcome of the cargo command execution. This includes /// the expanded source code in the command's stdout, along with any stderr output and the exit status. - fn expand_package(package_name: &str) -> std::process::Output { + fn expand_package(package_name: &str) -> std::io::Result { std::process::Command::new("cargo") .arg("+nightly-2023-12-28") .arg("rustc") @@ -437,63 +445,7 @@ impl Commander { .arg("--") .arg("-Zunpretty=expanded") .output() - .unwrap() } - - /// Retrieves Rust `use` statements from the expanded source code of the "program_client" package. - /// - /// This function initiates an expansion of the "program_client" package to obtain - /// the macro-expanded source code. It then parses this source to extract all `use` statement declarations, - /// returning them as a vector of `syn::ItemUse`. If no `use` statements are found, a default import for - /// `trdelnik_client::*` is included in the returned vector to ensure the caller has at least one valid import. - /// - /// # Returns - /// A `Vec` containing the parsed `use` statements from the "program_client" package's source code. - /// If no `use` statements are found, the vector contains a default `use trdelnik_client::*;` statement. - /// - /// # Errors - /// - Returns an `Error` if the package expansion fails, either due to command execution failure or issues - /// parsing the expanded code into a UTF-8 string. - /// - Propagates errors encountered while acquiring locks on shared state or parsing `use` statements from the code. - #[throws] - pub async fn get_program_client_imports() -> Vec { - let shared_mutex = std::sync::Arc::new(std::sync::Mutex::new(String::new())); - - let mutex = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); - let c_mutex = std::sync::Arc::clone(&mutex); - - let c_shared_mutex = std::sync::Arc::clone(&shared_mutex); - - let cargo_thread = std::thread::spawn(move || -> Result<(), Error> { - let output = Self::expand_package("program_client"); - - if output.status.success() { - let mut code = c_shared_mutex.lock().unwrap(); - code.push_str(&String::from_utf8(output.stdout)?); - - c_mutex.store(false, std::sync::atomic::Ordering::SeqCst); - Ok(()) - } else { - // command failed leave unmodified - c_mutex.store(false, std::sync::atomic::Ordering::SeqCst); - Ok(()) - } - }); - - Self::expand_progress_bar("program_client", &mutex); - - cargo_thread.join().unwrap()?; - - let code = shared_mutex.lock().unwrap(); - let code = code.as_str(); - let mut use_modules: Vec = vec![]; - Self::get_use_statements(code, &mut use_modules)?; - if use_modules.is_empty() { - use_modules.push(syn::parse_quote! { use trdelnik_client::*; }) - } - use_modules - } - #[throws] pub fn get_use_statements(code: &str, use_modules: &mut Vec) { for item in syn::parse_file(code).unwrap().items.into_iter() { diff --git a/crates/client/src/test_generator.rs b/crates/client/src/test_generator.rs index 1a836bb5..82ba75be 100644 --- a/crates/client/src/test_generator.rs +++ b/crates/client/src/test_generator.rs @@ -163,8 +163,7 @@ impl TestGenerator { commander.build_anchor_project().await?; // expands programs within programs folder self.expand_programs().await?; - // expands program_client and obtains - // use statements + // obtain use statements // if program_client is not yet initialized // use statements are set to default self.get_program_client_imports().await?; @@ -205,7 +204,9 @@ impl TestGenerator { commander.build_anchor_project().await?; // expand programs self.expand_programs().await?; - // expand program_client + // obtain use statements + // if program_client is not yet initialized + // use statements are set to default self.get_program_client_imports().await?; // initialize program client if it is not yet initialized self.init_program_client().await?; @@ -246,7 +247,9 @@ impl TestGenerator { commander.build_anchor_project().await?; // expand programs self.expand_programs().await?; - // expand program_client + // obtain use statements + // if program_client is not yet initialized + // use statements are set to default self.get_program_client_imports().await?; // add/update program_client self.add_program_client().await?; @@ -260,10 +263,21 @@ impl TestGenerator { (self.idl, self.codes_libs_pairs) = Commander::expand_program_packages(&self.packages).await?; } - /// Expand .program_client source code and obtain its use statements. + /// Get user specified use statements from .program_client lib. #[throws] async fn get_program_client_imports(&mut self) { - self.use_tokens = Commander::get_program_client_imports().await?; + let lib_path = construct_path!(self.root, PROGRAM_CLIENT_DIRECTORY, SRC_DIRECTORY, LIB); + if lib_path.exists() { + let code = fs::read_to_string(lib_path).await.unwrap_or_else(|_e| { + println!("\x1b[1;93mWarning\x1b[0m: Unable to read .program_client, use statements set to default."); + String::default() + }); + Commander::get_use_statements(&code, &mut self.use_tokens)?; + } + if self.use_tokens.is_empty() { + self.use_tokens + .push(syn::parse_quote! { use trdelnik_client::*; }) + } } /// Checks if the whole folder structure for .program_client is already