#semver #version #semantic #parser #npm-package #comparison #range

semver_rs

Semantic version parsing and comparison based on NPM's node-semver package

5 releases

0.2.0 Nov 22, 2021
0.1.3 Apr 30, 2019
0.1.2 Apr 30, 2019
0.1.1 Apr 30, 2019
0.1.0 Apr 30, 2019

#1003 in Parser implementations

Download history 4/week @ 2023-12-14 2/week @ 2023-12-21 1/week @ 2023-12-28 2/week @ 2024-01-04 1/week @ 2024-01-11 6/week @ 2024-02-08 33/week @ 2024-02-15 22/week @ 2024-02-22 20/week @ 2024-02-29 22/week @ 2024-03-07 18/week @ 2024-03-14 15/week @ 2024-03-21 32/week @ 2024-03-28

88 downloads per month
Used in 2 crates

Custom license

77KB
1.5K SLoC

SemVer

CI semver_rs semver_rs

Semantic version parsing and comparison (semver). The implementation of this crate is based on the node-semver npm package. The tests are taken directly from node-semver's repo. This should make this crate as good at parsing semver expressions as the node package manager.

Installation

Add this to your [dependencies] section in Cargo.toml:

semver_rs = "0.2"

Usage

Comparing two versions

use semver_rs::Version;

// by constructing version instances manually
let ver1 = Version::new("2.0.0").parse()?;
let ver2 = Version::new("1.2.3").parse()?;

assert!(ver1 > ver2);

// by using the exported helper function
use semver_rs::compare;
use std::cmp::Ordering;

assert!(compare("2.0.0", "1.2.3", None)? == Ordering::Greater);

Checking whether a version is in a range

use semver_rs::{Range, Version};

// by constructing version instances manually
let range = Range::new(">=1.2.3").parse()?;
let ver = Version::new("1.2.4").parse()?;

assert!(range.test(&ver));

// by using the exported helper function
use semver_rs::satisfies;

assert!(satisfies("1.2.4", ">=1.2.4", None)?);

Parsing with specific options

use semver_rs::{Version, Range, Options};

let opts = Options::builder().loose(true).include_prerelease(true).build();
let range = Range::new(">=1.2.3").with_options(opts).parse()?;
let ver = Version::new("1.2.4-pre1").with_options(opts).parse()?;

assert!(range.test(&ver));

Serializing

In order to allow serializing the semver structs allow the serde feature:

semver_rs = { version = "0.2", features = ["serde"] }
use semver_rs::{Range, Options};

let opts = Options::builder().loose(true).include_prerelease(true).build();
let range = Range::new(">=1.2.3").with_options(opts).parse().unwrap();
let _ = serde_json::to_string(&opts).unwrap();

Development

Install just and run the setup:

cargo install just && just setup

Run bench

To run the benchmarks populating the next point run:

just bench

This shell script collects some ranges from random npm packages and compares the results for the three implementations - semver_node, semver_rs and steveklabnik/semver. From the table bellow the results can be observed.

Comparisons and considerations with other crates

At the time of writing this README there's only one other crate in the Rust ecosystem capable of parsing semver - steveklabnik/semver. While this crate is being used in cargo and is clearly doing its job there very well, while comparing arbitrary semver strings from a number of NPM packages I found it unable to parse a lot of them. Since its implementation of semver was vastly different from NPM's I decided to base this crate on NPM's package in the hopes of making it easier to keep up with any updates in the future. I kept the implementation as close as possible so the code structure and the way input is parsed should be very similar.

One trade-off this implementation had to make was a tiny bit of performance. Since the parsing is based heavily on Regex it's a little bit slower. There are still a lot of string allocations that can be eliminated, especially in parsing Ranges and Versions with prereleases.

┌─────────┬───────────────────────┬───────────┬───────────────┬────────┬─────────────────────┐
 (index)          name          │ satisfies │ not_satisfies │ errors │     average_us      │
├─────────┼───────────────────────┼───────────┼───────────────┼────────┼─────────────────────┤
    0    │     'semver_node'     │    14     │      451      │   1    │  32.68025751072961  │
    1    │      'semver_rs'      │    14     │      451      │   1    │  8.454935622317597  │
    2    │ 'steveklabnik/semver' │    11     │      445      │   10   │ 0.27682403433476394 │
└─────────┴───────────────────────┴───────────┴───────────────┴────────┴─────────────────────┘

In conclussion semver_rs is faster than semver_node and slower than steveklabnik/semver. It's also as accurate in parsing as semver_node, while steveklabnik/semver couldn't handle 9 of the ranges.

Dependencies

~2.6–4MB
~77K SLoC