Semantic Versioning 2.0.0: parsing, printing, precedence, bumping and version ranges, with the range grammar of npm, Cargo, Ruby or the strict spec as a parameter.
┌──────────────┐
"1.2.3-alpha+build" │ Version │ parse / to_string
">=1.2.3, <2.0.0" │ Range │ precedence_cmp / satisfies
"^1.2.3" "~1.2" │ Syntax │ npm / cargo / ruby / strict
└──────────────┘import {
"moonbitstack/moonsver",
}// moon.work
members = [
"vendor/moonsver",
]
// moon.mod
import {
"moonbitstack/moonsver@0.1.0",
}///|
fn demo() -> Unit raise @moonsver.Refused {
// Parsing and printing
let v = @moonsver.Version::parse("1.2.3-alpha.1+build.5")
println(v.to_string()) // 1.2.3-alpha.1+build.5
println(v.core_string()) // 1.2.3
println(v.is_prerelease()) // true
// Precedence (§11; build metadata stays out of it)
let a = @moonsver.Version::parse("1.0.0-alpha")
let b = @moonsver.Version::parse("1.0.0")
println(a.precedence_cmp(b)) // -1
// Ranges
let r = @moonsver.Range::parse("^1.2.3")
println(r.to_string()) // >=1.2.3, <2.0.0
println(r.satisfies(@moonsver.Version::parse("1.9.0"))) // true
println(r.satisfies(@moonsver.Version::parse("2.0.0"))) // false
// Bumps
println(@moonsver.Version::parse("1.2.3").inc(@moonsver.Major).to_string()) // 2.0.0
}| Method | What it does |
|---|---|
| Version::parse(s, syntax?) | Parse, raising Refused; npm grammar by default |
| Version::new(major, minor, patch) | Build from three Ints |
| Version::from_bigint(major, minor, patch) | Build from three BigInts |
| major() / minor() / patch() | The numbers, as BigInt |
| prerelease() / build() | Identifier lists (ArrayView) |
| is_prerelease() / is_stable() | Prerelease or not |
| same_core(other) | Do the three numbers match? |
| core() / core_string() | Drop prerelease and build metadata |
| precedence_cmp(other) | §11 precedence, returning -1, 0 or 1 |
| compare(other) | Total order: precedence, then build metadata (Compare, so sort works) |
| inc(release) | Apply a bump, see below |
| to_string() | Canonical text (Show) |
| Input | Bump | Result |
|---|---|---|
| 1.0.0-pre | Major | 1.0.0 (minor and patch are already zero, so only the prerelease goes) |
| 1.2.0-pre | Major | 2.0.0 |
| 1.2.3-pre | Patch | 1.2.3 |
| 1.2.3 | Prerelease | 1.2.4-0 |
| 1.2.3-alpha.1.2 | Prerelease | 1.2.3-alpha.1.3 (last numeric identifier wins) |
| 1.2.3-alpha.beta | Prerelease | 1.2.3-alpha.beta.0 |
| 1.2.3-rc.1+build | Stable | 1.2.3 |
| Method | What it does |
|---|---|
| Range::parse(s, syntax?) | Parse, raising Refused |
| Range::any() | Any version (*) |
| satisfies(version) / satisfies_with(version, policy) | Does the version fit? |
| max_satisfying(versions) / min_satisfying(versions) | The extremes that fit |
| satisfying(versions) | Everything that fits, sorted ascending by precedence |
| sets() | The disjuncts (ArrayView[ComparatorSet]) |
| policy() | The prerelease policy the range was parsed with |
| is_any() | Is this the same as any version? |
| to_string() | Canonical text, which re-parses under the same grammar |
| Field | What it controls | npm | Cargo | Ruby | strict |
|---|---|---|---|---|---|
| default_op | Meaning of a bare version | Exact | Caret | Exact | Exact |
| tilde_bump | How ~ finds its upper bound | Patch | Patch | LastSpecified | Patch |
| allow_caret | Accept ^ | yes | yes | no | no |
| allow_tilde | Accept ~ (~> only under LastSpecified) | yes | yes | yes | no |
| allow_ne | Accept != | no | no | yes | no |
| allow_or | Accept \|\| | yes | no | no | no |
| allow_hyphen_range | Accept 1.2.3 - 2.0.0 | yes | no | no | no |
| allow_xrange | Accept 1.x, 1.2.*, bare 1.2 | yes | yes | no | no |
| allow_empty | Empty string means any version | yes | no | no | no |
| allow_v_prefix | Accept v1.2.3 | yes | no | no | no |
| allow_equals_prefix | Accept =1.2.3 as a version literal | yes | yes | yes | no |
| allow_build_metadata | Accept +build | yes | yes | yes | yes |
| trim | Whitespace around a version literal | yes | yes | yes | no |
| prerelease_policy | How prereleases are matched | SameTuple | SameTuple | SameTuple | SameTuple |
| Written | npm | Cargo | Ruby |
|---|---|---|---|
| 1.2.3 | =1.2.3 | >=1.2.3, <2.0.0 | =1.2.3 |
| ~1.2 | >=1.2.0, <1.3.0 | >=1.2.0, <1.3.0 | — (~> 1.2 gives >=1.2.0, <2.0.0) |
| 1.2.* | >=1.2.0, <1.3.0 | >=1.2.0, <1.3.0 | not supported |
| ^1.2.3 \|\| ^2 | fine | rejected | rejected |
| Policy | Meaning | Example |
|---|---|---|
| SameTuple | The npm and Cargo convention | >=1.2.3-alpha takes 1.2.3-beta, refuses 1.2.4-beta; * takes no prerelease at all |
| Always | Compare them like any other version (npm's includePrerelease) | >=1.0.0 takes 1.5.0-beta |
| Never | Prereleases never match, for when only stable releases will do | >=1.0.0 refuses 1.5.0-beta |
| Behaviour | node-semver | Rust semver | Cargo | moonsver |
|---|---|---|---|---|
| Numeric range | JS safe integer | u64 | u64 | arbitrary precision |
| Bare version means | exact | exact | caret | whatever Syntax::default_op says |
| Build metadata in precedence | no | no | no | no |
| Build metadata in Eq | no (eq uses compare) | yes | — | yes |
| \|\| | yes | no | no | a setting |
| Hyphen ranges | yes | no | no | a setting |
| 1.x shorthand | yes | no | * forms only | a setting |
| Prerelease matching | same-tuple anchor | same-tuple anchor | same-tuple anchor | three policies |
moon run bin -- parse 1.2.3 v2.0.0-rc.1+build
moon run bin -- compare 1.0.0-rc.1 1.0.0
moon run bin -- sort 2.0.0 1.0.0-beta 1.0.0 0.9.9
moon run bin -- bump prerelease 1.2.3-alpha.1 # 1.2.3-alpha.2
moon run bin -- range "^0.2.3" # >=0.2.3, <0.3.0
moon run bin -- range --syntax cargo 1.2.3 # >=1.2.3, <2.0.0
moon run bin -- range --syntax ruby "~> 1.2" # >=1.2.0, <2.0.0
moon run bin -- satisfies 1.9.0 "^1.2.3" # true
moon run bin -- max "^1.2.0" 1.0.0 1.2.3 1.5.0 # 1.5.0
moon run bin -- parse --json 1.2.3-alpha.1{
"input": "1.2.3-alpha.1",
"normalised": "1.2.3-alpha.1",
"major": "1",
"minor": "2",
"patch": "3",
"prerelease": ["alpha", "1"],
"build": [],
"is_prerelease": true
}let j = @json.to_json(@moonsver.Range::parse("^1.2.3"))
// Json::string(">=1.2.3, <2.0.0")moon run examples # the smallest useful example
moon test # 47 cases, green on wasm-gc, js, native and wasm
moon check # no warningspub(all) suberror Refused {
Empty(at~ : Int, String)
Unexpected(at~ : Int, String)
Truncated(at~ : Int, String)
Number(at~ : Int, String)
LeadingZero(at~ : Int, String)
EmptyIdentifier(at~ : Int, String)
Identifier(at~ : Int, String)
Unsupported(at~ : Int, String)
Range(at~ : Int, String)
} derive(Eq)impl Eq for Comparatorimpl Show for Comparatorimpl Debug for Comparatorimpl Eq for ComparatorSetimpl Show for ComparatorSetpub(all) enum DefaultOp {
Exact
Caret
Tilde
}impl Compare for Identifierimpl Eq for Identifierimpl Hash for Identifierimpl Show for Identifierimpl Debug for Identifierpub(all) enum Op {
Eq
Ne
Gt
Ge
Lt
Le
}pub(all) enum PrereleasePolicy {
SameTuple
Always
Never
}impl Eq for PrereleasePolicyimpl Debug for PrereleasePolicypub(all) enum Release {
Major
Minor
Patch
PreMajor
PreMinor
PrePatch
Prerelease
Stable
}pub(all) struct Syntax {
allow_v_prefix : Bool
allow_equals_prefix : Bool
allow_build_metadata : Bool
trim : Bool
default_op : DefaultOp
tilde_bump : TildeBump
allow_caret : Bool
allow_tilde : Bool
allow_ne : Bool
allow_or : Bool
allow_hyphen_range : Bool
allow_xrange : Bool
allow_empty : Bool
prerelease_policy : PrereleasePolicy
}pub(all) enum TildeBump {
Patch
LastSpecified
}Install
Download zipSemantic Versioning 2.0.0: parsing, printing, precedence, bumping and version ranges, with the range grammar of npm, Cargo, Ruby or the strict spec as a parameter.