-
-
Notifications
You must be signed in to change notification settings - Fork 18
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
[#98] changed all formatting functions so they accept a type that implements Display #100
Conversation
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 |
There was a problem hiding this 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) -> ! { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) -> ! { |
There was a problem hiding this comment.
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? 🤔
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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! 🚀
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
…d Boxed the offending &str
… exit the program
There was a problem hiding this 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), |
There was a problem hiding this comment.
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")); |
There was a problem hiding this comment.
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?
err::abort_suggest_issue(Box::new("Unable to find $HOME directory")); | |
err::abort_suggest_issue("Unable to find $HOME directory"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing ✨
Resolves #98
This pr converts all functions that accept
&str
to print them out to stdout/err to accept any type that implementsDisplay
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