From f894b17d679da948cde7e43ca029bdff8167671d Mon Sep 17 00:00:00 2001 From: DeveloperC Date: Wed, 16 Oct 2024 12:31:24 +0100 Subject: [PATCH] feat: converting error handing to anyhow --- Cargo.lock | 7 ++ Cargo.toml | 3 + end-to-end-tests/features/steps/assertions.py | 6 ++ end-to-end-tests/features/steps/then.py | 14 ++-- src/commits/mod.rs | 79 ++++++++----------- src/main.rs | 16 ++-- 6 files changed, 65 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ae5371..6085d36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + [[package]] name = "autocfg" version = "1.1.0" @@ -137,6 +143,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" name = "conventional_commits_next_version" version = "6.0.1" dependencies = [ + "anyhow", "clap", "git2", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 50daa54..55b6ce8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,9 @@ lazy_static = "1.4.0" strum = "0.26.0" strum_macros = "0.26.0" +# For error handling. +anyhow = "1.0.89" + [dev-dependencies] # For parameterized testing. diff --git a/end-to-end-tests/features/steps/assertions.py b/end-to-end-tests/features/steps/assertions.py index ff66106..7bd5616 100644 --- a/end-to-end-tests/features/steps/assertions.py +++ b/end-to-end-tests/features/steps/assertions.py @@ -24,6 +24,12 @@ def assert_error_equals(result, error): f"Error = {error.encode()}.\n" +def assert_error_contains(result, error): + assert error in result.stderr, "Expected standard error to contain the error.\n" + \ + f"Standard error = {result.stderr.encode()}.\n" + \ + f"Error = {error.encode()}.\n" + + def assert_error_matches_regex(result, regex): assert regex.match(result.stderr) is not None, f"Expected standard errors to match the regex.\n" + \ f"Standard error = {result.stderr.encode()}.\n" + \ diff --git a/end-to-end-tests/features/steps/then.py b/end-to-end-tests/features/steps/then.py index 5b5d205..a15e944 100644 --- a/end-to-end-tests/features/steps/then.py +++ b/end-to-end-tests/features/steps/then.py @@ -41,7 +41,7 @@ def assert_current_version_assertion_fails(context): @then('their is a could not find commit hash "{commit_hash}" error.') def assert_could_not_find_commit_hash_error(context, commit_hash): # Given - could_not_find_commit_hash_error = f" ERROR conventional_commits_next_version::commits > Can not find a commit with the hash '{commit_hash}'.\n" # fmt: off + could_not_find_commit_hash_error = f"Can not find a commit with the hash '{commit_hash}'.\n" # fmt: off # When result = execute_conventional_commits_next_version(context) @@ -49,37 +49,37 @@ def assert_could_not_find_commit_hash_error(context, commit_hash): # Then assert_no_output(result) assert_command_unsuccessful(result) - assert_error_equals(result, could_not_find_commit_hash_error) + assert_error_contains(result, could_not_find_commit_hash_error) @then('their is a could not find reference "{reference}" error.') def assert_could_not_find_reference_error(context, reference): # Given - could_not_find_reference_error = f" ERROR conventional_commits_next_version::commits > Could not find a reference with the name \"{reference}\".\n" # fmt: off + could_not_find_reference_error = f"Could not find a reference with the name \"{reference}\".\n" # fmt: off # When/Then result = assert_current_version_assertion_fails(context) # Then - assert_error_equals(result, could_not_find_reference_error) + assert_error_contains(result, could_not_find_reference_error) @then('their is a could not find shortened commit hash "{shortened_commit_hash}" error.') def assert_could_not_find_shortened_commit_hash_error(context, shortened_commit_hash): # Given - could_not_find_shortened_commit_hash_error = f" ERROR conventional_commits_next_version::commits > No commit hashes start with the provided short commit hash \"{shortened_commit_hash}\".\n" # fmt: off + could_not_find_shortened_commit_hash_error = f"No commit hashes start with the provided short commit hash \"{shortened_commit_hash}\".\n" # fmt: off # When/Then result = assert_current_version_assertion_fails(context) # Then - assert_error_equals(result, could_not_find_shortened_commit_hash_error) + assert_error_contains(result, could_not_find_shortened_commit_hash_error) @then('their is a ambiguous shortened commit hash "{shortened_commit_hash}" error.') def assert_ambiguous_shortened_commit_hash_error(context, shortened_commit_hash): # Given - ambiguous_shortened_commit_hash_error = re.compile(f"^ ERROR conventional_commits_next_version::commits > Ambiguous short commit hash, the commit hashes [[]({shortened_commit_hash}[a-f0-9]*(, )?)*[]] all start with the provided short commit hash \"{shortened_commit_hash}\".\n$") # fmt: off + ambiguous_shortened_commit_hash_error = re.compile(f"^ ERROR conventional_commits_next_version > Ambiguous short commit hash, the commit hashes [[]({shortened_commit_hash}[a-f0-9]*(, )?)*[]] all start with the provided short commit hash \"{shortened_commit_hash}\".\n$") # fmt: off # When/Then result = assert_current_version_assertion_fails(context) diff --git a/src/commits/mod.rs b/src/commits/mod.rs index 657e795..4be936b 100644 --- a/src/commits/mod.rs +++ b/src/commits/mod.rs @@ -1,5 +1,6 @@ use std::collections::VecDeque; +use anyhow::{bail, Context, Result}; use git2::{Oid, Repository, Revwalk}; use semver::{BuildMetadata, Prerelease, Version}; @@ -27,7 +28,7 @@ impl Commits { reference: T, commit_filters: Vec, git_history_mode: GitHistoryMode, - ) -> Result { + ) -> Result { let reference_oid = get_reference_oid(repository, reference.as_ref())?; get_commits_till_head_from_oid(repository, reference_oid, commit_filters, git_history_mode) } @@ -37,7 +38,7 @@ impl Commits { commit_hash: T, commit_filters: Vec, git_history_mode: GitHistoryMode, - ) -> Result { + ) -> Result { let commit_oid = parse_to_oid(repository, commit_hash.as_ref())?; get_commits_till_head_from_oid(repository, commit_oid, commit_filters, git_history_mode) } @@ -138,25 +139,23 @@ fn get_commits_till_head_from_oid( from_commit_hash: Oid, commit_filters: Vec, git_history_mode: GitHistoryMode, -) -> Result { +) -> Result { fn get_revwalker( repository: &Repository, from_commit_hash: Oid, git_history_mode: GitHistoryMode, - ) -> Result { + ) -> Result { let mut commits = repository.revwalk()?; if git_history_mode == GitHistoryMode::FirstParent { commits.simplify_first_parent()?; } commits.push_head()?; - match commits.hide(from_commit_hash) { - Ok(_) => Ok(commits), - Err(error) => { - error!("Can not find a commit with the hash '{from_commit_hash}'."); - Err(error) - } - } + commits.hide(from_commit_hash).context(format!( + "Can not find a commit with the hash '{}'.", + from_commit_hash + ))?; + Ok(commits) } let revwalker = get_revwalker(repository, from_commit_hash, git_history_mode)?; @@ -178,31 +177,32 @@ fn get_commits_till_head_from_oid( } debug!("Operating upon {} commits.", commits.len()); - Ok(Commits { commits }) } -fn get_reference_oid(repository: &Repository, matching: &str) -> Result { - match repository.resolve_reference_from_short_name(matching) { - Ok(reference) => { - trace!( - "Matched {matching:?} to the reference {:?}.", - reference.name().unwrap() - ); - let commit = reference.peel_to_commit()?; - Ok(commit.id()) - } - Err(error) => { - error!("Could not find a reference with the name {matching:?}."); - Err(error) - } - } +fn get_reference_oid(repository: &Repository, matching: &str) -> Result { + let reference = repository + .resolve_reference_from_short_name(matching) + .context(format!( + "Could not find a reference with the name {:?}.", + matching + ))?; + trace!( + "Matched {:?} to the reference {:?}.", + matching, + reference.name().unwrap() + ); + let commit = reference.peel_to_commit()?; + Ok(commit.id()) } -fn parse_to_oid(repository: &Repository, oid: &str) -> Result { +fn parse_to_oid(repository: &Repository, oid: &str) -> Result { match oid.len() { 1..=39 => { - trace!("Attempting to find a match for the short commit hash {oid:?}."); + trace!( + "Attempting to find a match for the short commit hash {:?}.", + oid + ); let matching_oid_lowercase = oid.to_lowercase(); let mut revwalker = repository.revwalk()?; @@ -220,7 +220,7 @@ fn parse_to_oid(repository: &Repository, oid: &str) -> Result None } Err(error) => { - error!("{error:?}"); + error!("{:?}", error); None } }) @@ -228,26 +228,17 @@ fn parse_to_oid(repository: &Repository, oid: &str) -> Result match matched_commit_hashes.len() { 0 => { - let error_message = format!( - "No commit hashes start with the provided short commit hash {matching_oid_lowercase:?}." + bail!( + "No commit hashes start with the provided short commit hash {:?}.", + matching_oid_lowercase ); - error!("{error_message}"); - Err(git2::Error::from_str(&error_message)) } 1 => Ok(*matched_commit_hashes.first().unwrap()), _ => { - let error_message = format!("Ambiguous short commit hash, the commit hashes {matched_commit_hashes:?} all start with the provided short commit hash {matching_oid_lowercase:?}."); - error!("{error_message}"); - Err(git2::Error::from_str(&error_message)) + bail!("Ambiguous short commit hash, the commit hashes {:?} all start with the provided short commit hash {:?}.", matched_commit_hashes, matching_oid_lowercase); } } } - _ => match git2::Oid::from_str(oid) { - Ok(oid) => Ok(oid), - Err(error) => { - error!("{oid:?} is not a valid commit hash."); - Err(error) - } - }, + _ => git2::Oid::from_str(oid).context(format!("{:?} is not a valid commit hash.", oid)), } } diff --git a/src/main.rs b/src/main.rs index eb4fefc..de3a0e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,13 +6,12 @@ extern crate pretty_env_logger; use std::io::{stdin, Read}; +use anyhow::{bail, Result}; use clap::Parser; use git2::Repository; -pub use crate::calculation_mode::CalculationMode; use crate::cli::Arguments; -pub use crate::commits::Commits; -pub use crate::git_history_mode::GitHistoryMode; +use crate::commits::Commits; mod calculation_mode; mod cli; @@ -27,12 +26,13 @@ fn main() { let arguments = Arguments::parse(); trace!("The command line arguments provided are {arguments:?}."); - if run(arguments).is_err() { + if let Err(err) = run(arguments) { + error!("{:?}", err); std::process::exit(ERROR_EXIT_CODE); } } -fn run(arguments: Arguments) -> Result<(), git2::Error> { +fn run(arguments: Arguments) -> Result<()> { let commits = match ( arguments.from_stdin, arguments.from_commit_hash, @@ -63,7 +63,7 @@ fn run(arguments: Arguments) -> Result<(), git2::Error> { ) } (_, _, _) => { - unreachable!("Invalid combination of arguments."); + bail!("Invalid combination of arguments."); } }?; let expected_version = @@ -71,9 +71,7 @@ fn run(arguments: Arguments) -> Result<(), git2::Error> { if let Some(current_version) = arguments.current_version { if current_version < expected_version { - let error_message = format!("The current version {current_version} is not larger or equal to the expected version {expected_version}."); - error!("{error_message}"); - return Err(git2::Error::from_str(&error_message)); + bail!(format!("The current version {current_version} is not larger or equal to the expected version {expected_version}.")); } info!("The current version {current_version} is larger or equal to the expected version {expected_version}."); } else {