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

[#98] changed all formatting functions so they accept a type that implements Display #100

Merged
merged 7 commits into from
Sep 19, 2022

Conversation

MitchellBerend
Copy link
Collaborator

@MitchellBerend MitchellBerend commented Sep 19, 2022

Resolves #98

This pr converts all functions that accept &str to print them out to stdout/err to accept any type that implements Display in its stead. This allows these functions to not care about the implementation of the actual print formatting and leaves that up to the implementation of whatever needs to be printed.

Additional tasks

  • Documentation for changes provided/changed
  • Tests added

@MitchellBerend MitchellBerend added help wanted Extra attention is needed output Fancy (and not so) output of the tool refactoring labels Sep 19, 2022
@MitchellBerend MitchellBerend removed the help wanted Extra attention is needed label Sep 19, 2022
@MitchellBerend MitchellBerend marked this pull request as ready for review September 19, 2022 09:51
@MitchellBerend
Copy link
Collaborator Author

Im not sure what tests I could add here, I haven't actually verified that the tests that are run and succeed actually cover this refactor fully. There .to_string call in a test is still required for conversion sake.

Copy link
Owner

@chshersh chshersh left a comment

Choose a reason for hiding this comment

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

Looks neat! I really like this refactoring 🤤

I have a few questions I'd like to know the answer to before merging. But it already looks great 👏🏻

src/infra/err.rs Outdated
use std::process;

/// Print an error message and exit with code 1
pub fn abort_with(err_msg: &str) -> ! {
/// This function can take in any type that implements the [`Display`] trait
pub fn abort_with<Message: Display>(err_msg: &Message) -> ! {
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 curious if we actually need & now.

Previously I used &str so I can specify static strings without String::from() or similar. But I imagine that Display is implemented for both the value itself and the reference type.

It also makes more sense to remove & in some semantic meaning. We use errors only once — to report them. So they shouldn't be used afterwards and can be safely moved inside error reporting functions.

So I expect that removing & from all type signatures near the Message and from all use sites will simplify the code further.

Copy link
Collaborator Author

@MitchellBerend MitchellBerend Sep 19, 2022

Choose a reason for hiding this comment

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

For the abort errors this might not make sense actually since the program exits right after.

I'm not 100% sure what the move semantics are here, I know println automatically take references to whatever is passed in but the function still moves the error out of the overarching scope. So in my mind the functions that don't exit the program should always take a reference to the error so it does not get dropped after printing it to stdout/err.

Edit: Removing the references in the signature here actually does not prevent compilation, I guess the compiler is smart enough to see that the objects always get dropped upon exiting the program.

src/infra/err.rs Outdated
@@ -13,7 +15,8 @@ pub fn abort_with(err_msg: &str) -> ! {
}

/// Print an error message, suggesting opening an issue and exit with code 1
pub fn abort_suggest_issue(err_msg: &str) -> ! {
/// This function can take in any type that implements the [`Display`] trait
pub fn abort_suggest_issue<Message: Display + ?Sized>(err_msg: &Message) -> ! {
Copy link
Owner

Choose a reason for hiding this comment

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

Why do we need ?Sized here and not in other functions? 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is an error that passes the message as a &str, the compiler complains about this. I could try and box that slice but that means the slice lives on the heap even though its actually already known at compile time.


Maybe the Box is actually a good thing, since all the output functions always just take a Display trait bound. Having a consistent api does sound nice.

let tool = format!("{}", style(tool_name).cyan().bold());
self.pb.println(format!("{} {} {}", ERROR, tool, msg))
}

fn unexpected_err_msg(&self, tool_name: &str, msg: &str) {
/// This method can take in any type that implements the [`Display`] trait
fn unexpected_err_msg<Message: Display>(&self, tool_name: &str, msg: &Message) {
Copy link
Owner

Choose a reason for hiding this comment

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

I really like how only type-signatures changed but not the implementation of functions and the call sites became simpler as well! 🚀

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes this is a perfect application of polymorphism, it does complicate the code a bit for people that aren't used to generic programming, but this cleans up the code a lot in my opinion.

Copy link
Owner

Choose a reason for hiding this comment

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

Indeed, I like the simplification in this particular case 🙂

In the end, the generic function is written only once and you need to understand it once. But it's used many times and it can save a bit of boilerplate each time.

Copy link
Owner

@chshersh chshersh left a comment

Choose a reason for hiding this comment

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

Beautiful! Only one last comment 🙂

@@ -64,14 +64,14 @@ impl Config {
let expanded_store_directory = shellexpand::full(&self.store_directory);

let store_directory = match expanded_store_directory {
Err(e) => err::abort_with(&e.to_string()),
Err(e) => err::abort_with(e),
Copy link
Owner

Choose a reason for hiding this comment

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

This is perfect 👨🏻‍🍳 🤌

src/lib.rs Outdated
@@ -37,7 +37,7 @@ fn resolve_config_path(config_path: Option<PathBuf>) -> PathBuf {
path
}
None => {
err::abort_suggest_issue("Unable to find $HOME directory");
err::abort_suggest_issue(Box::new("Unable to find $HOME directory"));
Copy link
Owner

Choose a reason for hiding this comment

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

I tried removing Box::new from here and it compiles fine. Could we remove it then?

Suggested change
err::abort_suggest_issue(Box::new("Unable to find $HOME directory"));
err::abort_suggest_issue("Unable to find $HOME directory");

Copy link
Collaborator Author

@MitchellBerend MitchellBerend Sep 19, 2022

Choose a reason for hiding this comment

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

Huh weird, I guess maybe the reference change made this possible.

Edit: Ah yes I guess it would make sense that the size is not known since its behind a reference. I thought since its a static string compiler magic would just put it on the stack automatically.

Copy link
Owner

@chshersh chshersh left a comment

Choose a reason for hiding this comment

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

Amazing ✨

@chshersh chshersh merged commit 7700be2 into chshersh:main Sep 19, 2022
@MitchellBerend MitchellBerend deleted the 98-error-display-refactor branch September 19, 2022 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
output Fancy (and not so) output of the tool refactoring
Projects
None yet
2 participants