Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error handling #299

Merged
merged 34 commits into from
Mar 23, 2020
Merged

Conversation

Kibouo
Copy link
Contributor

@Kibouo Kibouo commented Mar 21, 2020

PR for #291

  • Currently adding error messages based on context. However, I'm not familiar with the code base so thorough review is highly recommended to improve error messages
  • ... especially for the fzf and core modules.

TODO:

  • Every call to bash now has an error message similar to "Failed to execute bash". This should probably be related to the actual command ran, i.e. params given to bash.
  • A lot of anyhow::[with_]context() is used to add, well, context. It might be better to create custom error variants for repetitive errors.
  • navi fn welcome is broken.
  • Add "file a bug report" message to top-level error.
  • Test for potential breakages. Logic changes have been avoided as much as possible but stuff happens.
  • navi repo browse and navi repo add https://github.com/denisidoro/navi crash. Root cause is fzf not returning data due to window layout error.
    image
  • bump version

@Kibouo Kibouo requested a review from denisidoro as a code owner March 21, 2020 18:10
@welcome
Copy link

welcome bot commented Mar 21, 2020

Thanks for opening this pull request!

@Kibouo
Copy link
Contributor Author

Kibouo commented Mar 21, 2020

My approach currently is to:

  • eliminate all unwrap(), except in tests
  • expect() when calling an internal function with highly controlled input, since these "ugly" panic messages will should only ever be seen by devs.
  • return as much Result as possible, as the added context to each layer of errors will be nicely formatted and hopefully useful for the end-user.

Copy link
Owner

@denisidoro denisidoro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 👏 👏

I've learned a lot reading this. Thank you for your contribution!

@Kibouo Kibouo requested a review from denisidoro March 22, 2020 23:17
@denisidoro
Copy link
Owner

The navi repo bug is fixed in master 👍

@Kibouo
Copy link
Contributor Author

Kibouo commented Mar 22, 2020

Kindly re-review changes. Please check whether the actual messages are clear.

Also added a wrapping error telling the end-user to file an issue. Ex:
image

This required the error type FileAnIssue to be made public in the library. It had to be used in the bin. handle_config is called multiple times so it would look weird to potentially have multiple of these messages nested. Is this change ok?

A few things remain to be done as can be seen in the TODO of the first post.

@Kibouo
Copy link
Contributor Author

Kibouo commented Mar 22, 2020

Would this be a bump in version to 2.4.0?

@Kibouo Kibouo changed the title [WIP] Improve error handling Improve error handling Mar 23, 2020
Copy link
Owner

@denisidoro denisidoro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work!

I've never thought that error messages could serve as "documentation" for the source code.

My future work will surely be influenced by this PR.

Thanks again!

io::BufReader::new(file)
.lines()
.map(|line| line.map_err(Error::from))
.collect::<Result<Vec<String>, Error>>()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not well versed in Rust, but won't this collect make the collection eager? If the file being parsed has 10,000 lines, won't this make the function read all these lines before we can process the first one?

Copy link
Contributor Author

@Kibouo Kibouo Mar 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Err case it short circuits. But Ok case it indeed allocates unnecessarily. Fixed!

// The dir might not exist which would throw an error. But here we don't care about that
// since our purpose is to make space for a new directory. Potential other errors
// (e.g due to lack of permission) will cause an error during dir creation.
let _ = filesystem::remove_dir(&repo_path_str);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why using let _ =?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust requires you to use the return value of remove_dir. But as stated in the comment above it, we don't care about the potential Err value. And the Ok type is () anyway.


filesystem::remove_dir(&repo_path_str);
filesystem::create_dir(&repo_path_str);
// The dir might not exist which would throw an error. But here we don't care about that
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you suggest another approach? I agree this is very error-prone

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking whether the dir exists before trying to remove it is a bit more robust, but not worth it imho. The check + remove is not atomic so remove_dir would still be able to return the same errors. Just less frequently.

src/handler.rs Outdated
Search { query } => flows::search::main(query.clone(), config),

Query { query } => {
let error_string = format!("Failed to filter cheatsheets for {}", &query);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't with_context prevent us from generating this str 100% of the times?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

query is part of config. But config is given away to flows::query::main on :17, which makes it impossible to use query after that. So we need to make a copy of query before that happens.

let query_clone = query.clone() and then passing that to with_context would be a possibility. Not sure how smart the compiler is. I'll change it to be sure 🤔

} else if let [flag] = flag_and_value {
Err(anyhow!("No value provided for the flag `{}`", flag))
} else {
unreachable!() // Chunking by 2 allows only for tuples of 1 or 2 items...
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use an else in the line 72 instead?

Copy link
Contributor Author

@Kibouo Kibouo Mar 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chunks(2) call on :46 returns slices (&[..]) of the collection. The problem is that, despite telling chunks that the slices should be 2 long, it doesn't know this at compile time. So Rust wants us to handle every possible case of slice length exhaustively.

Concrete this means that Rust would want the following let binding to be exhaustive.

} else {
    let [flag] = flag_and_value;
    Err(anyhow!("No value provided for the flag `{}`", flag))
}

image

You could do the following but obviously it wouldn't be as helpful an error:

} else {
    Err(anyhow!("No value provided for a flag"))
}


#[derive(Error, Debug)]
#[error(
"\rHey listen! Navi encountered a problem.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😂

@denisidoro denisidoro merged commit 7734723 into denisidoro:master Mar 23, 2020
@welcome
Copy link

welcome bot commented Mar 23, 2020

Congrats on merging your first pull request!

@Kibouo Kibouo mentioned this pull request Mar 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants