From 8fe8930deff13205a2303f1f836d1e7680af3755 Mon Sep 17 00:00:00 2001 From: Robert Lawson <63192607+Robert-maker1994@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:41:59 +0100 Subject: [PATCH] feat: Improve error handling (#398) --- crates/tuono/src/scaffold_project.rs | 68 ++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/crates/tuono/src/scaffold_project.rs b/crates/tuono/src/scaffold_project.rs index 8f68a83f..53c3ec66 100644 --- a/crates/tuono/src/scaffold_project.rs +++ b/crates/tuono/src/scaffold_project.rs @@ -36,6 +36,11 @@ struct GithubTreeResponse { tree: Vec, } +fn exit_with_error(message: &str) -> ! { + eprintln!("{}", message); + std::process::exit(1); +} + #[derive(Deserialize, Debug)] struct GithubFile { path: String, @@ -44,7 +49,13 @@ struct GithubFile { } fn create_file(path: PathBuf, content: String) -> std::io::Result<()> { - let mut file = File::create(path)?; + let mut file = File::create(&path).unwrap_or_else(|err| { + exit_with_error(&format!( + "Failed to create file {}: {}", + path.display(), + err + )); + }); let _ = file.write_all(content.as_bytes()); Ok(()) @@ -55,21 +66,23 @@ pub fn create_new_project(folder_name: Option, template: Option) // In case of missing select the tuono example let template = template.unwrap_or("tuono-app".to_string()); - let client = blocking::Client::builder() .user_agent("") .build() - .expect("Failed to build reqwest client"); + .unwrap_or_else(|_| exit_with_error("Error: Failed to build request client")); // This string does not include the "v" version prefix let cli_version: &str = crate_version!(); - let res_tag = client + let res_tag: GithubTagResponse = client .get(format!("{}v{}", GITHUB_TUONO_TAGS_URL, cli_version)) .send() - .unwrap_or_else(|_| panic!("Failed to call the tag github API for v{cli_version}")) - .json::() - .expect("Failed to parse the tag response"); + .and_then(|response| response.json::()) + .unwrap_or_else(|_| { + exit_with_error(&format!( + "Error: Failed to call or parse the tag github API for v{cli_version}" + )) + }); let sha_tagged_commit = res_tag.object.sha; @@ -80,7 +93,9 @@ pub fn create_new_project(folder_name: Option, template: Option) )) .send() .unwrap_or_else(|_| { - panic!("Failed to call the tagged commit tree github API for v{cli_version}") + exit_with_error(&format!( + "Failed to call the tagged commit tree github API for v{cli_version}" + )) }) .json::>() .expect("Failed to parse the tree structure"); @@ -112,7 +127,7 @@ pub fn create_new_project(folder_name: Option, template: Option) let folder_path = current_dir.join(folder_name); create_directories(&new_project_files, &folder_path, &template) - .expect("Failed to create directories"); + .unwrap_or_else(|err| exit_with_error(&format!("Failed to create directories: {}", err))); for GithubFile { element_type, path, .. @@ -122,15 +137,21 @@ pub fn create_new_project(folder_name: Option, template: Option) let file_content = client .get(format!("{GITHUB_RAW_CONTENT_URL}/v{cli_version}/{path}")) .send() - .expect("Failed to call the folder github API") - .text() - .expect("Failed to parse the repo structure"); + .map_err(|_| exit_with_error("Failed to call the folder github API")) + .and_then(|response| { + response + .text() + .map_err(|_| exit_with_error("Failed to parse the repo structure")) + }) + .unwrap(); let path = PathBuf::from(&path.replace(&format!("examples/{template}/"), "")); let file_path = folder_path.join(&path); - create_file(file_path, file_content).expect("failed to create file"); + if let Err(err) = create_file(file_path, file_content) { + exit_with_error(&format!("Failed to create file: {}", err)); + } } } @@ -157,19 +178,23 @@ fn create_directories( } Ok(()) } - fn update_package_json_version(folder_path: &Path) -> io::Result<()> { let v = crate_version!(); let package_json_path = folder_path.join(PathBuf::from("package.json")); - let package_json = fs::read_to_string(&package_json_path)?; + let package_json = fs::read_to_string(&package_json_path) + .unwrap_or_else(|err| exit_with_error(&format!("Failed to read package.json: {}", err))); let package_json = package_json.replace("link:../../packages/tuono", v); let mut file = OpenOptions::new() .write(true) .truncate(true) - .open(package_json_path)?; + .open(package_json_path) + .unwrap_or_else(|err| exit_with_error(&format!("Failed to open package.json: {}", err))); - file.write_all(package_json.as_bytes())?; + file.write_all(package_json.as_bytes()) + .unwrap_or_else(|err| { + exit_with_error(&format!("Failed to write to package.json: {}", err)) + }); Ok(()) } @@ -177,16 +202,19 @@ fn update_package_json_version(folder_path: &Path) -> io::Result<()> { fn update_cargo_toml_version(folder_path: &Path) -> io::Result<()> { let v = crate_version!(); let cargo_toml_path = folder_path.join(PathBuf::from("Cargo.toml")); - let cargo_toml = fs::read_to_string(&cargo_toml_path)?; + let cargo_toml = fs::read_to_string(&cargo_toml_path) + .unwrap_or_else(|err| exit_with_error(&format!("Failed to read Cargo.toml: {}", err))); let cargo_toml = cargo_toml.replace("{ path = \"../../crates/tuono_lib/\"}", &format!("\"{v}\"")); let mut file = OpenOptions::new() .write(true) .truncate(true) - .open(cargo_toml_path)?; + .open(cargo_toml_path) + .unwrap_or_else(|err| exit_with_error(&format!("Failed to open Cargo.toml: {}", err))); - file.write_all(cargo_toml.as_bytes())?; + file.write_all(cargo_toml.as_bytes()) + .unwrap_or_else(|err| exit_with_error(&format!("Failed to write to Cargo.toml: {}", err))); Ok(()) }