semver/
lib.rs

1//! [![github]](https://github.com/dtolnay/semver) [![crates-io]](https://crates.io/crates/semver) [![docs-rs]](https://docs.rs/semver)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! A parser and evaluator for Cargo's flavor of Semantic Versioning.
10//!
11//! Semantic Versioning (see <https://semver.org>) is a guideline for how
12//! version numbers are assigned and incremented. It is widely followed within
13//! the Cargo/crates.io ecosystem for Rust.
14//!
15//! <br>
16//!
17//! # Example
18//!
19//! ```
20//! use semver::{BuildMetadata, Prerelease, Version, VersionReq};
21//!
22//! fn main() {
23//!     let req = VersionReq::parse(">=1.2.3, <1.8.0").unwrap();
24//!
25//!     // Check whether this requirement matches version 1.2.3-alpha.1 (no)
26//!     let version = Version {
27//!         major: 1,
28//!         minor: 2,
29//!         patch: 3,
30//!         pre: Prerelease::new("alpha.1").unwrap(),
31//!         build: BuildMetadata::EMPTY,
32//!     };
33//!     assert!(!req.matches(&version));
34//!
35//!     // Check whether it matches 1.3.0 (yes it does)
36//!     let version = Version::parse("1.3.0").unwrap();
37//!     assert!(req.matches(&version));
38//! }
39//! ```
40//!
41//! <br><br>
42//!
43//! # Scope of this crate
44//!
45//! Besides Cargo, several other package ecosystems and package managers for
46//! other languages also use SemVer:&ensp;RubyGems/Bundler for Ruby, npm for
47//! JavaScript, Composer for PHP, CocoaPods for Objective-C...
48//!
49//! The `semver` crate is specifically intended to implement Cargo's
50//! interpretation of Semantic Versioning.
51//!
52//! Where the various tools differ in their interpretation or implementation of
53//! the spec, this crate follows the implementation choices made by Cargo. If
54//! you are operating on version numbers from some other package ecosystem, you
55//! will want to use a different semver library which is appropriate to that
56//! ecosystem.
57//!
58//! The extent of Cargo's SemVer support is documented in the *[Specifying
59//! Dependencies]* chapter of the Cargo reference.
60//!
61//! [Specifying Dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
62
63#![doc(html_root_url = "https://docs.rs/semver/1.0.27")]
64#![cfg_attr(docsrs, feature(doc_cfg))]
65#![cfg_attr(not(feature = "std"), no_std)]
66#![deny(unsafe_op_in_unsafe_fn)]
67#![allow(
68    clippy::cast_lossless,
69    clippy::cast_possible_truncation,
70    clippy::checked_conversions,
71    clippy::doc_markdown,
72    clippy::incompatible_msrv,
73    clippy::items_after_statements,
74    clippy::manual_map,
75    clippy::manual_range_contains,
76    clippy::match_bool,
77    clippy::missing_errors_doc,
78    clippy::must_use_candidate,
79    clippy::needless_doctest_main,
80    clippy::ptr_as_ptr,
81    clippy::redundant_else,
82    clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7324
83    clippy::similar_names,
84    clippy::uninlined_format_args,
85    clippy::unnested_or_patterns,
86    clippy::unseparated_literal_suffix,
87    clippy::wildcard_imports
88)]
89
90extern crate alloc;
91
92mod display;
93mod error;
94mod eval;
95mod identifier;
96mod impls;
97mod parse;
98
99#[cfg(feature = "serde")]
100mod serde;
101
102use crate::identifier::Identifier;
103use alloc::vec::Vec;
104use core::cmp::Ordering;
105use core::str::FromStr;
106
107pub use crate::parse::Error;
108
109/// **SemVer version** as defined by <https://semver.org>.
110///
111/// # Syntax
112///
113/// - The major, minor, and patch numbers may be any integer 0 through u64::MAX.
114///   When representing a SemVer version as a string, each number is written as
115///   a base 10 integer. For example, `1.0.119`.
116///
117/// - Leading zeros are forbidden in those positions. For example `1.01.00` is
118///   invalid as a SemVer version.
119///
120/// - The pre-release identifier, if present, must conform to the syntax
121///   documented for [`Prerelease`].
122///
123/// - The build metadata, if present, must conform to the syntax documented for
124///   [`BuildMetadata`].
125///
126/// - Whitespace is not allowed anywhere in the version.
127///
128/// # Total ordering
129///
130/// Given any two SemVer versions, one is less than, greater than, or equal to
131/// the other. Versions may be compared against one another using Rust's usual
132/// comparison operators.
133///
134/// - The major, minor, and patch number are compared numerically from left to
135///   right, lexicographically ordered as a 3-tuple of integers. So for example
136///   version `1.5.0` is less than version `1.19.0`, despite the fact that
137///   "1.19.0" &lt; "1.5.0" as ASCIIbetically compared strings and 1.19 &lt; 1.5
138///   as real numbers.
139///
140/// - When major, minor, and patch are equal, a pre-release version is
141///   considered less than the ordinary release:&ensp;version `1.0.0-alpha.1` is
142///   less than version `1.0.0`.
143///
144/// - Two pre-releases of the same major, minor, patch are compared by
145///   lexicographic ordering of dot-separated components of the pre-release
146///   string.
147///
148///   - Identifiers consisting of only digits are compared
149///     numerically:&ensp;`1.0.0-pre.8` is less than `1.0.0-pre.12`.
150///
151///   - Identifiers that contain a letter or hyphen are compared in ASCII sort
152///     order:&ensp;`1.0.0-pre12` is less than `1.0.0-pre8`.
153///
154///   - Any numeric identifier is always less than any non-numeric
155///     identifier:&ensp;`1.0.0-pre.1` is less than `1.0.0-pre.x`.
156///
157/// Example:&ensp;`1.0.0-alpha`&ensp;&lt;&ensp;`1.0.0-alpha.1`&ensp;&lt;&ensp;`1.0.0-alpha.beta`&ensp;&lt;&ensp;`1.0.0-beta`&ensp;&lt;&ensp;`1.0.0-beta.2`&ensp;&lt;&ensp;`1.0.0-beta.11`&ensp;&lt;&ensp;`1.0.0-rc.1`&ensp;&lt;&ensp;`1.0.0`
158#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
159pub struct Version {
160    pub major: u64,
161    pub minor: u64,
162    pub patch: u64,
163    pub pre: Prerelease,
164    pub build: BuildMetadata,
165}
166
167/// **SemVer version requirement** describing the intersection of some version
168/// comparators, such as `>=1.2.3, <1.8`.
169///
170/// # Syntax
171///
172/// - Either `*` (meaning "any"), or one or more comma-separated comparators.
173///
174/// - A [`Comparator`] is an operator ([`Op`]) and a partial version, separated
175///   by optional whitespace. For example `>=1.0.0` or `>=1.0`.
176///
177/// - Build metadata is syntactically permitted on the partial versions, but is
178///   completely ignored, as it's never relevant to whether any comparator
179///   matches a particular version.
180///
181/// - Whitespace is permitted around commas and around operators. Whitespace is
182///   not permitted within a partial version, i.e. anywhere between the major
183///   version number and its minor, patch, pre-release, or build metadata.
184#[derive(Clone, Eq, PartialEq, Hash, Debug)]
185pub struct VersionReq {
186    pub comparators: Vec<Comparator>,
187}
188
189/// A pair of comparison operator and partial version, such as `>=1.2`. Forms
190/// one piece of a VersionReq.
191#[derive(Clone, Eq, PartialEq, Hash, Debug)]
192pub struct Comparator {
193    pub op: Op,
194    pub major: u64,
195    pub minor: Option<u64>,
196    /// Patch is only allowed if minor is Some.
197    pub patch: Option<u64>,
198    /// Non-empty pre-release is only allowed if patch is Some.
199    pub pre: Prerelease,
200}
201
202/// SemVer comparison operator: `=`, `>`, `>=`, `<`, `<=`, `~`, `^`, `*`.
203///
204/// # Op::Exact
205/// - &ensp;**`=I.J.K`**&emsp;&mdash;&emsp;exactly the version I.J.K
206/// - &ensp;**`=I.J`**&emsp;&mdash;&emsp;equivalent to `>=I.J.0, <I.(J+1).0`
207/// - &ensp;**`=I`**&emsp;&mdash;&emsp;equivalent to `>=I.0.0, <(I+1).0.0`
208///
209/// # Op::Greater
210/// - &ensp;**`>I.J.K`**
211/// - &ensp;**`>I.J`**&emsp;&mdash;&emsp;equivalent to `>=I.(J+1).0`
212/// - &ensp;**`>I`**&emsp;&mdash;&emsp;equivalent to `>=(I+1).0.0`
213///
214/// # Op::GreaterEq
215/// - &ensp;**`>=I.J.K`**
216/// - &ensp;**`>=I.J`**&emsp;&mdash;&emsp;equivalent to `>=I.J.0`
217/// - &ensp;**`>=I`**&emsp;&mdash;&emsp;equivalent to `>=I.0.0`
218///
219/// # Op::Less
220/// - &ensp;**`<I.J.K`**
221/// - &ensp;**`<I.J`**&emsp;&mdash;&emsp;equivalent to `<I.J.0`
222/// - &ensp;**`<I`**&emsp;&mdash;&emsp;equivalent to `<I.0.0`
223///
224/// # Op::LessEq
225/// - &ensp;**`<=I.J.K`**
226/// - &ensp;**`<=I.J`**&emsp;&mdash;&emsp;equivalent to `<I.(J+1).0`
227/// - &ensp;**`<=I`**&emsp;&mdash;&emsp;equivalent to `<(I+1).0.0`
228///
229/// # Op::Tilde&emsp;("patch" updates)
230/// *Tilde requirements allow the **patch** part of the semver version (the third number) to increase.*
231/// - &ensp;**`~I.J.K`**&emsp;&mdash;&emsp;equivalent to `>=I.J.K, <I.(J+1).0`
232/// - &ensp;**`~I.J`**&emsp;&mdash;&emsp;equivalent to `=I.J`
233/// - &ensp;**`~I`**&emsp;&mdash;&emsp;equivalent to `=I`
234///
235/// # Op::Caret&emsp;("compatible" updates)
236/// *Caret requirements allow parts that are **right of the first nonzero** part of the semver version to increase.*
237/// - &ensp;**`^I.J.K`**&ensp;(for I\>0)&emsp;&mdash;&emsp;equivalent to `>=I.J.K, <(I+1).0.0`
238/// - &ensp;**`^0.J.K`**&ensp;(for J\>0)&emsp;&mdash;&emsp;equivalent to `>=0.J.K, <0.(J+1).0`
239/// - &ensp;**`^0.0.K`**&emsp;&mdash;&emsp;equivalent to `=0.0.K`
240/// - &ensp;**`^I.J`**&ensp;(for I\>0 or J\>0)&emsp;&mdash;&emsp;equivalent to `^I.J.0`
241/// - &ensp;**`^0.0`**&emsp;&mdash;&emsp;equivalent to `=0.0`
242/// - &ensp;**`^I`**&emsp;&mdash;&emsp;equivalent to `=I`
243///
244/// # Op::Wildcard
245/// - &ensp;**`I.J.*`**&emsp;&mdash;&emsp;equivalent to `=I.J`
246/// - &ensp;**`I.*`**&ensp;or&ensp;**`I.*.*`**&emsp;&mdash;&emsp;equivalent to `=I`
247#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
248#[non_exhaustive]
249pub enum Op {
250    Exact,
251    Greater,
252    GreaterEq,
253    Less,
254    LessEq,
255    Tilde,
256    Caret,
257    Wildcard,
258}
259
260/// Optional pre-release identifier on a version string. This comes after `-` in
261/// a SemVer version, like `1.0.0-alpha.1`
262///
263/// # Examples
264///
265/// Some real world pre-release idioms drawn from crates.io:
266///
267/// - **[mio]** <code>0.7.0-<b>alpha.1</b></code> &mdash; the most common style
268///   for numbering pre-releases.
269///
270/// - **[pest]** <code>1.0.0-<b>beta.8</b></code>,&ensp;<code>1.0.0-<b>rc.0</b></code>
271///   &mdash; this crate makes a distinction between betas and release
272///   candidates.
273///
274/// - **[sassers]** <code>0.11.0-<b>shitshow</b></code> &mdash; ???.
275///
276/// - **[atomic-utils]** <code>0.0.0-<b>reserved</b></code> &mdash; a squatted
277///   crate name.
278///
279/// [mio]: https://crates.io/crates/mio
280/// [pest]: https://crates.io/crates/pest
281/// [atomic-utils]: https://crates.io/crates/atomic-utils
282/// [sassers]: https://crates.io/crates/sassers
283///
284/// *Tip:* Be aware that if you are planning to number your own pre-releases,
285/// you should prefer to separate the numeric part from any non-numeric
286/// identifiers by using a dot in between. That is, prefer pre-releases
287/// `alpha.1`, `alpha.2`, etc rather than `alpha1`, `alpha2` etc. The SemVer
288/// spec's rule for pre-release precedence has special treatment of numeric
289/// components in the pre-release string, but only if there are no non-digit
290/// characters in the same dot-separated component. So you'd have `alpha.2` &lt;
291/// `alpha.11` as intended, but `alpha11` &lt; `alpha2`.
292///
293/// # Syntax
294///
295/// Pre-release strings are a series of dot separated identifiers immediately
296/// following the patch version. Identifiers must comprise only ASCII
297/// alphanumerics and hyphens: `0-9`, `A-Z`, `a-z`, `-`. Identifiers must not be
298/// empty. Numeric identifiers must not include leading zeros.
299///
300/// # Total ordering
301///
302/// Pre-releases have a total order defined by the SemVer spec. It uses
303/// lexicographic ordering of dot-separated components. Identifiers consisting
304/// of only digits are compared numerically. Otherwise, identifiers are compared
305/// in ASCII sort order. Any numeric identifier is always less than any
306/// non-numeric identifier.
307///
308/// Example:&ensp;`alpha`&ensp;&lt;&ensp;`alpha.85`&ensp;&lt;&ensp;`alpha.90`&ensp;&lt;&ensp;`alpha.200`&ensp;&lt;&ensp;`alpha.0a`&ensp;&lt;&ensp;`alpha.1a0`&ensp;&lt;&ensp;`alpha.a`&ensp;&lt;&ensp;`beta`
309#[derive(Default, Clone, Eq, PartialEq, Hash)]
310pub struct Prerelease {
311    identifier: Identifier,
312}
313
314/// Optional build metadata identifier. This comes after `+` in a SemVer
315/// version, as in `0.8.1+zstd.1.5.0`.
316///
317/// # Examples
318///
319/// Some real world build metadata idioms drawn from crates.io:
320///
321/// - **[libgit2-sys]** <code>0.12.20+<b>1.1.0</b></code> &mdash; for this
322///   crate, the build metadata indicates the version of the C libgit2 library
323///   that the Rust crate is built against.
324///
325/// - **[mashup]** <code>0.1.13+<b>deprecated</b></code> &mdash; just the word
326///   "deprecated" for a crate that has been superseded by another. Eventually
327///   people will take notice of this in Cargo's build output where it lists the
328///   crates being compiled.
329///
330/// - **[google-bigquery2]** <code>2.0.4+<b>20210327</b></code> &mdash; this
331///   library is automatically generated from an official API schema, and the
332///   build metadata indicates the date on which that schema was last captured.
333///
334/// - **[fbthrift-git]** <code>0.0.6+<b>c7fcc0e</b></code> &mdash; this crate is
335///   published from snapshots of a big company monorepo. In monorepo
336///   development, there is no concept of versions, and all downstream code is
337///   just updated atomically in the same commit that breaking changes to a
338///   library are landed. Therefore for crates.io purposes, every published
339///   version must be assumed to be incompatible with the previous. The build
340///   metadata provides the source control hash of the snapshotted code.
341///
342/// [libgit2-sys]: https://crates.io/crates/libgit2-sys
343/// [mashup]: https://crates.io/crates/mashup
344/// [google-bigquery2]: https://crates.io/crates/google-bigquery2
345/// [fbthrift-git]: https://crates.io/crates/fbthrift-git
346///
347/// # Syntax
348///
349/// Build metadata is a series of dot separated identifiers immediately
350/// following the patch or pre-release version. Identifiers must comprise only
351/// ASCII alphanumerics and hyphens: `0-9`, `A-Z`, `a-z`, `-`. Identifiers must
352/// not be empty. Leading zeros *are* allowed, unlike any other place in the
353/// SemVer grammar.
354///
355/// # Total ordering
356///
357/// Build metadata is ignored in evaluating `VersionReq`; it plays no role in
358/// whether a `Version` matches any one of the comparison operators.
359///
360/// However for comparing build metadatas among one another, they do have a
361/// total order which is determined by lexicographic ordering of dot-separated
362/// components. Identifiers consisting of only digits are compared numerically.
363/// Otherwise, identifiers are compared in ASCII sort order. Any numeric
364/// identifier is always less than any non-numeric identifier.
365///
366/// Example:&ensp;`demo`&ensp;&lt;&ensp;`demo.85`&ensp;&lt;&ensp;`demo.90`&ensp;&lt;&ensp;`demo.090`&ensp;&lt;&ensp;`demo.200`&ensp;&lt;&ensp;`demo.1a0`&ensp;&lt;&ensp;`demo.a`&ensp;&lt;&ensp;`memo`
367#[derive(Default, Clone, Eq, PartialEq, Hash)]
368pub struct BuildMetadata {
369    identifier: Identifier,
370}
371
372impl Version {
373    /// Create `Version` with an empty pre-release and build metadata.
374    ///
375    /// Equivalent to:
376    ///
377    /// ```
378    /// # use semver::{BuildMetadata, Prerelease, Version};
379    /// #
380    /// # const fn new(major: u64, minor: u64, patch: u64) -> Version {
381    /// Version {
382    ///     major,
383    ///     minor,
384    ///     patch,
385    ///     pre: Prerelease::EMPTY,
386    ///     build: BuildMetadata::EMPTY,
387    /// }
388    /// # }
389    /// ```
390    pub const fn new(major: u64, minor: u64, patch: u64) -> Self {
391        Version {
392            major,
393            minor,
394            patch,
395            pre: Prerelease::EMPTY,
396            build: BuildMetadata::EMPTY,
397        }
398    }
399
400    /// Create `Version` by parsing from string representation.
401    ///
402    /// # Errors
403    ///
404    /// Possible reasons for the parse to fail include:
405    ///
406    /// - `1.0` &mdash; too few numeric components. A SemVer version must have
407    ///   exactly three. If you are looking at something that has fewer than
408    ///   three numbers in it, it's possible it is a `VersionReq` instead (with
409    ///   an implicit default `^` comparison operator).
410    ///
411    /// - `1.0.01` &mdash; a numeric component has a leading zero.
412    ///
413    /// - `1.0.unknown` &mdash; unexpected character in one of the components.
414    ///
415    /// - `1.0.0-` or `1.0.0+` &mdash; the pre-release or build metadata are
416    ///   indicated present but empty.
417    ///
418    /// - `1.0.0-alpha_123` &mdash; pre-release or build metadata have something
419    ///   outside the allowed characters, which are `0-9`, `A-Z`, `a-z`, `-`,
420    ///   and `.` (dot).
421    ///
422    /// - `23456789999999999999.0.0` &mdash; overflow of a u64.
423    pub fn parse(text: &str) -> Result<Self, Error> {
424        Version::from_str(text)
425    }
426
427    /// Compare the major, minor, patch, and pre-release value of two versions,
428    /// disregarding build metadata. Versions that differ only in build metadata
429    /// are considered equal. This comparison is what the SemVer spec refers to
430    /// as "precedence".
431    ///
432    /// # Example
433    ///
434    /// ```
435    /// use semver::Version;
436    ///
437    /// let mut versions = [
438    ///     "1.20.0+c144a98".parse::<Version>().unwrap(),
439    ///     "1.20.0".parse().unwrap(),
440    ///     "1.0.0".parse().unwrap(),
441    ///     "1.0.0-alpha".parse().unwrap(),
442    ///     "1.20.0+bc17664".parse().unwrap(),
443    /// ];
444    ///
445    /// // This is a stable sort, so it preserves the relative order of equal
446    /// // elements. The three 1.20.0 versions differ only in build metadata so
447    /// // they are not reordered relative to one another.
448    /// versions.sort_by(Version::cmp_precedence);
449    /// assert_eq!(versions, [
450    ///     "1.0.0-alpha".parse().unwrap(),
451    ///     "1.0.0".parse().unwrap(),
452    ///     "1.20.0+c144a98".parse().unwrap(),
453    ///     "1.20.0".parse().unwrap(),
454    ///     "1.20.0+bc17664".parse().unwrap(),
455    /// ]);
456    ///
457    /// // Totally order the versions, including comparing the build metadata.
458    /// versions.sort();
459    /// assert_eq!(versions, [
460    ///     "1.0.0-alpha".parse().unwrap(),
461    ///     "1.0.0".parse().unwrap(),
462    ///     "1.20.0".parse().unwrap(),
463    ///     "1.20.0+bc17664".parse().unwrap(),
464    ///     "1.20.0+c144a98".parse().unwrap(),
465    /// ]);
466    /// ```
467    pub fn cmp_precedence(&self, other: &Self) -> Ordering {
468        Ord::cmp(
469            &(self.major, self.minor, self.patch, &self.pre),
470            &(other.major, other.minor, other.patch, &other.pre),
471        )
472    }
473}
474
475impl VersionReq {
476    /// A `VersionReq` with no constraint on the version numbers it matches.
477    /// Equivalent to `VersionReq::parse("*").unwrap()`.
478    ///
479    /// In terms of comparators this is equivalent to `>=0.0.0`.
480    ///
481    /// Counterintuitively a `*` VersionReq does not match every possible
482    /// version number. In particular, in order for *any* `VersionReq` to match
483    /// a pre-release version, the `VersionReq` must contain at least one
484    /// `Comparator` that has an explicit major, minor, and patch version
485    /// identical to the pre-release being matched, and that has a nonempty
486    /// pre-release component. Since `*` is not written with an explicit major,
487    /// minor, and patch version, and does not contain a nonempty pre-release
488    /// component, it does not match any pre-release versions.
489    pub const STAR: Self = VersionReq {
490        comparators: Vec::new(),
491    };
492
493    /// Create `VersionReq` by parsing from string representation.
494    ///
495    /// # Errors
496    ///
497    /// Possible reasons for the parse to fail include:
498    ///
499    /// - `>a.b` &mdash; unexpected characters in the partial version.
500    ///
501    /// - `@1.0.0` &mdash; unrecognized comparison operator.
502    ///
503    /// - `^1.0.0, ` &mdash; unexpected end of input.
504    ///
505    /// - `>=1.0 <2.0` &mdash; missing comma between comparators.
506    ///
507    /// - `*.*` &mdash; unsupported wildcard syntax.
508    pub fn parse(text: &str) -> Result<Self, Error> {
509        VersionReq::from_str(text)
510    }
511
512    /// Evaluate whether the given `Version` satisfies the version requirement
513    /// described by `self`.
514    pub fn matches(&self, version: &Version) -> bool {
515        eval::matches_req(self, version)
516    }
517}
518
519/// The default VersionReq is the same as [`VersionReq::STAR`].
520impl Default for VersionReq {
521    fn default() -> Self {
522        VersionReq::STAR
523    }
524}
525
526impl Comparator {
527    pub fn parse(text: &str) -> Result<Self, Error> {
528        Comparator::from_str(text)
529    }
530
531    pub fn matches(&self, version: &Version) -> bool {
532        eval::matches_comparator(self, version)
533    }
534}
535
536impl Prerelease {
537    pub const EMPTY: Self = Prerelease {
538        identifier: Identifier::empty(),
539    };
540
541    pub fn new(text: &str) -> Result<Self, Error> {
542        Prerelease::from_str(text)
543    }
544
545    pub fn as_str(&self) -> &str {
546        self.identifier.as_str()
547    }
548
549    pub fn is_empty(&self) -> bool {
550        self.identifier.is_empty()
551    }
552}
553
554impl BuildMetadata {
555    pub const EMPTY: Self = BuildMetadata {
556        identifier: Identifier::empty(),
557    };
558
559    pub fn new(text: &str) -> Result<Self, Error> {
560        BuildMetadata::from_str(text)
561    }
562
563    pub fn as_str(&self) -> &str {
564        self.identifier.as_str()
565    }
566
567    pub fn is_empty(&self) -> bool {
568        self.identifier.is_empty()
569    }
570}