Author: John D Harrison
Date: 2017-01-06

# semver: Basics

#### John D Harrison

#### 2017-01-06

The goal of this vignette is to describe the basic functionality of the `semver` package.

## Introduction

The `semver` package provides tools and functions for parsing, rendering and operating on semantic version strings. Semantic versioning is a simple set of rules and requirements that dictate how version numbers are assigned and incremented as outlined at http://semver.org.

A basic summary of how semantic versioning operates is given a version number MAJOR.MINOR.PATCH, increment the:

1. MAJOR version when you make incompatible API changes,
2. MINOR version when you add functionality in a backwards-compatible manner, and
3. PATCH version when you make backwards-compatible bug fixes.

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

## `semver` package

The `semver` package provides a wrapper for the C++14 semantic versioning parser written by Marko Živanović. The project is currently hosted on github. The Rcpp package was used to provide R bindings. Some changes were made on the C++ side as currently CRAN does not accept packages compiling under C++14 (R version 3.4.0 should allow this in future).

### Parsing/Rendering semantic versions

#### parse_version

The `parse_version` function parses a character vector containing valid semantic versioning strings returning an “svlist” object.

    library(semver)
    examples <- c("1.0.0", "2.1.3", "1.0.0-alpha", "1.0.0-alpha+1.2",
                  "1.8.2-beta.1.13", "1.8.2-beta.1.10")
    sem_versions <- parse_version(examples)
    sem_versions

    ## [1] Maj: 1 Min: 0 Pat: 0
    ##
    ## [2] Maj: 2 Min: 1 Pat: 3
    ##
    ## [3] Maj: 1 Min: 0 Pat: 0 Pre: alpha
    ##
    ## [4] Maj: 1 Min: 0 Pat: 0 Pre: alpha Bld: 1.2
    ##
    ## [5] Maj: 1 Min: 8 Pat: 2 Pre: beta.1.13
    ##
    ## [6] Maj: 1 Min: 8 Pat: 2 Pre: beta.1.10

    str(sem_versions)

    ## List of 6
    ##  $ :Class 'svptr' <externalptr>
    ##  $ :Class 'svptr' <externalptr>
    ##  $ :Class 'svptr' <externalptr>
    ##  $ :Class 'svptr' <externalptr>
    ##  $ :Class 'svptr' <externalptr>
    ##  $ :Class 'svptr' <externalptr>
    ##  - attr(*, "class")= chr "svlist"

#### render_version

The `render_version` function acts on an “svptr”/“svlist” object. It returns an R list/(list of lists) giving the major, minor and patch version as an integer and the prerelease and build version as a charcter

    render_version(sem_versions[c(1, 4)])

    ## [[1]]
    ## [[1]]$major
    ## [1] 1
    ##
    ## [[1]]$minor
    ## [1] 0
    ##
    ## [[1]]$patch
    ## [1] 0
    ##
    ## [[1]]$prerelease
    ## [1] ""
    ##
    ## [[1]]$build
    ## [1] ""
    ##
    ##
    ## [[2]]
    ## [[2]]$major
    ## [1] 1
    ##
    ## [[2]]$minor
    ## [1] 0
    ##
    ## [[2]]$patch
    ## [1] 0
    ##
    ## [[2]]$prerelease
    ## [1] "alpha"
    ##
    ## [[2]]$build
    ## [1] "1.2"

    render_version(sem_versions[[5]])

    ## $major
    ## [1] 1
    ##
    ## $minor
    ## [1] 8
    ##
    ## $patch
    ## [1] 2
    ##
    ## $prerelease
    ## [1] "beta.1.13"
    ##
    ## $build
    ## [1] ""

    str(render_version(sem_versions[[5]]))

    ## List of 5
    ##  $ major     : int 1
    ##  $ minor     : int 8
    ##  $ patch     : int 2
    ##  $ prerelease: chr "beta.1.13"
    ##  $ build     : chr ""

### Comparing versions

The `parse_version` function returns a list of `svptr` objects. These `svptr` objects represent the semantic versions. We can do comparisons like:

#### svptr Ops

    sem_versions[[1]] <= sem_versions[[5]]

    ## [1] TRUE

    sem_versions[[1]] > sem_versions[[5]]

    ## [1] FALSE

    # compare example 5, 6 (pre-release ordering matters)
    sem_versions[[5]] > sem_versions[[6]]

    ## [1] TRUE

    # compare example 3, 4 (build order does not matter)
    sem_versions[[3]] == sem_versions[[4]]

    ## [1] TRUE

#### Summary of svlist objects

You can get the min, max and range of the versions

    min(sem_versions)

    ## Maj: 1 Min: 0 Pat: 0 Pre: alpha

    max(sem_versions)

    ## Maj: 2 Min: 1 Pat: 3

    range(sem_versions)

    ## [1] Maj: 1 Min: 0 Pat: 0 Pre: alpha
    ##
    ## [2] Maj: 2 Min: 1 Pat: 3

#### Sort, Order and rank an svlist

You can sort, order and rank the versions:

    sort(sem_versions)

    ## [1] Maj: 1 Min: 0 Pat: 0 Pre: alpha
    ##
    ## [2] Maj: 1 Min: 0 Pat: 0 Pre: alpha Bld: 1.2
    ##
    ## [3] Maj: 1 Min: 0 Pat: 0
    ##
    ## [4] Maj: 1 Min: 8 Pat: 2 Pre: beta.1.10
    ##
    ## [5] Maj: 1 Min: 8 Pat: 2 Pre: beta.1.13
    ##
    ## [6] Maj: 2 Min: 1 Pat: 3

    order(sem_versions)

    ## [1] 3 4 1 6 5 2

    rank(sem_versions)

    ## [1] 3.0 6.0 1.5 1.5 5.0 4.0

#### Ops on svlist

You can also compare “svlist” objects. If the lengths of the two lists are unequal recycling occurs:

    sem_versions > sem_versions[1]

    ## [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE

### Compare to character strings

Sometimes it can be useful to compare a parsed vector of semantic versions to a character string:

    sem_versions > "1.1.0-beta"

    ## [1] FALSE  TRUE FALSE FALSE  TRUE  TRUE

    sem_versions[sem_versions > "1.1.0-beta"]

    ## [1] Maj: 2 Min: 1 Pat: 3
    ##
    ## [2] Maj: 1 Min: 8 Pat: 2 Pre: beta.1.13
    ##
    ## [3] Maj: 1 Min: 8 Pat: 2 Pre: beta.1.10

## Updating versions

If you want to change or ammend the major, minor, patch, prerelease or build fields the semver package provides a number of methods to achieve this.

### set_version

The `set_version` method operates on `svptr` and `svlist` classes. It simply changes the indicated field to the value given. Other fields in the version are unaffected.

#### svptr

For the `svptr` class the `set_version` method takes a character string argument `field` which indicates which field (major, minor etc.) to change. The `value` argument is an integer scalar when field is major, minor or patch and a character string when prerelease or build.

    library(semver)
    examples <- c("1.0.0", "2.1.3", "1.0.0-alpha", "1.0.0-alpha+1.2",
                  "1.8.2-beta.1.13", "1.8.2-beta.1.10")
    sem_versions <- parse_version(examples)
    set_version(sem_versions[[1]], "major", 2L)

    ## Maj: 2 Min: 0 Pat: 0

    set_version(sem_versions[[1]], "minor", 1L)

    ## Maj: 1 Min: 1 Pat: 0

    set_version(sem_versions[[1]], "patch", 1L)

    ## Maj: 1 Min: 0 Pat: 1

    set_version(sem_versions[[4]], "prerelease", "beta")

    ## Maj: 1 Min: 0 Pat: 0 Pre: beta Bld: 1.2

    set_version(sem_versions[[4]], "build", "bld1a")

    ## Maj: 1 Min: 0 Pat: 0 Pre: alpha Bld: bld1a

Note that changing a field with the set_version method does not change any other field.

##### Dollar assignment

As syntactic sugar for assigning using `set_version` there is a dollar assignment method for svptr classes so the following are equivalent waysto assign fields:

    sem_versions[[1]] <- set_version(sem_versions[[1]], "major", 3L)
    sem_versions[[1]]

    ## Maj: 3 Min: 0 Pat: 0

    # Syntactic sugar
    sem_versions[[1]]$minor <- 2L
    sem_versions[[1]]

    ## Maj: 3 Min: 2 Pat: 0

#### svlist

For `svlist` classes the `set_version` method expects a character vector for the `field` argument. The `value` argument is expected to be a list with integer and character elements assigning to (major, minor, patch)/(prerelease, build) fields respectively. If either the length of the field or values argeument is shorter than the number of elements in the `svlist` then recycling occurs.

    examples <- c("1.0.0", "1.8.2-beta.1.10", "2.4.6-8")
    sem_versions <- parse_version(examples)
    # recycling on the field argument
    set_version(sem_versions, "major", list(2L, 4L, 6L))

    ## [1] Maj: 2 Min: 0 Pat: 0
    ##
    ## [2] Maj: 4 Min: 8 Pat: 2 Pre: beta.1.10
    ##
    ## [3] Maj: 6 Min: 4 Pat: 6 Pre: 8

    # recycling on the value argument
    set_version(sem_versions, c("major", "minor", "patch"), list(7L))

    ## [1] Maj: 7 Min: 0 Pat: 0
    ##
    ## [2] Maj: 1 Min: 7 Pat: 2 Pre: beta.1.10
    ##
    ## [3] Maj: 2 Min: 4 Pat: 7 Pre: 8

    # assigning integer and character values
    set_version(sem_versions, c("prerelease", "minor", "build"),
                list("alpha", 3L, "build1.12"))

    ## [1] Maj: 1 Min: 0 Pat: 0 Pre: alpha
    ##
    ## [2] Maj: 1 Min: 3 Pat: 2 Pre: beta.1.10
    ##
    ## [3] Maj: 2 Min: 4 Pat: 6 Pre: 8 Bld: build1.12

### reset_version

The `reset_version` method operates on `svptr` and `svlist` classes. It changes the indicated field to the value given. Fields with lower precedence are set to default values:

    MAJOR(0L) > MINOR (0L) > PATCH (0L) > PRERELEASE ("") > BUILD ("")

#### svptr

The `reset_version` method for `svptr` classes operates similarly to the `set_version` method with fields of lower precedence being set to default values:

    examples <- c("1.8.2-beta.1.10+somebuild", "2.4.6-8")
    sem_versions <- parse_version(examples)
    reset_version(sem_versions[[1]], "major", 2L)

    ## Maj: 2 Min: 0 Pat: 0

    reset_version(sem_versions[[1]], "minor", 3L)

    ## Maj: 1 Min: 3 Pat: 0

    reset_version(sem_versions[[1]], "patch", 4L)

    ## Maj: 1 Min: 8 Pat: 4

    reset_version(sem_versions[[1]], "prerelease", "gamma")

    ## Maj: 1 Min: 8 Pat: 2 Pre: gamma

    reset_version(sem_versions[[1]], "build", "superbuild")

    ## Maj: 1 Min: 8 Pat: 2 Pre: beta.1.10 Bld: superbuild

#### svlist

The `reset_version` method for `svlist` classes operates similarly to the `set_version` method with fields of lower precedence being set to default values. Again recycling of elements occur if the length of the field/value argument is shorter than the number of elements in the `svlist`:

    examples <- c("1.8.2-beta.1.10+somebuild", "2.4.6-8")
    sem_versions <- parse_version(examples)
    # recycling on both arguments
    reset_version(sem_versions, "major", list(3L))

    ## [1] Maj: 3 Min: 0 Pat: 0
    ##
    ## [2] Maj: 3 Min: 0 Pat: 0

    # recycling on field argument
    reset_version(sem_versions, "minor", list(3L, 4L))

    ## [1] Maj: 1 Min: 3 Pat: 0
    ##
    ## [2] Maj: 2 Min: 4 Pat: 0

    # recycling on value argument
    reset_version(sem_versions, c("major", "patch"), list(4L))

    ## [1] Maj: 4 Min: 0 Pat: 0
    ##
    ## [2] Maj: 2 Min: 4 Pat: 4

    # assigning integer and character fields
    reset_version(sem_versions, c("prerelease", "minor"), list("zeta", 7L))

    ## [1] Maj: 1 Min: 8 Pat: 2 Pre: zeta
    ##
    ## [2] Maj: 2 Min: 7 Pat: 0

### increment_version

The `increment_version` method operates on `svptr` and `svlist` classes. It increments the given field with the provided value. Fields of lower precedence are set to default value.

1. Only major, minor and patch field can be incremented

2. To decrement a field provide a negative integer as the value argument

#### svptr

The `increment_version` method for `svptr` classes increments the chosen field with the given value with fields of lower precedence being set to default values:

    examples <- c("1.8.2-beta.1.10+somebuild", "2.4.6-8")
    sem_versions <- parse_version(examples)
    # incrementing versions
    increment_version(sem_versions[[1]], "major", 1L)

    ## Maj: 2 Min: 0 Pat: 0

    increment_version(sem_versions[[1]], "minor", 2L)

    ## Maj: 1 Min: 10 Pat: 0

    increment_version(sem_versions[[1]], "patch", 3L)

    ## Maj: 1 Min: 8 Pat: 5

    # decrementing versions
    increment_version(sem_versions[[1]], "major", -1L)

    ## Maj: 0 Min: 0 Pat: 0

    increment_version(sem_versions[[1]], "minor", -2L)

    ## Maj: 1 Min: 6 Pat: 0

    increment_version(sem_versions[[1]], "patch", -2L)

    ## Maj: 1 Min: 8 Pat: 0

#### svlist

The `increment_version` method for `svlist` classes takes a character vector as argument `field` and an integer vector as argument `value`. Recycling occurs as for `set_version`/`reset_version` methods:

    examples <- c("1.8.2-beta.1.10+somebuild", "2.4.6-8")
    sem_versions <- parse_version(examples)
    ## Incrementing
    # recycling on both arguments
    increment_version(sem_versions, "major", 3L)

    ## [1] Maj: 4 Min: 0 Pat: 0
    ##
    ## [2] Maj: 5 Min: 0 Pat: 0

    # recycling on field argument
    increment_version(sem_versions, "minor", c(3L, 4L))

    ## [1] Maj: 1 Min: 11 Pat: 0
    ##
    ## [2] Maj: 2 Min: 8 Pat: 0

    # recycling on value argument
    increment_version(sem_versions, c("major", "patch"), 4L)

    ## [1] Maj: 5 Min: 0 Pat: 0
    ##
    ## [2] Maj: 2 Min: 4 Pat: 10

    ## Decrementing
    # recycling on both arguments
    increment_version(sem_versions, "major", -1L)

    ## [1] Maj: 0 Min: 0 Pat: 0
    ##
    ## [2] Maj: 1 Min: 0 Pat: 0

    # recycling on field argument
    increment_version(sem_versions, "minor", c(-3L, -4L))

    ## [1] Maj: 1 Min: 5 Pat: 0
    ##
    ## [2] Maj: 2 Min: 0 Pat: 0

    # recycling on value argument
    increment_version(sem_versions, c("minor"), -4L)

    ## [1] Maj: 1 Min: 4 Pat: 0
    ##
    ## [2] Maj: 2 Min: 0 Pat: 0