diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/rustc-semver | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rustc-semver')
-rw-r--r-- | vendor/rustc-semver/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/rustc-semver/CODE_OF_CONDUCT.md | 78 | ||||
-rw-r--r-- | vendor/rustc-semver/COPYRIGHT | 6 | ||||
-rw-r--r-- | vendor/rustc-semver/Cargo.toml | 24 | ||||
-rw-r--r-- | vendor/rustc-semver/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | vendor/rustc-semver/LICENSE-MIT | 21 | ||||
-rw-r--r-- | vendor/rustc-semver/README.md | 60 | ||||
-rw-r--r-- | vendor/rustc-semver/cov.sh | 34 | ||||
-rw-r--r-- | vendor/rustc-semver/rust-toolchain | 1 | ||||
-rw-r--r-- | vendor/rustc-semver/src/lib.rs | 665 |
10 files changed, 1091 insertions, 0 deletions
diff --git a/vendor/rustc-semver/.cargo-checksum.json b/vendor/rustc-semver/.cargo-checksum.json new file mode 100644 index 000000000..5b5ebc2e6 --- /dev/null +++ b/vendor/rustc-semver/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CODE_OF_CONDUCT.md":"db2ffb3942498eedec1424ae1d7fcb1b86563a008a6312f9d04a8f6aa11d2077","COPYRIGHT":"acc1fde07c321191aa7ae8d0f51f85c47d1d927da0ee23fcab74876079a893a2","Cargo.toml":"c935404a26ff20dd1c99bb21a6a7ba05eb322ab4fae89803378207acf43c1c8c","LICENSE-APACHE":"042bdce5c48778791dc426d40035721df1331c79daf7da306f6904cc76b471fa","LICENSE-MIT":"f60a51f5a545fcf856eca49cdb61c7b54af862889a91b4c973cbd8013d86ef1d","README.md":"314996b2c7492689cdd3da63d8bb06a7246af9d2d2c7765e180ff1d779b0f22d","cov.sh":"db16152c8992f9291076f35083b17141f48b246638f2a9015d435f3773c2554e","rust-toolchain":"2b92ea252be0fbc26f70317cdaa7b6411ea634b50d55338cd8c495e4dbf25d1d","src/lib.rs":"978fc659a78f71e7a9f0b15fbdbf9113b2ad5ac30718d97fcde782ae0bfbc05d"},"package":"5be1bdc7edf596692617627bbfeaba522131b18e06ca4df2b6b689e3c5d5ce84"}
\ No newline at end of file diff --git a/vendor/rustc-semver/CODE_OF_CONDUCT.md b/vendor/rustc-semver/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..ff38ab01b --- /dev/null +++ b/vendor/rustc-semver/CODE_OF_CONDUCT.md @@ -0,0 +1,78 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and our +community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and +expression, level of experience, education, socio-economic status, nationality, +personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at hello@flip1995.com. All complaints +will be reviewed and investigated and will result in a response that is deemed +necessary and appropriate to the circumstances. The project team is obligated to +maintain confidentiality with regard to the reporter of an incident. Further +details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/vendor/rustc-semver/COPYRIGHT b/vendor/rustc-semver/COPYRIGHT new file mode 100644 index 000000000..a0bca23e9 --- /dev/null +++ b/vendor/rustc-semver/COPYRIGHT @@ -0,0 +1,6 @@ +Copyright 2020 Philipp Krones + +Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +https://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +https://opensource.org/licenses/MIT>, at your option. Files in the project may +not be copied, modified, or distributed except according to those terms. diff --git a/vendor/rustc-semver/Cargo.toml b/vendor/rustc-semver/Cargo.toml new file mode 100644 index 000000000..0996d5189 --- /dev/null +++ b/vendor/rustc-semver/Cargo.toml @@ -0,0 +1,24 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "rustc-semver" +version = "1.1.0" +authors = ["flip1995 <hello@philkrones.com>"] +description = "Crate for parsing versions of Rust releases" +keywords = ["parser", "version", "semver", "rustc"] +categories = ["parsing", "config"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/flip1995/rustc-semver" + +[dependencies] diff --git a/vendor/rustc-semver/LICENSE-APACHE b/vendor/rustc-semver/LICENSE-APACHE new file mode 100644 index 000000000..87ab8aa4d --- /dev/null +++ b/vendor/rustc-semver/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Philipp Krones + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/rustc-semver/LICENSE-MIT b/vendor/rustc-semver/LICENSE-MIT new file mode 100644 index 000000000..76c1579fb --- /dev/null +++ b/vendor/rustc-semver/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Philipp Krones + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/rustc-semver/README.md b/vendor/rustc-semver/README.md new file mode 100644 index 000000000..d5ee06909 --- /dev/null +++ b/vendor/rustc-semver/README.md @@ -0,0 +1,60 @@ +[![Coverage](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=coverage&query=%24.data%5B0%5D.totals.lines.percent&suffix=%25&url=https%3A%2F%2Fraw.githubusercontent.com%2Fflip1995%2Frustc-semver%2Fgh-pages%2Fcov.json)](https://flip1995.github.io/rustc-semver/) +[![Tests](https://github.com/flip1995/rustc-semver/workflows/Tests/badge.svg)](https://github.com/flip1995/rustc-semver/actions?query=branch%3Amaster+event%3Apush+workflow%3ATests) + +# Rustc Semver + +This crate provides a minimalistic parser for Rust versions. + +## Description + +The parser will only accept Versions in the form + +```text +<major>.<minor>.<patch> +``` + +and 3 special versions: + +- `1.0.0-alpha` +- `1.0.0-alpha.2` +- `1.0.0-beta` + +This covers every version of `rustc` that were released to date. + +## Usage + +There are 2 functions to create a `RustcVersion`: + +1. `const RustcVersion::new(u32, u32, u32)`: This is mainly used to create + constants +2. `RustcVersion::parse(&str)`: Usually you want to parse a version with this + function + +If you have a `RustcVersion` you can compare them, like you would expect: + +```rust +assert!(RustcVersion::parse("1.42.0")? < RustcVersion::parse("1.43")?); +``` + +If you want to check whether one version meets another version according to the +[Caret Requirements], there is the method `RustcVersion::meets`: + +```rust +assert!(RustcVersion::new(1, 48, 0).meets(RustcVersion::parse("1.42")?)); +``` + +[Caret Requirements]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements + +## Code of Conduct + +This repository adopts the [Contributor Covenant Code of +Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) + +## License + +Copyright 2020 Philipp Krones + +Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +https://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +https://opensource.org/licenses/MIT>, at your option. Files in the project may +not be copied, modified, or distributed except according to those terms. diff --git a/vendor/rustc-semver/cov.sh b/vendor/rustc-semver/cov.sh new file mode 100644 index 000000000..e3b42c7e8 --- /dev/null +++ b/vendor/rustc-semver/cov.sh @@ -0,0 +1,34 @@ +cargo clean +rm -rf cov/rustc-semver/ +rm -rf cov/* + +RUSTFLAGS="-Zinstrument-coverage" \ +LLVM_PROFILE_FILE="$(pwd)/cov/rustc-semver%m.profraw" \ + cargo +nightly test + +llvm-profdata merge -sparse cov/rustc-semver*.profraw -o cov/rustc-semver.profdata + +case $1 in + "--json") + llvm-cov export \ + --instr-profile=cov/rustc-semver.profdata \ + --summary-only \ + --format=text \ + $(find target/debug/deps -executable -type f) | python3 -m json.tool > cov.json + ;; + "--html") + cargo install rustfilt + llvm-cov show \ + --instr-profile=cov/rustc-semver.profdata \ + --Xdemangler=rustfilt \ + --show-line-counts-or-regions \ + --output-dir=cov/rustc-semver \ + --format=html \ + $(find target/debug/deps -executable -type f) + ;; + *) + echo "Use with either --html or --json" + exit 1 + ;; +esac + diff --git a/vendor/rustc-semver/rust-toolchain b/vendor/rustc-semver/rust-toolchain new file mode 100644 index 000000000..2bf5ad044 --- /dev/null +++ b/vendor/rustc-semver/rust-toolchain @@ -0,0 +1 @@ +stable diff --git a/vendor/rustc-semver/src/lib.rs b/vendor/rustc-semver/src/lib.rs new file mode 100644 index 000000000..86396e7b6 --- /dev/null +++ b/vendor/rustc-semver/src/lib.rs @@ -0,0 +1,665 @@ +#![no_std] + +use core::{cmp::Ordering, fmt::Display, num::ParseIntError}; + +/// `Error` represents an Error during parsing of a [`RustcVersion`]. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum Error { + /// A version was passed that has too many elements seperated by `'.'`. + TooManyElements, + /// A version was passed that neither is a [`SpecialVersion`], nor a + /// normal [`RustcVersion`]. + NotASpecialVersion, + /// A version was passed, that was either an empty string or a part of the + /// version was left out, e.g. `1. .3` + EmptyVersionPart, + /// A version was passed that has unallowed chracters. + ParseIntError, +} + +impl From<ParseIntError> for Error { + fn from(_: ParseIntError) -> Self { + Self::ParseIntError + } +} + +/// Result type for this crate +pub type Result<T> = core::result::Result<T, Error>; + +/// `RustcVersion` represents a version of the Rust Compiler. +/// +/// This struct only supports the [`NormalVersion`] format +/// ```text +/// major.minor.patch +/// ``` +/// and 3 special formats represented by the [`SpecialVersion`] enum. +/// +/// A version can be created with one of the functions [`RustcVersion::new`] or +/// [`RustcVersion::parse`]. The [`RustcVersion::new`] method only supports the +/// normal version format. +/// +/// You can compare two versions, just as you would expect: +/// +/// ```rust +/// use rustc_semver::RustcVersion; +/// +/// assert!(RustcVersion::new(1, 34, 0) > RustcVersion::parse("1.10").unwrap()); +/// assert!(RustcVersion::new(1, 34, 0) > RustcVersion::parse("0.9").unwrap()); +/// ``` +/// +/// This comparison is semver conform according to the [semver definition of +/// precedence]. However, if you want to check whether one version meets +/// another version according to the [Caret Requirements], you should use +/// [`RustcVersion::meets`]. +/// +/// [Caret Requirements]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements +/// [semver definition of precedence]: https://semver.org/#spec-item-11 +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum RustcVersion { + Normal(NormalVersion), + Special(SpecialVersion), +} + +/// `NormalVersion` represents a normal version used for all releases since +/// Rust 1.0.0. +/// +/// This struct supports versions in the format +/// ```test +/// major.minor.patch +/// ``` +#[derive(Debug, Copy, Clone)] +pub struct NormalVersion { + major: u32, + minor: u32, + patch: u32, + omitted: OmittedParts, +} + +#[derive(Debug, Copy, Clone)] +enum OmittedParts { + None, + Minor, + Patch, +} + +impl From<usize> for OmittedParts { + fn from(parts: usize) -> Self { + match parts { + 1 => Self::Minor, + 2 => Self::Patch, + 3 => Self::None, + _ => unreachable!( + "This function should never be called with `parts == 0` or `parts > 3`" + ), + } + } +} + +/// `SpecialVersion` represents a special version from the first releases. +/// +/// Before Rust 1.0.0, there were two alpha and one beta release, namely +/// +/// - `1.0.0-alpha` +/// - `1.0.0-alpha.2` +/// - `1.0.0-beta` +/// +/// This enum represents those releases. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum SpecialVersion { + Alpha, + Alpha2, + Beta, +} + +impl PartialOrd for RustcVersion { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for RustcVersion { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Normal(ver), Self::Normal(o_ver)) => ver.cmp(o_ver), + (Self::Normal(NormalVersion { major, .. }), Self::Special(_)) => { + if *major >= 1 { + Ordering::Greater + } else { + Ordering::Less + } + } + (Self::Special(_), Self::Normal(NormalVersion { major, .. })) => { + if *major >= 1 { + Ordering::Less + } else { + Ordering::Greater + } + } + (Self::Special(s_ver), Self::Special(o_s_ver)) => s_ver.cmp(o_s_ver), + } + } +} + +impl PartialOrd for NormalVersion { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for NormalVersion { + fn cmp(&self, other: &Self) -> Ordering { + match self.major.cmp(&other.major) { + Ordering::Equal => match self.minor.cmp(&other.minor) { + Ordering::Equal => self.patch.cmp(&other.patch), + ord => ord, + }, + ord => ord, + } + } +} + +impl PartialEq for NormalVersion { + fn eq(&self, other: &Self) -> bool { + self.major == other.major && self.minor == other.minor && self.patch == other.patch + } +} + +impl Eq for NormalVersion {} + +impl PartialOrd for SpecialVersion { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for SpecialVersion { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Alpha, Self::Alpha) + | (Self::Alpha2, Self::Alpha2) + | (Self::Beta, Self::Beta) => Ordering::Equal, + (Self::Alpha, _) | (Self::Alpha2, Self::Beta) => Ordering::Less, + (Self::Beta, _) | (Self::Alpha2, Self::Alpha) => Ordering::Greater, + } + } +} + +impl Display for RustcVersion { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Normal(NormalVersion { + major, + minor, + patch, + .. + }) => write!(f, "{}.{}.{}", major, minor, patch), + Self::Special(special) => write!(f, "{}", special), + } + } +} + +impl Display for SpecialVersion { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Alpha => write!(f, "1.0.0-alpha"), + Self::Alpha2 => write!(f, "1.0.0-alpha.2"), + Self::Beta => write!(f, "1.0.0-beta"), + } + } +} + +impl From<[u32; 3]> for NormalVersion { + fn from(arr: [u32; 3]) -> Self { + NormalVersion { + major: arr[0], + minor: arr[1], + patch: arr[2], + omitted: OmittedParts::None, + } + } +} + +const ACCEPTED_SPECIAL_VERSIONS: [(&str, SpecialVersion); 3] = [ + ("1.0.0-alpha", SpecialVersion::Alpha), + ("1.0.0-alpha.2", SpecialVersion::Alpha2), + ("1.0.0-beta", SpecialVersion::Beta), +]; + +impl RustcVersion { + /// `RustcVersion::new` is a `const` constructor for a `RustcVersion`. + /// + /// This function is primarily used to construct constants, for everything + /// else use [`RustcVersion::parse`]. + /// + /// This function only allows to construct normal versions. For special + /// versions, construct them directly with the [`SpecialVersion`] enum. + /// + /// # Examples + /// + /// ```rust + /// use rustc_semver::RustcVersion; + /// + /// const MY_FAVORITE_RUST: RustcVersion = RustcVersion::new(1, 48, 0); + /// + /// assert!(MY_FAVORITE_RUST > RustcVersion::new(1, 0, 0)) + /// ``` + pub const fn new(major: u32, minor: u32, patch: u32) -> Self { + Self::Normal(NormalVersion { + major, + minor, + patch, + omitted: OmittedParts::None, + }) + } + + /// `RustcVersion::parse` parses a [`RustcVersion`]. + /// + /// This function can parse all normal and special versions. It is possbile + /// to omit parts of the version, like the patch or minor version part. So + /// `1`, `1.0`, and `1.0.0` are all valid inputs and will result in the + /// same version. + /// + /// # Errors + /// + /// This function returns an [`Error`], if the passed string is not a valid + /// [`RustcVersion`] + /// + /// # Examples + /// + /// ```rust + /// use rustc_semver::{SpecialVersion, RustcVersion}; + /// + /// let ver = RustcVersion::new(1, 0, 0); + /// + /// assert_eq!(RustcVersion::parse("1").unwrap(), ver); + /// assert_eq!(RustcVersion::parse("1.0").unwrap(), ver); + /// assert_eq!(RustcVersion::parse("1.0.0").unwrap(), ver); + /// assert_eq!( + /// RustcVersion::parse("1.0.0-alpha").unwrap(), + /// RustcVersion::Special(SpecialVersion::Alpha) + /// ); + /// ``` + pub fn parse(version: &str) -> Result<Self> { + let special_version = ACCEPTED_SPECIAL_VERSIONS.iter().find_map(|sv| { + if version == sv.0 { + Some(sv.1) + } else { + None + } + }); + if let Some(special_version) = special_version { + return Ok(RustcVersion::Special(special_version)); + } + + let mut rustc_version = [0_u32; 3]; + let mut parts = 0; + for (i, part) in version.split('.').enumerate() { + let part = part.trim(); + if part.is_empty() { + return Err(Error::EmptyVersionPart); + } + if i == 3 { + return Err(Error::TooManyElements); + } + match str::parse(part) { + Ok(part) => rustc_version[i] = part, + Err(e) => { + if i == 2 { + return Err(Error::NotASpecialVersion); + } else { + return Err(e.into()); + } + } + } + + parts = i + 1; + } + + let mut ver = NormalVersion::from(rustc_version); + ver.omitted = OmittedParts::from(parts); + Ok(RustcVersion::Normal(ver)) + } + + /// `RustcVersion::meets` implements a semver conform version check + /// according to the [Caret Requirements]. + /// + /// Note that [`SpecialVersion`]s only meet themself and no other version + /// meets a [`SpecialVersion`]. This is because [according to semver], + /// special versions are considered unstable and "might not satisfy the + /// intended compatibility requirements as denoted by \[their\] associated + /// normal version". + /// + /// # Examples + /// + /// ```rust + /// use rustc_semver::RustcVersion; + /// + /// assert!(RustcVersion::new(1, 30, 0).meets(RustcVersion::parse("1.29").unwrap())); + /// assert!(!RustcVersion::new(1, 30, 0).meets(RustcVersion::parse("1.31").unwrap())); + /// + /// assert!(RustcVersion::new(0, 2, 1).meets(RustcVersion::parse("0.2").unwrap())); + /// assert!(!RustcVersion::new(0, 3, 0).meets(RustcVersion::parse("0.2").unwrap())); + /// ``` + /// + /// [Caret Requirements]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements + /// [according to semver]: https://semver.org/#spec-item-9 + pub fn meets(self, other: Self) -> bool { + match (self, other) { + (RustcVersion::Special(_), _) | (_, RustcVersion::Special(_)) => self == other, + (RustcVersion::Normal(ver), RustcVersion::Normal(o_ver)) => { + // In any case must `self` be bigger than `other`, with the major part matching + // the other version. + let mut meets = ver >= o_ver && ver.major == o_ver.major; + + // In addition, the left-most non-zero digit must not be modified. + match o_ver.omitted { + OmittedParts::None => { + // Nothing was omitted, this means that everything must match in case of + // leading zeros. + if o_ver.major == 0 { + // Leading 0 in major position, check for + // `self.minor == other.minor` + meets &= ver.minor == o_ver.minor; + + if o_ver.minor == 0 { + // Leading 0 in minor position, check for + // `self.patch == other.patch`. + meets &= ver.patch == o_ver.patch; + } + } + } + OmittedParts::Patch => { + // The patch version was omitted, this means the patch version of `self` + // does not have to match the patch version of `other`. + if o_ver.major == 0 { + meets &= ver.minor == o_ver.minor; + } + } + OmittedParts::Minor => { + // The minor (and patch) version was omitted, this means + // the minor and patch version of `self` do not have to + // match the minor and patch version of `other` + } + } + + meets + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn omitted_parts() { + assert_eq!( + RustcVersion::parse("1.0.0").unwrap(), + RustcVersion::new(1, 0, 0) + ); + assert_eq!( + RustcVersion::parse("1.0").unwrap(), + RustcVersion::new(1, 0, 0) + ); + assert_eq!( + RustcVersion::parse("1").unwrap(), + RustcVersion::new(1, 0, 0) + ); + } + + #[test] + fn special_versions() { + assert_eq!( + RustcVersion::parse("1.0.0-alpha").unwrap(), + RustcVersion::Special(SpecialVersion::Alpha) + ); + assert_eq!( + RustcVersion::parse("1.0.0-alpha.2").unwrap(), + RustcVersion::Special(SpecialVersion::Alpha2) + ); + assert_eq!( + RustcVersion::parse("1.0.0-beta").unwrap(), + RustcVersion::Special(SpecialVersion::Beta) + ); + assert_eq!( + RustcVersion::parse("1.0.0-sigma"), + Err(Error::NotASpecialVersion) + ); + assert_eq!( + RustcVersion::parse("1.0.0beta"), + Err(Error::NotASpecialVersion) + ); + assert_eq!( + RustcVersion::parse("1.1.0-beta"), + Err(Error::NotASpecialVersion) + ); + } + + #[test] + fn less_than() { + let bigger = RustcVersion::new(1, 30, 1); + assert!(RustcVersion::parse("1.0.0").unwrap() < bigger); + assert!(RustcVersion::parse("1.0").unwrap() < bigger); + assert!(RustcVersion::parse("1").unwrap() < bigger); + assert!(RustcVersion::parse("1.30").unwrap() < bigger); + assert!(RustcVersion::parse("1.0.0-beta").unwrap() < bigger); + assert!(RustcVersion::parse("0.9").unwrap() < RustcVersion::Special(SpecialVersion::Alpha)); + assert!( + RustcVersion::parse("1.0.0-alpha").unwrap() + < RustcVersion::Special(SpecialVersion::Alpha2) + ); + assert!( + RustcVersion::parse("1.0.0-alpha").unwrap() + < RustcVersion::Special(SpecialVersion::Beta) + ); + assert!( + RustcVersion::parse("1.0.0-alpha.2").unwrap() + < RustcVersion::Special(SpecialVersion::Beta) + ); + } + + #[test] + fn equal() { + assert_eq!( + RustcVersion::parse("1.22.0").unwrap(), + RustcVersion::new(1, 22, 0) + ); + assert_eq!( + RustcVersion::parse("1.22").unwrap(), + RustcVersion::new(1, 22, 0) + ); + assert_eq!( + RustcVersion::parse("1.48.1").unwrap(), + RustcVersion::new(1, 48, 1) + ); + assert_eq!( + RustcVersion::parse("1.0.0-alpha") + .unwrap() + .cmp(&RustcVersion::Special(SpecialVersion::Alpha)), + Ordering::Equal + ); + assert_eq!( + RustcVersion::parse("1.0.0-alpha.2") + .unwrap() + .cmp(&RustcVersion::Special(SpecialVersion::Alpha2)), + Ordering::Equal + ); + assert_eq!( + RustcVersion::parse("1.0.0-beta") + .unwrap() + .cmp(&RustcVersion::Special(SpecialVersion::Beta)), + Ordering::Equal + ); + } + + #[test] + fn greater_than() { + let less = RustcVersion::new(1, 15, 1); + assert!(RustcVersion::parse("1.16.0").unwrap() > less); + assert!(RustcVersion::parse("1.16").unwrap() > less); + assert!(RustcVersion::parse("2").unwrap() > less); + assert!(RustcVersion::parse("1.15.2").unwrap() > less); + assert!( + RustcVersion::parse("1.0.0-beta").unwrap() + > RustcVersion::Special(SpecialVersion::Alpha2) + ); + assert!( + RustcVersion::parse("1.0.0-beta").unwrap() + > RustcVersion::Special(SpecialVersion::Alpha) + ); + assert!( + RustcVersion::parse("1.0.0-alpha.2").unwrap() + > RustcVersion::Special(SpecialVersion::Alpha) + ); + assert!(RustcVersion::parse("1.0.0-alpha.2").unwrap() > RustcVersion::new(0, 8, 0)); + assert!( + RustcVersion::parse("1.45.2").unwrap() > RustcVersion::Special(SpecialVersion::Alpha2) + ); + } + + #[test] + fn edge_cases() { + assert_eq!(RustcVersion::parse(""), Err(Error::EmptyVersionPart)); + assert_eq!(RustcVersion::parse(" "), Err(Error::EmptyVersionPart)); + assert_eq!(RustcVersion::parse("\t"), Err(Error::EmptyVersionPart)); + assert_eq!(RustcVersion::parse("1."), Err(Error::EmptyVersionPart)); + assert_eq!(RustcVersion::parse("1. "), Err(Error::EmptyVersionPart)); + assert_eq!(RustcVersion::parse("1.\t"), Err(Error::EmptyVersionPart)); + assert_eq!(RustcVersion::parse("1. \t.3"), Err(Error::EmptyVersionPart)); + assert_eq!( + RustcVersion::parse(" 1 . \t 3.\r 5").unwrap(), + RustcVersion::new(1, 3, 5) + ); + } + + #[test] + fn formatting() { + extern crate alloc; + use alloc::string::{String, ToString}; + assert_eq!( + RustcVersion::new(1, 42, 28).to_string(), + String::from("1.42.28") + ); + assert_eq!( + RustcVersion::Special(SpecialVersion::Alpha).to_string(), + String::from("1.0.0-alpha") + ); + assert_eq!( + RustcVersion::Special(SpecialVersion::Alpha2).to_string(), + String::from("1.0.0-alpha.2") + ); + assert_eq!( + RustcVersion::Special(SpecialVersion::Beta).to_string(), + String::from("1.0.0-beta") + ); + } + + #[test] + fn too_many_elements() { + assert_eq!( + RustcVersion::parse("1.0.0.100"), + Err(Error::TooManyElements) + ); + } + + #[test] + fn alpha_numeric_version() { + assert_eq!(RustcVersion::parse("a.0.1"), Err(Error::ParseIntError)); + assert_eq!(RustcVersion::parse("2.x.1"), Err(Error::ParseIntError)); + assert_eq!(RustcVersion::parse("0.2.s"), Err(Error::NotASpecialVersion)); + } + + #[test] + fn meets_full() { + // Nothing was omitted + assert!(RustcVersion::new(1, 2, 3).meets(RustcVersion::new(1, 2, 3))); + assert!(RustcVersion::new(1, 2, 5).meets(RustcVersion::new(1, 2, 3))); + assert!(RustcVersion::new(1, 3, 0).meets(RustcVersion::new(1, 2, 3))); + assert!(!RustcVersion::new(2, 0, 0).meets(RustcVersion::new(1, 2, 3))); + assert!(!RustcVersion::new(0, 9, 0).meets(RustcVersion::new(1, 0, 0))); + + assert!(RustcVersion::new(0, 2, 3).meets(RustcVersion::new(0, 2, 3))); + assert!(RustcVersion::new(0, 2, 5).meets(RustcVersion::new(0, 2, 3))); + assert!(!RustcVersion::new(0, 3, 0).meets(RustcVersion::new(0, 2, 3))); + assert!(!RustcVersion::new(1, 0, 0).meets(RustcVersion::new(0, 2, 3))); + + assert!(RustcVersion::new(0, 0, 3).meets(RustcVersion::new(0, 0, 3))); + assert!(!RustcVersion::new(0, 0, 5).meets(RustcVersion::new(0, 0, 3))); + assert!(!RustcVersion::new(0, 1, 0).meets(RustcVersion::new(0, 0, 3))); + + assert!(RustcVersion::new(0, 0, 0).meets(RustcVersion::new(0, 0, 0))); + assert!(!RustcVersion::new(0, 0, 1).meets(RustcVersion::new(0, 0, 0))); + } + + #[test] + fn meets_no_patch() { + // Patch was omitted + assert!(RustcVersion::new(1, 2, 0).meets(RustcVersion::parse("1.2").unwrap())); + assert!(RustcVersion::new(1, 2, 5).meets(RustcVersion::parse("1.2").unwrap())); + assert!(RustcVersion::new(1, 3, 0).meets(RustcVersion::parse("1.2").unwrap())); + assert!(!RustcVersion::new(2, 0, 0).meets(RustcVersion::parse("1.2").unwrap())); + assert!(!RustcVersion::new(0, 9, 0).meets(RustcVersion::parse("1.0").unwrap())); + + assert!(RustcVersion::new(0, 2, 0).meets(RustcVersion::parse("0.2").unwrap())); + assert!(RustcVersion::new(0, 2, 5).meets(RustcVersion::parse("0.2").unwrap())); + assert!(!RustcVersion::new(0, 3, 0).meets(RustcVersion::parse("0.2").unwrap())); + assert!(!RustcVersion::new(1, 0, 0).meets(RustcVersion::parse("0.2").unwrap())); + + assert!(RustcVersion::new(0, 0, 0).meets(RustcVersion::parse("0.0").unwrap())); + assert!(RustcVersion::new(0, 0, 5).meets(RustcVersion::parse("0.0").unwrap())); + assert!(!RustcVersion::new(0, 1, 0).meets(RustcVersion::parse("0.0").unwrap())); + } + + #[test] + fn meets_no_minor() { + // Minor was omitted + assert!(RustcVersion::new(1, 0, 0).meets(RustcVersion::parse("1").unwrap())); + assert!(RustcVersion::new(1, 3, 0).meets(RustcVersion::parse("1").unwrap())); + assert!(!RustcVersion::new(2, 0, 0).meets(RustcVersion::parse("1").unwrap())); + assert!(!RustcVersion::new(0, 9, 0).meets(RustcVersion::parse("1").unwrap())); + + assert!(RustcVersion::new(0, 0, 0).meets(RustcVersion::parse("0").unwrap())); + assert!(RustcVersion::new(0, 0, 1).meets(RustcVersion::parse("0").unwrap())); + assert!(RustcVersion::new(0, 2, 5).meets(RustcVersion::parse("0").unwrap())); + assert!(!RustcVersion::new(1, 0, 0).meets(RustcVersion::parse("0").unwrap())); + } + + #[test] + fn meets_special() { + assert!(RustcVersion::Special(SpecialVersion::Alpha) + .meets(RustcVersion::Special(SpecialVersion::Alpha))); + assert!(RustcVersion::Special(SpecialVersion::Alpha2) + .meets(RustcVersion::Special(SpecialVersion::Alpha2))); + assert!(RustcVersion::Special(SpecialVersion::Beta) + .meets(RustcVersion::Special(SpecialVersion::Beta))); + assert!(!RustcVersion::Special(SpecialVersion::Alpha) + .meets(RustcVersion::Special(SpecialVersion::Alpha2))); + assert!(!RustcVersion::Special(SpecialVersion::Alpha) + .meets(RustcVersion::Special(SpecialVersion::Beta))); + assert!(!RustcVersion::Special(SpecialVersion::Alpha2) + .meets(RustcVersion::Special(SpecialVersion::Beta))); + assert!(!RustcVersion::Special(SpecialVersion::Alpha).meets(RustcVersion::new(1, 0, 0))); + assert!(!RustcVersion::Special(SpecialVersion::Alpha2).meets(RustcVersion::new(1, 0, 0))); + assert!(!RustcVersion::Special(SpecialVersion::Beta).meets(RustcVersion::new(1, 0, 0))); + assert!(!RustcVersion::new(1, 0, 0).meets(RustcVersion::Special(SpecialVersion::Alpha))); + assert!(!RustcVersion::new(1, 0, 0).meets(RustcVersion::Special(SpecialVersion::Alpha2))); + assert!(!RustcVersion::new(1, 0, 0).meets(RustcVersion::Special(SpecialVersion::Beta))); + } + + #[test] + #[should_panic( + expected = "This function should never be called with `parts == 0` or `parts > 3`" + )] + fn omitted_parts_with_zero() { + OmittedParts::from(0); + } + + #[test] + #[should_panic( + expected = "This function should never be called with `parts == 0` or `parts > 3`" + )] + fn omitted_parts_with_four() { + OmittedParts::from(4); + } +} |