Sign in

    moonsver

    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.

    semver
    version
    range
    parser
    npm
    cargo
    moonbit
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    yesterday
    Downloads
    11

    No dependencies

    #moonsver

    SemVer 2.0.0 for MoonBit: parsing, printing, precedence and version ranges.

    Ranges are not part of the spec, and every ecosystem wrote its own grammar for them. moonsver pulls those differences out into a set of grammar settings (Syntax), so one parser handles npm, Cargo, RubyGems and a strict reading of the spec — the same idea as the grammar parameters in moonregex.

    ┌──────────────┐ "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 └──────────────┘

    • SemVer 2.0.0 only. PEP 440 is not SemVer and is not implemented; 1.0.0.post1 and ~=1.4 are refused rather than guessed at.
    • Arbitrary precision. The three numbers are BigInts, so 18446744073709551616.0.0 parses, prints and compares exactly. node-semver stops at the JavaScript safe integer, Rust's semver crate at u64.
    • Aligned with the mature implementations. Precedence follows §11 of the spec to the letter; range expansion matches node-semver's replaceXRange / replaceCarets / replaceTildes and Cargo's caret rules.
    • No third-party dependencies, just moonbitlang/core.

    #Using it

    In your moon.pkg (module moonbitstack/moonsver, package alias @moonsver):

    import { "moonbitstack/moonsver", }

    While it is still local, list the checkout as a member of your moon.work workspace and import it as usual — for local members the version in the import is ignored:

    // moon.work members = [ "vendor/moonsver", ] // moon.mod import { "moonbitstack/moonsver@0.1.0", }

    (Not on mooncakes yet; once it is, moon add moonbitstack/moonsver will do the job.)

    #Quick start

    ///|
    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
    }

    Input that does not parse raises Refused, which says what was wrong and where.

    #API

    #Version

    MethodWhat 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)

    Equality (Eq) is full equality: 1.0.0+a != 1.0.0+b, though the two share a precedence. That is the choice Rust's semver makes as well; node-semver's eq ignores build metadata.

    #Release

    Major, Minor, Patch, PreMajor, PreMinor, PrePatch, Prerelease, Stable, with the same behaviour as node-semver's inc(), including the corners that are easy to miss:

    InputBumpResult
    1.0.0-preMajor1.0.0 (minor and patch are already zero, so only the prerelease goes)
    1.2.0-preMajor2.0.0
    1.2.3-prePatch1.2.3
    1.2.3Prerelease1.2.4-0
    1.2.3-alpha.1.2Prerelease1.2.3-alpha.1.3 (last numeric identifier wins)
    1.2.3-alpha.betaPrerelease1.2.3-alpha.beta.0
    1.2.3-rc.1+buildStable1.2.3

    Release::parse("major") and to_string() are there for the CLI and config files.

    #Range

    MethodWhat 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

    A Range is the parsed, expanded structure rather than the original string: ^1.2.3 is the conjunction of >=1.2.3 and <2.0.0. Canonical output joins comparators with , and disjuncts with || .

    #satisfies(version, range, syntax?)

    A one-liner for two strings, raising Refused when either does not parse.

    #Errors

    Refused names what was wrong: Empty, Unexpected, Truncated, Number, LeadingZero, EmptyIdentifier, Identifier, Unsupported or Range. Each carries at, the character index of the first offending character, and a message; at() and message() read them without matching.

    #Grammar settings (Syntax)

    A version has one spelling in SemVer 2.0.0, but ranges are not in the spec, and npm, Cargo and RubyGems each wrote their own grammar. Syntax pulls the differences apart into fourteen settings:

    FieldWhat it controlsnpmCargoRubystrict
    default_opMeaning of a bare versionExactCaretExactExact
    tilde_bumpHow ~ finds its upper boundPatchPatchLastSpecifiedPatch
    allow_caretAccept ^yesyesnono
    allow_tildeAccept ~ (~> only under LastSpecified)yesyesyesno
    allow_neAccept !=nonoyesno
    allow_orAccept \|\|yesnonono
    allow_hyphen_rangeAccept 1.2.3 - 2.0.0yesnonono
    allow_xrangeAccept 1.x, 1.2.*, bare 1.2yesyesnono
    allow_emptyEmpty string means any versionyesnonono
    allow_v_prefixAccept v1.2.3yesnonono
    allow_equals_prefixAccept =1.2.3 as a version literalyesyesyesno
    allow_build_metadataAccept +buildyesyesyesyes
    trimWhitespace around a version literalyesyesyesno
    prerelease_policyHow prereleases are matchedSameTupleSameTupleSameTupleSameTuple

    Syntax::from_name("cargo") and name() serve config files and the CLI; Default is npm's grammar. You can also build a Syntax literal of your own — npm's sugar with Cargo's caret default, say.

    #The same string, three readings

    WrittennpmCargoRuby
    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.0not supported
    ^1.2.3 \|\| ^2finerejectedrejected

    Note that 1.2.* is not a caret under Cargo: an explicit wildcard wins over default_op, which is what Cargo's own documentation says (1.2.* := >=1.2.0,<1.3.0).

    #How it behaves

    #Precedence (§11)

    1. Compare major, minor, patch in turn (arbitrary precision);
    2. a version with a prerelease ranks below the same numbers without one;
    3. prerelease identifiers compare one by one: numeric by value, numeric below alphanumeric, alphanumeric in ASCII order;
    4. a prefix ranks below the longer list;
    5. build metadata takes no part.

    1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 <1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0 is a test case, so it stays that way.

    #Prereleases and ranges

    The spec says nothing about how a range should treat prereleases, and every ecosystem settled on "only through a comparator with the same major.minor.patch that carries a prerelease itself". moonsver makes that a three-way setting:

    PolicyMeaningExample
    SameTupleThe npm and Cargo convention>=1.2.3-alpha takes 1.2.3-beta, refuses 1.2.4-beta; * takes no prerelease at all
    AlwaysCompare them like any other version (npm's includePrerelease)>=1.0.0 takes 1.5.0-beta
    NeverPrereleases never match, for when only stable releases will do>=1.0.0 refuses 1.5.0-beta

    The policy is recorded in the Range when it is parsed, and satisfies_with can override it for a single call.

    #Canonical text

    Range::to_string prints the expanded comparators, and the output re-parses under the same grammar (there are tests for the round trip). The one exception is *, which is always accepted as "any version" — it is what an empty range prints, so refusing it would break the round trip.

    #How it compares

    Behaviournode-semverRust semverCargomoonsver
    Numeric rangeJS safe integeru64u64arbitrary precision
    Bare version meansexactexactcaretwhatever Syntax::default_op says
    Build metadata in precedencenononono
    Build metadata in Eqno (eq uses compare)yes—yes
    \|\|yesnonoa setting
    Hyphen rangesyesnonoa setting
    1.x shorthandyesno* forms onlya setting
    Prerelease matchingsame-tuple anchorsame-tuple anchorsame-tuple anchorthree policies

    #CLI

    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

    parse --json prints (the numbers are strings, so nothing is lost):

    { "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 }

    Exit codes: 0 means the command ran — satisfies returning false, or max/min finding nothing, is still 0, so read stdout for the answer. Bad arguments and parse failures exit non-zero with the detail on stderr.

    #Consuming it as a library

    Version and Range both implement ToJson / FromJson, serialised as strings, so they can go straight into config files or API responses:

    let j = @json.to_json(@moonsver.Range::parse("^1.2.3"))
    // Json::string(">=1.2.3, <2.0.0")

    A tool like moonctl can hand Range::max_satisfying a list of versions and get the newest one that fits, without reimplementing the comparison rules.

    #Examples and tests

    moon run examples # the smallest useful example moon test # 47 cases, green on wasm-gc, js, native and wasm moon check # no warnings

    The cases cover the valid and invalid examples in the spec, the §11 precedence chain, build metadata, leading zeroes, arbitrary precision, npm's caret / tilde / shorthand / hyphen / disjunction forms, Cargo's caret default and wildcards, Ruby's pessimistic operator, all three prerelease policies, the extreme-value helpers, the JSON round trip and error kinds with their positions.

    #Deliberately not done

    • PEP 440. Not SemVer; 1.0.0.post1, ~=1.4 and ==1.* are refused.
    • Loose mode. node-semver's loose lets 01.2.3 and 1.2.3beta through. moonsver offers different grammars, not a licence to accept broken input.
    • Range algebra. subset, intersects, minVersion and coerce are not here. They are not parsing problems but interval arithmetic over comparator sets, and deserve a module of their own.
    • Some Ruby spellings. ~>, != and the comparators all work, but Ruby's bare 1.2 means exactly 1.2.0 (Ruby pads the missing segments with zeroes), which is not npm's x-range. Under the ruby preset that form is refused rather than guessed at — it is a grey area, and guessing would be worse.
    • Upper bounds under Always. npm appends -0 to upper bounds (<2.0.0-0) to shut out the next version's prereleases. SameTuple gets the same result and keeps the bounds clean; with Always those prereleases do fall inside, which is what Always means.

    Refused

    pub(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)

    Why a version or a range was refused, and where.

    at is the zero-based character index of the first offending character, or the input's length when it ended early; the message says what was expected.
    impl Show for Refused

    Refused::at

    fn Refused::at(self : Refused) -> Int

    Where the input went wrong.

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::message

    fn Refused::message(self : Refused) -> String

    What was wrong, in words.

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::output

    fn Refused::output(self : Refused, logger : &Logger) -> Unit

    Refused::to_repr

    Refused::to_string

    fn Refused::to_string(self : Refused) -> String

    Comparator

    pub(all) struct Comparator {
    op : Op
    version : Version
    }

    One comparator: an operator and a full version.

    Build metadata is dropped from comparators (>=1.2.3+build is >=1.2.3), since it takes no part in precedence.
    impl Eq for Comparator
    impl Show for Comparator

    Comparator::equal

    fn Comparator::equal(self : Comparator, other : Comparator) -> Bool

    Comparator::not_equal

    fn Comparator::not_equal(x : Comparator, y : Comparator) -> Bool

    Comparator::output

    fn Comparator::output(self : Comparator, logger : &Logger) -> Unit

    Comparator::to_repr

    Comparator::to_string

    fn Comparator::to_string(self : Comparator) -> String

    ComparatorSet

    pub(all) struct ComparatorSet {
    comparators : Array[Comparator]
    }

    A conjunction (AND) of comparators. An empty list means "any version".
    impl Eq for ComparatorSet

    ComparatorSet::equal

    fn ComparatorSet::equal(self : ComparatorSet, other : ComparatorSet) -> Bool

    ComparatorSet::not_equal

    fn ComparatorSet::not_equal(x : ComparatorSet, y : ComparatorSet) -> Bool

    ComparatorSet::output

    fn ComparatorSet::output(self : ComparatorSet, logger : &Logger) -> Unit

    ComparatorSet::to_string

    fn ComparatorSet::to_string(self : ComparatorSet) -> String

    DefaultOp

    pub(all) enum DefaultOp {
    Exact
    Caret
    Tilde
    }

    What a bare version means when no operator is given.
    impl Eq for DefaultOp

    DefaultOp::equal

    fn DefaultOp::equal(self : DefaultOp, other : DefaultOp) -> Bool

    DefaultOp::not_equal

    fn DefaultOp::not_equal(x : DefaultOp, y : DefaultOp) -> Bool

    Identifier

    pub(all) enum Identifier {
    Num(
    BigInt
    )
    Alpha(String)
    }

    A version identifier.

    SemVer 2.0.0 allows two kinds of identifier: all digits, or a string drawn from [0-9A-Za-z-]. Numeric ones are held as arbitrary-precision integers, so a value such as 18446744073709551616 does not overflow.
    impl Eq for Identifier
    impl Hash for Identifier
    impl Show for Identifier

    Identifier::alpha

    fn Identifier::alpha(s : String) -> Identifier

    Identifier::as_bigint

    The integer value, or None for an alphanumeric identifier.

    Identifier::as_int

    fn Identifier::as_int(self : Identifier) -> Int?

    The value as an Int, or None if it is alphanumeric or out of range.

    Identifier::compare

    fn Identifier::compare(self : Identifier, other : Identifier) -> Int

    Identifier::equal

    fn Identifier::equal(self : Identifier, other : Identifier) -> Bool

    Identifier::hash

    fn Identifier::hash(self : Identifier) -> Int

    Identifier::hash_combine

    fn Identifier::hash_combine(self : Identifier, hasher : Hasher) -> Unit

    Identifier::is_numeric

    fn Identifier::is_numeric(self : Identifier) -> Bool

    Identifier::not_equal

    fn Identifier::not_equal(x : Identifier, y : Identifier) -> Bool

    Identifier::numeric

    fn Identifier::numeric(n : Int) -> Identifier

    Identifier::op_ge

    fn Identifier::op_ge(x : Identifier, y : Identifier) -> Bool

    Identifier::op_gt

    fn Identifier::op_gt(x : Identifier, y : Identifier) -> Bool

    Identifier::op_le

    fn Identifier::op_le(x : Identifier, y : Identifier) -> Bool

    Identifier::op_lt

    fn Identifier::op_lt(x : Identifier, y : Identifier) -> Bool

    Identifier::output

    fn Identifier::output(self : Identifier, logger : &Logger) -> Unit

    Identifier::to_repr

    Identifier::to_string

    fn Identifier::to_string(self : Identifier) -> String

    pub(all) enum Op {
    Eq
    Ne
    Gt
    Ge
    Lt
    Le
    }

    A comparison operator.
    impl Eq for Op
    impl Show for Op
    impl Debug for Op

    Op::equal

    fn Op::equal(self : Op, other : Op) -> Bool

    Op::not_equal

    fn Op::not_equal(x : Op, y : Op) -> Bool

    Op::output

    fn Op::output(self : Op, logger : &Logger) -> Unit

    Op::to_repr

    fn Op::to_repr(self : Op) ->
    Repr

    Op::to_string

    fn Op::to_string(self : Op) -> String

    PrereleasePolicy

    pub(all) enum PrereleasePolicy {
    SameTuple
    Always
    Never
    }

    How prereleases are matched against a range.

    The spec only orders prereleases against each other; it says nothing about how a range should treat them, so every ecosystem settled on its own convention.

    PrereleasePolicy::equal

    fn PrereleasePolicy::equal(self : PrereleasePolicy, other : PrereleasePolicy) -> Bool

    PrereleasePolicy::not_equal

    fn PrereleasePolicy::not_equal(x : PrereleasePolicy, y : PrereleasePolicy) -> Bool

    PrereleasePolicy::to_repr

    Range

    pub struct Range {
    sets : Array[ComparatorSet]
    policy : PrereleasePolicy
    }

    A version range: a disjunction (OR) of comparator sets.

    A Range is the parsed, normalised structure rather than the original string. ^1.2.3, for instance, becomes the set >=1.2.3, <2.0.0.
    impl Eq for Range
    impl Show for Range
    impl ToJson for Range

    Range::any

    fn Range::any() -> Range

    A range matching any stable version, the same as *.

    Range::equal

    fn Range::equal(self : Range, other : Range) -> Bool

    Range::is_any

    fn Range::is_any(self : Range) -> Bool

    Whether this is equivalent to "any version".

    Range::max_satisfying

    fn Range::max_satisfying(self : Range, versions : Array[Version]) -> Version?

    The highest satisfying version, by precedence, with build metadata breaking ties.

    Range::min_satisfying

    fn Range::min_satisfying(self : Range, versions : Array[Version]) -> Version?

    The lowest satisfying version.

    Range::not_equal

    fn Range::not_equal(x : Range, y : Range) -> Bool

    Range::output

    fn Range::output(self : Range, logger : &Logger) -> Unit

    Canonical range text: disjuncts joined with || .

    The output always re-parses under the same grammar (even strict reads *).

    Range::parse

    fn Range::parse(s : String, syntax? : Syntax) -> Range raise Refused

    Parse a version range.

    npm's grammar is the default; pass syntax= for Cargo, Ruby or strict.

    Range::policy

    fn Range::policy(self : Range) -> PrereleasePolicy

    The prerelease policy this range was parsed with.

    Range::satisfies

    fn Range::satisfies(self : Range, version : Version) -> Bool

    Does the version fall inside the range?

    The prerelease policy comes from the Syntax used to parse the range.

    Range::satisfies_with

    fn Range::satisfies_with(self : Range, version : Version, policy : PrereleasePolicy) -> Bool

    Does the version fall inside the range, with the policy overridden for this call?

    Range::satisfying

    fn Range::satisfying(self : Range, versions : Array[Version]) -> Array[Version]

    Every satisfying version, sorted ascending by precedence.

    Range::sets

    fn Range::sets(self : Range) -> ArrayView[ComparatorSet]

    The disjuncts, one per || branch.

    Range::to_json

    fn Range::to_json(self : Range) -> Json

    Range::to_repr

    Range::to_string

    fn Range::to_string(self : Range) -> String

    Canonical range text: disjuncts joined with || .

    The output always re-parses under the same grammar (even strict reads *).

    Release

    pub(all) enum Release {
    Major
    Minor
    Patch
    PreMajor
    PreMinor
    PrePatch
    Prerelease
    Stable
    }

    The kind of bump to apply.

    Semantics follow node-semver's inc(), including the detail that is easy to miss: the major bump of 1.0.0-pre is 1.0.0.
    impl Show for Release

    Release::output

    fn Release::output(self : Release, logger : &Logger) -> Unit

    Release::parse

    fn Release::parse(name : String) -> Release?

    Parse a bump name, for the CLI.

    Release::to_repr

    Release::to_string

    fn Release::to_string(self : Release) -> String

    Syntax

    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
    }

    Range grammar settings.

    A version has one spelling in SemVer 2.0.0, but ranges are not in the spec, and npm, Cargo and RubyGems each wrote their own grammar. The differences are pulled out as settings here: one parser, several grammars, the same idea as moonregex.

    Switches that govern version literals:
    • allow_v_prefix: accept v1.2.3
    • allow_equals_prefix: accept =1.2.3 as a version literal
    • allow_build_metadata: accept a +build segment
    • trim: allow whitespace around a version literal

    Switches that govern range syntax:
    • default_op: what a bare version means (npm Exact, Cargo Caret)
    • tilde_bump: how ~ finds its upper bound (npm/Cargo Patch, Ruby LastSpecified)
    • allow_caret / allow_tilde: accept ^ / ~ (~> only under LastSpecified)
    • allow_ne: accept !=
    • allow_or: accept ||
    • allow_hyphen_range: accept 1.2.3 - 2.0.0
    • allow_xrange: accept the shorthand forms 1.x, 1.2.* and bare 1.2
    • allow_empty: whether an empty string means "any version"
    • prerelease_policy: how prereleases are matched
    impl Default for Syntax
    impl Eq for Syntax
    impl Show for Syntax

    Syntax::cargo

    fn Syntax::cargo() -> Syntax

    Cargo grammar: a bare version carries a caret (1.2.3 is ^1.2.3), with , or space for conjunctions and * / 1.* wildcards; no ||, no hyphen ranges, no v.

    Syntax::default

    fn Syntax::default() -> Syntax

    The default grammar is npm's.

    Syntax::equal

    fn Syntax::equal(self : Syntax, other : Syntax) -> Bool

    Syntax::from_name

    fn Syntax::from_name(name : String) -> Syntax?

    Look up a preset by name, for the CLI and config files.

    Syntax::name

    fn Syntax::name(self : Syntax) -> String?

    The preset name for this syntax, or None for a custom mix.

    Syntax::not_equal

    fn Syntax::not_equal(x : Syntax, y : Syntax) -> Bool

    Syntax::npm

    fn Syntax::npm() -> Syntax

    npm (node-semver) grammar: a bare version is exact, with ^, ~, ||, hyphen ranges, 1.x shorthand, a v prefix, and the empty string for "any".

    Syntax::output

    fn Syntax::output(self : Syntax, logger : &Logger) -> Unit

    Syntax::ruby

    fn Syntax::ruby() -> Syntax

    RubyGems grammar: ~> is the pessimistic operator (drop the last segment, then bump), and != is allowed; no ^, ||, hyphen ranges or shorthand.

    Syntax::strict

    fn Syntax::strict() -> Syntax

    Strict SemVer 2.0.0 spelling: only = > >= < <=, joined by commas or spaces, with no ^, ~, ||, hyphen ranges, 1.x shorthand or v prefix.

    The one exception is *, which always means "any version": it is what Range::to_string prints for an empty range, so refusing it would break the round trip.

    Syntax::to_string

    fn Syntax::to_string(self : Syntax) -> String

    TildeBump

    pub(all) enum TildeBump {
    Patch
    LastSpecified
    }

    How ~ and ~> work out their upper bound.
    impl Eq for TildeBump

    TildeBump::equal

    fn TildeBump::equal(self : TildeBump, other : TildeBump) -> Bool

    TildeBump::not_equal

    fn TildeBump::not_equal(x : TildeBump, y : TildeBump) -> Bool

    Version

    A SemVer 2.0.0 version.

    The three numbers are arbitrary-precision integers, so 18446744073709551616.0.0 parses, prints and compares exactly, well past 64 bits (node-semver stops at the JS safe integer, Rust's semver crate at u64).

    The fields are private and only Version::new / Version::parse can build one, so a version never carries a leading zero or a malformed identifier list.
    impl Compare for Version
    impl Eq for Version
    impl Hash for Version
    impl Show for Version
    impl ToJson for Version

    Version::build

    fn Version::build(self : Version) -> ArrayView[String]

    The build metadata identifiers: ["build", "5"] for 1.2.3+build.5.

    Version::compare

    fn Version::compare(self : Version, other : Version) -> Int

    Version::core

    fn Version::core(self : Version) -> Version

    Keep the three numbers only, dropping prerelease and build metadata.

    Version::core_string

    fn Version::core_string(self : Version) -> String

    The major.minor.patch form, without prerelease or build metadata.

    Version::equal

    fn Version::equal(self : Version, other : Version) -> Bool

    Full equality: the numbers, the prerelease and the build metadata all match.

    That differs from precedence: 1.0.0+a and 1.0.0+b are not equal, yet rank alike.

    Version::from_bigint

    Build a version from three arbitrary-precision integers.

    Version::hash

    fn Version::hash(self : Version) -> Int

    Version::hash_combine

    fn Version::hash_combine(self : Version, hasher : Hasher) -> Unit

    Version::inc

    fn Version::inc(self : Version, release : Release) -> Version

    Apply a bump.

    Version::is_prerelease

    fn Version::is_prerelease(self : Version) -> Bool

    Whether the version carries a prerelease.

    Version::is_stable

    fn Version::is_stable(self : Version) -> Bool

    Whether this is a stable release. Build metadata does not make it unstable.

    Version::major

    The major number.

    Version::minor

    The minor number.

    Version::new

    fn Version::new(major : Int, minor : Int, patch : Int) -> Version

    Build a version from three Ints, with no prerelease and no build metadata.

    Version::not_equal

    fn Version::not_equal(x : Version, y : Version) -> Bool

    Full equality: the numbers, the prerelease and the build metadata all match.

    That differs from precedence: 1.0.0+a and 1.0.0+b are not equal, yet rank alike.

    Version::op_ge

    fn Version::op_ge(x : Version, y : Version) -> Bool

    Version::op_gt

    fn Version::op_gt(x : Version, y : Version) -> Bool

    Version::op_le

    fn Version::op_le(x : Version, y : Version) -> Bool

    Version::op_lt

    fn Version::op_lt(x : Version, y : Version) -> Bool

    Version::output

    fn Version::output(self : Version, logger : &Logger) -> Unit

    The canonical form: major.minor.patch[-prerelease][+build].

    Version::parse

    fn Version::parse(s : String, syntax? : Syntax) -> Version raise Refused

    Parse a version.

    The npm grammar is the default, so v1.2.3, =1.2.3 and surrounding whitespace are all fine. Pass syntax=Syntax::strict() to hold to the spec exactly.

    Version::patch

    The patch number.

    Version::precedence_cmp

    fn Version::precedence_cmp(self : Version, other : Version) -> Int

    Compare precedence as laid out in SemVer 2.0.0 §11.

    Returns -1, 0 or 1. Build metadata takes no part: 1.0.0+a and 1.0.0+b share the same precedence.

    Version::prerelease

    fn Version::prerelease(self : Version) -> ArrayView[Identifier]

    The prerelease identifiers: ["alpha", 1] for 1.2.3-alpha.1.

    Version::same_core

    fn Version::same_core(self : Version, other : Version) -> Bool

    Whether the three numbers match, ignoring prerelease and build metadata.

    Version::to_json

    fn Version::to_json(self : Version) -> Json

    Version::to_repr

    Version::to_string

    fn Version::to_string(self : Version) -> String

    The canonical form: major.minor.patch[-prerelease][+build].

    satisfies

    fn satisfies(version : String, range : String, syntax? : Syntax) -> Bool raise Refused

    Convenience wrapper: parse two strings and check whether the version satisfies.

    Both strings are read with the same grammar, the version through Version::parse.