# [WIP] samovar
## Overview
`samovar` is an Erlang library implementing [SEMVER](https://semver.org/) standard.
Implementation is Erlang/OTP friendly by allowing use of old release version syntax.
## Supported syntaxes
### Simple range
```
1.2.3
=1.2.3
>1.2.3
<1.2.3
>=1.2.3
~1.2.3 is >=1.2.3 <1.3.0
^1.2.3 is >=1.2.3 <2.0.0
^0.2.3 is >=0.2.3 <0.3.0 (0.x.x is special)
^0.0.1 is =0.0.1 (0.0.x is special)
0.x.x is for “initial development”
1.x.x means public API is defined
^1.2 is >=1.2.0 <2.0.0 (like ^1.2.0)
~1.2 is >=1.2.0 <1.3.0 (like ~1.2.0)
^1 is >=1.0.0 <2.0.0
~1 is >=1.0.0 <2.0.0
1.x is >=1.0.0 <2.0.0
1.* is >=1.0.0 <2.0.0
1 is >=1.0.0 <2.0.0
* any version
x any version
```
### Hyphenated range
```
1.2.3 - 2.3.0 is >=1.2.3 <=2.3.4
Partial right
When the right is partial, missing pieces are assumed to be x (2.3 = 2.3.x).
1.2.3 - 2.3 is >=1.2.3 <2.4.0
1.2.3 - 2 is >=1.2.3 <3.0.0
Partial left
When the left is partial, missing pieces are assumed to be 0 (eg, 1.2.0).
1.2 - 2.3.0 is 1.2.0 - 2.3.0
```
### Combining range
```
>=0.14 <16 And (space-separated)
0.14.x || 15.x.x Or (pipe-separated)
```
## Syntax for old Erlang/OTP release
Old Erlang/OTP releases version will be internally mapped to syntaxically correct SEMVER version, on the fly,
both for version and range.
For instance `R16B` will be transcoded to `16.2.0` , or `R16B03-1` transcoded to `16.2.3-1`
This imply that `true = samovar:check("R16B03-1", ">R16B <21.2")` will be correct even if versions syntax is invalid from SEMVER standard perspective.
## API
### Check a version vs a semver range
```erlang
samovar:check(Version :: string(), Range :: string()) -> boolean() | {error, Reason :: atom()}.
```
```erlang
1> samovar:check("R16B03-1", ">R16B <21.2").
true
2> samovar:check("1.0.3", "1.0 - 1.1").
true
3> samovar:check("1.0.3", "~1.0").
true
4> samovar:check("1.1.2", "~1.0").
false
5> samovar:check("1.3", "<=1.2 || >1.4").
false
6> samovar:check("foo", "~1.2").
{error, invalid_version}
7> samovar:check("1.2", "foobar").
{error, invalid_range}
```
### Semversionize an Erlang/OTP release
```erlang
samovar:versionize(OTPVersion :: string()) -> SemverVersion :: string().
```
This function is internally used by `samovar` for old Erlang/OTP release version transcodification.
This function is however exported for users.
Note : this function is returning the input if no old Erlang/OTP release version is detected. Do not use this function to check correct semver syntax !
```erlang
1> samovar:versionize("17.5").
"17.5"
2> samovar:versionize("R16B02").
"16.2.2"
3> samovar:versionize("foo").
"foo"
```
### Parse a semver version and get a proplist
```erlang
samovar:proplist(Version :: string()) -> {ok, Proplist :: list()} | {error, Reason :: atom()}.
```
```erlang
1> samovar:proplist("14.5.8-rc1").
{ok,[{comp,[]},
{major,"14"},
{minor,"5"},
{patch,"8"},
{suffix,"rc1"},
{pre,"rc1"},
{build,[]}]}
```
Note : as `samovar` is intend to be usable on any Erlang/OTP release, map is not proposed as output format.
However proplist can easily be converted to map with :
```erlang
1> {ok, P} = samovar:proplist("14.5.8-rc1"),
2> M = maps:from_list(P).
#{build => [],comp => [],major => "14",minor => "5",
patch => "8",pre => "rc1",suffix => "rc1"}
```
### Parse a semver version and get a record
```erlang
samovar:parse(Version :: string()) -> {ok, Record :: tuple()} | {error, Reason :: atom()}.
```
This function allow to get a record with string version splitted into Major, Minor, Patch and Suffix elements.
Elements are still strings. See next functions for integer format.
```erlang
1> rr("include/samovar.hrl").
[version]
2> {ok, V} = samovar:parse("14.5.8-rc1").
{ok,#version{comp = [],major = "14",minor = "5",patch = "8",
suffix = "rc1",pre = "rc1",build = []}}
3> V#version.major .
"14"
4> {ok, X} = samovar:parse("~14.5").
{ok,#version{comp = "~",major = "14",minor = "5",patch = [],
suffix = [],pre = [],build = []}}
```
### Get individual elements of version string
```erlang
samovar:major(Version :: string()) -> Major :: integer().
samovar:minor(Version :: string()) -> Minor :: integer().
samovar:patch(Version :: string()) -> Patch :: integer().
samovar:suffix(Version :: string()) -> Suffix :: string().
samovar:prerelease(Version :: string()) -> PreRelease :: string().
samovar:build(Version :: string()) -> Build :: string().
```
```erlang
1> samovar:major("17.8.9-rc1+build001").
17
2> samovar:minor("17.8.9-rc1+build001").
8
3> samovar:patch("17.8.9-rc1+build001").
9
4> samovar:suffix("17.8.9-rc1+build001").
"rc1+build001"
5> samovar:prerelease("17.8.9-rc1+build001").
"rc1"
6> samovar:build("17.8.9-rc1+build001").
"build001"
```