Skip to content

Clippy pendantic and more refactors#219

Closed
soruh wants to merge 6 commits intolycheeverse:masterfrom
soruh:clippy-pendantic
Closed

Clippy pendantic and more refactors#219
soruh wants to merge 6 commits intolycheeverse:masterfrom
soruh:clippy-pendantic

Conversation

@soruh
Copy link

@soruh soruh commented Apr 12, 2021

  • Check the code with clippy::pedantic, fix most of it's complaints and ignore the rest (mostly documentation tasks).
  • Manually converted some code into using methods on Option and Result instead of matches.

P.S.: my rustfmt seems to have collapsed some uses and comment, if you dislike this I can try to undo that manually.

P.P.S.: sorry for creating another relatively large PR :)

@soruh
Copy link
Author

soruh commented Apr 12, 2021

Ah it seems I accidentally used some nightly features

let bar = ProgressBar::new(links.len() as u64)
.with_style(ProgressStyle::default_bar().template(
let with_progress = !cfg.no_progress;
let pb = with_progress.then(|| {
Copy link
Member

Choose a reason for hiding this comment

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

Hum... I find the old version easier to understand. 🤔

Copy link
Author

Choose a reason for hiding this comment

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

I can see that I'll revert it when I rebase on the new master when #208 is merged.

Copy link
Member

Choose a reason for hiding this comment

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

#208 is merged now. 😄

src/client.rs Outdated
.and_then(|token| {
(!token.is_empty()).then(|| Github::new(user_agent, Credentials::Token(token)))
})
.transpose()?;
Copy link
Member

Choose a reason for hiding this comment

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

Aha! transpose does make sense here.

Copy link
Author

Choose a reason for hiding this comment

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

Yeah I was so happy when I found out about transpose

@mre
Copy link
Member

mre commented Apr 13, 2021

Great improvements! Thanks a lot for this.
I'd like to wait with the merge until #208 is in. Most likely you'd have to rebase your changes on top of master once that is done. Hope that's okay for you.

@soruh
Copy link
Author

soruh commented Apr 13, 2021

Seems like a good Idea although doing this again on the new master might actually be less work than rebasing.

@lebensterben
Copy link
Member

There's no need to merge this.
I've already applied clippy::pendantic in #208.

@soruh
Copy link
Author

soruh commented Apr 13, 2021

Which is why the plan is to rebase on top of your PR.
Also as far I can see you didn't actually enable clippy::pedantic but only applied it's suggestions which means that it will not be checked against by CI.

@lebensterben
Copy link
Member

@soruh
That's true. But you shouldn't rebase on top of my PR because you will see many rebase conflicts. It's much easier just to discard this one and start a new PR which adds the clippy attributes.
P.S. I've enabled both clippy::pedantic and clippy::all.

@lebensterben lebensterben mentioned this pull request Apr 14, 2021
@mre
Copy link
Member

mre commented Apr 22, 2021

@soruh, would you like to rebase on top of the current master, create a new PR with the improvements or are all those changes covered by #208 already? 😊

@soruh
Copy link
Author

soruh commented Apr 22, 2021

I think there are some additional changes, I'll try to rebase it when I find the time and will make a new PR if there are significant changes left / close this one if there aren't.

@mre
Copy link
Member

mre commented Apr 22, 2021

Thanks!

@soruh soruh force-pushed the clippy-pendantic branch from ed8bed5 to 40cd387 Compare April 29, 2021 21:15
@soruh
Copy link
Author

soruh commented Apr 29, 2021

I just rebased my code onto master, there is not a huge amount left but still enough for keeping this open I think.

I re-added my old solution of converting file extensions to lower-case before checking them. By now I have realized that this is probably not the optimal solution (as is always heap allocates). A version of to_lowercase that returns a Cow or just checking for the uppercase variants of the extensions directly may be preferable.

I personally like the .not().then(..) pattern but am of course open to change it to something more traditional if preferred.

There a a few things I would like an opinion on (annotated with REVIEW).

None => tokio::runtime::Runtime::new()?,
};
None => tokio::runtime::Runtime::new(),
}?;
Copy link
Member

Choose a reason for hiding this comment

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

ok. but easy to miss when reading.

Copy link
Author

Choose a reason for hiding this comment

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

We are creating a runtime in both cases and are checking if that failed after which I find more readable.

Copy link
Member

Choose a reason for hiding this comment

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

the ? operator in }?; is quite easy to miss.

return match e.kind() {
ErrorKind::NotFound => Ok(None),
_ => Err(Error::from(e)),
_ => Err(e.into()),
Copy link
Member

Choose a reason for hiding this comment

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

No I deliberately avoid using Into::into().
It's hard to figure out what it is converting to.

Copy link
Author

Choose a reason for hiding this comment

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

We are calling .into() inside of an Err(...), the Error:: doesn't give you any new information here and just makes the more verbose.

Copy link
Member

Choose a reason for hiding this comment

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

How do you figure out what's the type of E in a Result<T, E>?
Error::from(e) clearly shows E is of type Error, while e.into() has no such information.

let pb = if cfg.no_progress {
None
} else {
let pb = cfg.no_progress.not().then(|| {
Copy link
Member

Choose a reason for hiding this comment

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

Does this increase any readability or performance?

Copy link
Author

Choose a reason for hiding this comment

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

I think the functional style is clearer but I agree that the .not() may not be (see my original comment).

Copy link
Member

Choose a reason for hiding this comment

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

The issue is not with not, the main issue is the functional style.
std::option::Option::then() is a fairly new addition. If it's the more idiomatic way of handling Boolean conditionals, it'd be added much earlier.

let github_token = self
.github_token
.as_ref()
.and_then(|token| {
Copy link
Member

Choose a reason for hiding this comment

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

Again.
When I refactored these code, I deliberately avoided this kind of pattern.

Copy link
Author

Choose a reason for hiding this comment

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

I agree that the not() should be a ! but think this is way more readable than the Some(ref token) if !token.is_empty() => Some( incantation it replaced. I agree that this is a matter of opinion though.

Copy link
Member

Choose a reason for hiding this comment

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

No.

I deliberately keep Some(ref token). this make it very clear of the type signature.
( Calling as_ref() has the same effect. )

The not readable part is the nested and_then and then. Unless you add explicit type signature, it's less clear. But if you did, it's too verbose.

(attr_name, elem_name),
// TODO: re-enable once `or-patterns` become stable in Rust 1.53
// ("href" | "src" | "srcset" | "cite", _) | ("data", "object") | ("onhashchange", "body")
("href", _)
Copy link
Member

Choose a reason for hiding this comment

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

When I refactored this part, it's requested to keep the current form for readability.

// assert_eq!(FileType::from(Path::new("/")), FileType::Plaintext);
// assert_eq!(FileType::from("test"), FileType::Plaintext);
assert_eq!(FileType::from("test.md"), FileType::Markdown);
assert_eq!(FileType::from("TEST.MD"), FileType::Markdown);
Copy link
Member

Choose a reason for hiding this comment

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

-1
On modern file system it's case sensitive.
According to mimeinfo of text/markdown, valid extension is one of .md .mkd and .markdown.

If a .MD extension is recognized as markdown, it IS A BUG.

Copy link
Author

Choose a reason for hiding this comment

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

Are you saying that handling an html file incorrectly because it is called "index.HTML" is okay? Or are you only referring to Markdown files? In that case I guess If we do what I proposed in my comment above we can only check if certain extension match insensitively, I don't see how handling files with extensions that are not technically in spec is an improvement

Copy link
Member

Choose a reason for hiding this comment

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

I'm not talking about the REVIEW comment.

What I'm taking is the additional assert_eq tests you added are not sound. They should be removed.

Copy link
Author

Choose a reason for hiding this comment

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

Ah, that makes sense, are you okay with changing that to a ".HTML" test or are uppercase extension not technically in-spec in general?

Copy link
Member

Choose a reason for hiding this comment

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

To incorporate them is very complicated.
For modern Linux/Unix/FreeBSD systems, the file system should be case-sensitive. Thus these are unknown schemes and are recognized as planttext. (But newer Linux kernel supports opt-in case-insensitive feature)
For macOS, HFS/HFS+ defaults to case-insensitive if operating system is installed before 10.3. Otherwise it's case-sensitive. For APS, it's case-sensitive.
For Windows, NTFS is actually case sensitive internally, but defaults to case-insensitive in user space.

If you really want to incorporate those tests, you need conditional compilations based on operating system and whehther the filesystem is case sensitive.

My opinion is these cases are too complicated to handle. Just remove those tests.

Status::Timeout(Some(code)) => format!(" [{}]", code),
Status::Error(e) => format!(" ({})", e),
_ => "".to_owned(),
_ => "".into(),
Copy link
Member

Choose a reason for hiding this comment

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

No.
When converting a &str to String, I deliberately use .to_owned() to indicate the memory allocation.

Copy link
Author

@soruh soruh Apr 30, 2021

Choose a reason for hiding this comment

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

This doesn't allocate; I thought about using String::new() but decided that this is clearer.

Copy link
Member

Choose a reason for hiding this comment

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

The whole code base consistently use str::to_owned() for this type of conversion.
When you refactor you need to take the overall coding style and convention into account.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, I'll revert it

} else {
None
};
let base_url = base_url
Copy link
Member

Choose a reason for hiding this comment

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

Again, not necessarily more readable.

Copy link
Author

Choose a reason for hiding this comment

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

I disagree but again, I guess it's a matter of opinion if you think the functional or the imperative style is more readable

@soruh
Copy link
Author

soruh commented Apr 30, 2021

I will be reverting some of the changes of the original rebase (at least the .not()) and will see about the style related ones once there is a consensus on them.

Regarding the lowercase file extension handling I do think it makes sense to do but can remove it if again if deemed unnecessary.

P.S. @lebensterben I would like to inform you that the way you have reviewed my code has honestly been a very bad experience; This had been the only time that I did not look forward to reading a review of my code.
I personally don't mind that much but I hope you don't always do this as I can't imagine it encourages new contributors to continue contributing (it certainly hasn't for me).

P.P.S I'm pretty tired though which may be affecting my irritability, in that regard. I'll review the rest tomorrow.

@lebensterben
Copy link
Member

FYI when I refactor, there are a few things taken into account

  • Does the refactor make it more performant?
  • Does the refactor make it more readable?
  • Does the refactor conform to idiomatic Rust coding styles and the overall style of this code base.

Also, I always prefer to make things more explicit. Without writing type signatures everywhere, a way to achieve this to use more specific function/methods.
E.g.

  • For &str to String, use to_owned instead of into or to_string.
  • For general conversion of T to U, use U::from(T) instead of T.into().

@soruh
Copy link
Author

soruh commented Apr 30, 2021

I agree with the first part and usually do the same.
In this case the PR was mostly intended to fix clippy::pedantic warnings (without being fully aware that your PR already did most of that), but while I was at it I added some things that I would do in my code intentionally in a separate commit so that they are easy to remove if not wanted.
Since due to the rebase the PR has become basically only the latter part I agree that it not a very idiomatic refactor.
This version is basically just the code after rebasing it, I'll look into making it more idomatic if I find the time.

@mre
Copy link
Member

mre commented Sep 4, 2021

@soruh are you still planning to work on this or shall we close the PR and open a new one if time allows?

@mre mre added the question Further information is requested label Sep 4, 2021
@soruh
Copy link
Author

soruh commented Sep 12, 2021

Ah yeah sorry, I think I'll just close this (don't really have time to work on this at the moment).

@soruh soruh closed this Sep 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

question Further information is requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants