Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upsemver example: find the latest version satsifying given range #371
Conversation
ludwigpacifici
reviewed
Nov 25, 2017
| fn run() -> Result<()> { | ||
| let mut ver = Version::from_str("0.0.0").unwrap(); | ||
| let vset = ["0.9.0", "1.0.0", "1.0.1"]; | ||
| let r = VersionReq::parse("<= 1.0.0").unwrap(); |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Nov 25, 2017
Contributor
Let's use error_chain and replace unwrap() by ?. If the reader copy paste the snippet and modify the version with a wrong value, the program will exit with a message error more useful than a panic.
This comment has been minimized.
This comment has been minimized.
|
Hello @anna-liao Thank you for your PR! Here are some suggestions to help you improve your semver example:
|
ludwigpacifici
reviewed
Nov 25, 2017
| let vset = ["0.9.0", "1.0.0", "1.0.1"]; | ||
| let r = VersionReq::parse("<= 1.0.0").unwrap(); | ||
| for v in vset.iter() { |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Nov 25, 2017
Contributor
Let's try to make this for loop more Rust idiomatic by using iterators. For example:
filter_mapto convert validvsetstring versions tosemver::Versionfilterversions which does not match yourVersionReq- and use
maxto get the latest version from the range
anna-liao
force-pushed the
anna-liao:issue285
branch
2 times, most recently
from
95bce82
to
355cea1
Nov 26, 2017
This comment has been minimized.
This comment has been minimized.
|
@ludwigpacifici Thanks for the review! I have updated my PR, though have not added the pre-release tag yet, pending response from @budziq |
ludwigpacifici
reviewed
Nov 26, 2017
| use semver::Version; | ||
| use semver::VersionReq; | ||
| mod errors { |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Nov 26, 2017
Contributor
You can make your example more concise by removing mod errors scope.
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
reviewed
Nov 26, 2017
| let r = VersionReq::parse("<= 1.0.0") | ||
| .chain_err(|| "unable to parse VersionReq")?; | ||
| let vmax = vset.iter() |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Nov 26, 2017
Contributor
You can make your example more concise by removing vset and go ["0.9.0", "1.0.0", "1.0.1"].iter()...
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
budziq
Nov 27, 2017
Collaborator
also we can use slightly longer variable names to improve readabaility
ludwigpacifici
reviewed
Nov 26, 2017
| ## Find the latest version satisfying given range | ||
| [![semver-badge]][semver] [![cat-config-badge]][cat-config] | ||
| Given an array of unparsed version `&str`s, iterate and filter against a semver `VersionReq` and find the latest `Version`. |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Nov 26, 2017
•
Contributor
I would give more details in the description. For example, explain the range is obtained via the matches and the max.
This comment has been minimized.
This comment has been minimized.
budziq
Nov 27, 2017
Collaborator
Agreed. I'd remove the "an array" and "unparsed". I'd also add the hyperlinks to key identifiers like @ludwigpacifici suggests, Imho these would be VersionReq::parse, VersionReq::matches and optionally Version::parse.
Additionally "latest" is quite imprecise, I'd suggest rewriting it as 'satisfying the given range "<= 1.0.0"' .
This comment has been minimized.
This comment has been minimized.
|
Thanks @anna-liao. I added some more comments, but I would suggest you to wait for @budziq feedback and then proceed to the changes. I just saw he mentioned another solution (with sets) to implement it. Let's see which is more suitable for this example. |
budziq
requested changes
Nov 27, 2017
|
@anna-liao Thanks. Few suggestions. |
| use semver::Version; | ||
| use semver::VersionReq; | ||
| mod errors { |
This comment has been minimized.
This comment has been minimized.
| error_chain! { } | ||
| } | ||
| error_chain! { |
This comment has been minimized.
This comment has been minimized.
budziq
Nov 27, 2017
Collaborator
please checkout our note about error handling.
The error boilerplate (error definition and error-chain import) should be hidden with '#'
| error_chain! { | ||
| foreign_links { | ||
| SemVer(semver::SemVerError); |
This comment has been minimized.
This comment has been minimized.
| fn run() -> Result<()> { | ||
| let vset = ["0.9.0", "1.0.0", "1.0.1"]; | ||
| let r = VersionReq::parse("<= 1.0.0") | ||
| .chain_err(|| "unable to parse VersionReq")?; |
This comment has been minimized.
This comment has been minimized.
budziq
Nov 27, 2017
Collaborator
you can drop the chain_err if you add semver::ReqParseError to the foreign_links
| let r = VersionReq::parse("<= 1.0.0") | ||
| .chain_err(|| "unable to parse VersionReq")?; | ||
| let vmax = vset.iter() |
This comment has been minimized.
This comment has been minimized.
| extern crate error_chain; | ||
| extern crate semver; | ||
| use semver::Version; |
This comment has been minimized.
This comment has been minimized.
| .max() | ||
| .chain_err(|| "unable to find max Version")?; | ||
| assert_eq!(vmax, Version { major: 1, minor: 0, patch: 0, pre: vec!(), build: vec!() }); |
This comment has been minimized.
This comment has been minimized.
| let vmax = vset.iter() | ||
| .filter_map(|s| Version::parse(s).ok()) | ||
| .filter(|s| r.matches(&s)) |
This comment has been minimized.
This comment has been minimized.
| ## Find the latest version satisfying given range | ||
| [![semver-badge]][semver] [![cat-config-badge]][cat-config] | ||
| Given an array of unparsed version `&str`s, iterate and filter against a semver `VersionReq` and find the latest `Version`. |
This comment has been minimized.
This comment has been minimized.
budziq
Nov 27, 2017
Collaborator
Agreed. I'd remove the "an array" and "unparsed". I'd also add the hyperlinks to key identifiers like @ludwigpacifici suggests, Imho these would be VersionReq::parse, VersionReq::matches and optionally Version::parse.
Additionally "latest" is quite imprecise, I'd suggest rewriting it as 'satisfying the given range "<= 1.0.0"' .
This comment has been minimized.
This comment has been minimized.
|
Thanks for the reviews, @ludwigpacifici and @budziq ! Really appreciate you both taking the time to go over my PR with a fine tooth comb. I am having an issue while revising the PR: |
anna-liao
force-pushed the
anna-liao:issue285
branch
from
355cea1
to
fc7ddbe
Nov 27, 2017
ludwigpacifici
reviewed
Nov 27, 2017
| Given a list of version `&str`s, find the latest [`semver::Version`] that satisfies the given range | ||
| "<= 1.0.0". A [`semver::VersionReq`] is used to compare the parsed version to a | ||
| minimum version requirement. Filter for version `&str` that are verified as [`semver::Version`] with [`Version::parse`] and satisfies the [`semver::VersionReq`] using [`semver::VersionReq::matches`](https://docs.rs/semver/*/semver/struct.VersionReq.html#method.matches). Return the latest [`semver::Version`] using [`std::cmp::max`](https://doc.rust-lang.org/std/cmp/fn.max.html). |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Nov 27, 2017
Contributor
To ease readability, links are placed at the bottom of the file in alphabetic order.
No need for double space after a dot. Maybe I am too picky? :-)
- "requirement. Filter"
- "matches). Return"
This comment has been minimized.
This comment has been minimized.
Nice catch @anna-liao! I do not see something obvious in your PR that can explain that. Also, I can reproduce your issue with Firefox with different links:
With Chrome it is fine. Not sure if the issue is cookbook or mdbook related. @budziq, in case you missed it, did you have time to look at the below (from top of this thread)? :-)
|
anna-liao
force-pushed the
anna-liao:issue285
branch
from
fc7ddbe
to
f2167e1
Nov 27, 2017
This comment has been minimized.
This comment has been minimized.
I can reproduce it only on Firefox 57 which due to stylo is more prone to incomplete style flashes. What we experience here is firefox pointing to correct page location but then a highlight.js and mdbooks custom js kicks-in to style the code snippets and hide the lines starting with '#'. The links you are mentioning are in places particularly heavy with '#'. Hiding the '#' lines causes the page to get shorter but firefox viewport does not move anymore. I'm not versed in cross-browser compatible html+js+css so I cannot say if that this is a problem with either firefox or mdbook but it is certainly not a problem with the cookbook. I guess that we should start with posting the issue to mdbook bugtracker. Anyone care to do that? :)
I'm not particularly sure if we should focus on teaching semver intricacies as the mentioned behavior is just part of the standard but if we were able to present it in a compact form then I'm game! If we were to go that way I'd suggest to extract the comparing/searching logic into a function and call it two times (once on a dataset that we already have and once on a version with prerelease tag) |
budziq
requested changes
Nov 28, 2017
|
Minor suggestions below |
| Given a list of version `&str`s, find the latest [`semver::Version`] that satisfies the given range | ||
| "<= 1.0.0". A [`semver::VersionReq`] is used to compare the parsed version to a | ||
| minimum version requirement. Filter for version `&str` that are verified as [`semver::Version`] with [`Version::parse`] and satisfies the [`semver::VersionReq`] using [`VersionReq::matches`]. Return the latest [`semver::Version`] using [`max`]. |
This comment has been minimized.
This comment has been minimized.
budziq
Nov 28, 2017
Collaborator
I'd probably refrain from describing all of the code step by step (especially the last sentence might be omitted).
This comment has been minimized.
This comment has been minimized.
anna-liao
Nov 28, 2017
Author
Contributor
@budziq @ludwigpacifici I updated the description so that it is concise.
Regarding an example with the pre-release tags, I am working on that in a separate branch and will submit as a PR when it is working.
| [![semver-badge]][semver] [![cat-config-badge]][cat-config] | ||
| Given a list of version `&str`s, find the latest [`semver::Version`] that satisfies the given range | ||
| "<= 1.0.0". A [`semver::VersionReq`] is used to compare the parsed version to a |
This comment has been minimized.
This comment has been minimized.
budziq
Nov 28, 2017
Collaborator
I'd probably try to keep the description more compact. Haw about something along these lines?:
Given a list of version &strs, finds the latest [semver::Version] that satisfying given [semver::VersionReq] "<= 1.0.0" using [VersionReq::matches].
anna-liao
referenced this pull request
Nov 28, 2017
Open
bug: hyperlink to another section jumps to wrong location on Firefox 57 #499
anna-liao
force-pushed the
anna-liao:issue285
branch
2 times, most recently
from
75f982e
to
cec54b2
Nov 28, 2017
This comment has been minimized.
This comment has been minimized.
|
Thanks @anna-liao for raising an mdBook issue.
I raised this idea because I tried this example with these inputs |
anna-liao
force-pushed the
anna-liao:issue285
branch
from
cec54b2
to
9e3df3f
Nov 30, 2017
This comment has been minimized.
This comment has been minimized.
|
Added new version with pre-release example. r? @ludwigpacifici @budziq |
ludwigpacifici
reviewed
Nov 30, 2017
ludwigpacifici
reviewed
Nov 30, 2017
| "3.4.5-alpha.9", | ||
| ].into_iter() | ||
| )?, | ||
| Some(Version { |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Nov 30, 2017
Contributor
Since the expected result can be surprising, I wonder if a comment should be added like: "Shows Semver precedence for pre-release tags"
budziq
requested changes
Nov 30, 2017
|
few more suggestions |
| # } | ||
| # } | ||
| fn find_max_matching_version( |
This comment has been minimized.
This comment has been minimized.
budziq
Nov 30, 2017
Collaborator
If you go with signature such as this one:
fn find_max_matching_version<'a, I>(
version_req_str: &str,
iterable: I,
) -> Result<Option<Version>>
where
I: IntoIterator<Item = &'a str>,
{Then you will be able to omit both into_iter() calls below and
also rustfmt allows us to put whole function declaration in a single line so lets do it for readability.
This comment has been minimized.
This comment has been minimized.
anna-liao
Dec 2, 2017
•
Author
Contributor
@budziq When I run the code with this signature, I get this error:
error[E0599]: no method named `filter_map` found for type `I` in the current scope
--> src/main.rs:33:10
|
33 | .filter_map(|s| Version::parse(s).ok())
| ^^^^^^^^^^
|
= note: the method `filter_map` exists but the following trait bounds were not satisfied:
`&mut I : std::iter::Iterator`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `filter_map`, perhaps you need to implement it:
candidate #1: `std::iter::Iterator`
I couldn't figure out how to resolve this compiler error. Do I need to implement filter_map for I?
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Dec 2, 2017
Contributor
@anna-liao , have you tried to remove the two below into_iter() calls (in the asserts) and go iterable.into_iter().filter_map(... in find_max_matching_version function?
This comment has been minimized.
This comment has been minimized.
budziq
Dec 2, 2017
•
Collaborator
Do I need to implement filter_map for I?
Nope. Actually the error message could be a lot better here.
Let me try to explain what is going on here. IntoIterator is a trait bound here making the find_max_matching_version function generic over type implementing the trait. So it will accept now things like Vec, HashSet (and almost any other container) or any Iterator as long as their Item is &str.
But while all these types are accepted as the parameter, they are not an iterator yet (which itself implement filter_map). You will have to actually use the into_iter trait method to turn the argument into an Iterator.
This is how almost all conversion trait bounds are used (AsRef<T>, Into<T>, etc). You use the trait bound to accept a range of types and use the trait method inside the function to get parameter converted into the type you want. I hope this helps :)
This comment has been minimized.
This comment has been minimized.
anna-liao
Dec 2, 2017
Author
Contributor
@ludwigpacifici @budziq Ah thanks for the explanation! I have tested the code as suggested, and updated the PR
anna-liao
force-pushed the
anna-liao:issue285
branch
from
9e3df3f
to
8857d11
Dec 2, 2017
budziq
requested changes
Dec 2, 2017
|
Nice! One final nit and we are ready to merge! |
| extern crate semver; | ||
| use semver::{AlphaNumeric, Numeric, Version, VersionReq}; | ||
| use std::vec::IntoIter; |
This comment has been minimized.
This comment has been minimized.
| "3.4.5-alpha.9", | ||
| ] | ||
| )?, | ||
| Some(Version { |
This comment has been minimized.
This comment has been minimized.
budziq
Dec 2, 2017
Collaborator
I'm not sure if we should not replace this multiline Some(Version{...}) with just Some(Version::parse("1.0.0")?) for brevity in both cases (also reducing number of required imports). But it will not be a disaster if we leave it as it is. What are your thoughts on it @anna-liao , @ludwigpacifici ?
anna-liao
force-pushed the
anna-liao:issue285
branch
from
8857d11
to
b4bf27a
Dec 3, 2017
This comment has been minimized.
This comment has been minimized.
|
@budziq I think that's a good idea, as it is more concise and readable. I have updated the PR with the revision. |
This comment has been minimized.
This comment has been minimized.
|
I have one question below, otherwise looks good to me! |
ludwigpacifici
reviewed
Dec 3, 2017
| # } | ||
| # } | ||
| fn find_max_matching_version<'a, I>(version_req_str: &str, iterable: I) -> Result<Option<Version>> |
This comment has been minimized.
This comment has been minimized.
ludwigpacifici
Dec 3, 2017
Contributor
The semantic of the return type Result<Option<T>> looks off to me. However, I have no better suggestion. Does it mean something specific?
This comment has been minimized.
This comment has been minimized.
budziq
Dec 3, 2017
Collaborator
Does it mean something specific?
Yep. Typically Result is used to handle possibility of error while Option expresses possibility of nonexistence. So as long as nonexistence of searched value is not considered a hard error it would be best expressed as an Option while having to deal with parsing errors leaves us with Result so Result<Option<T>> gives the reader of our API a clear indication that the operation can both fail as well as yield no usable return value even if there is no error.
This comment has been minimized.
This comment has been minimized.
budziq
merged commit 0b3d6ff
into
rust-lang-nursery:master
Dec 3, 2017
This comment has been minimized.
This comment has been minimized.
|
Nicely done @anna-liao ! |
This comment has been minimized.
This comment has been minimized.
|
Well done @anna-liao ! |
anna-liao
deleted the
anna-liao:issue285
branch
Dec 3, 2017
This comment has been minimized.
This comment has been minimized.
|
Thanks @budziq and @ludwigpacifici :) |
anna-liao commentedNov 25, 2017
fixes #285 r? @budziq