type identifier =
| Str of string
| Int of int
type t = {
major: int;
minor: int;
patch: int;
prerelease: identifier list;
build: identifier list;
}
let zero = { major = 0; minor = 0; patch = 0; prerelease = []; build = [] }
let compare_identifiers a b =
match (a, b) with
| (Int _, Str _) -> -1
| (Str _, Int _) -> 1
| (Int a, Int b) -> a - b
| (Str a, Str b) -> String.compare a b
let compare_identifier_lists =
let rec compare_sets a b =
match (a, b) with
| ([], []) -> 0
| (_ :: _, []) -> 1
| ([], _ :: _) -> -1
| (a_hd :: a_tl, b_hd :: b_tl) ->
let k = compare_identifiers a_hd b_hd in
if k <> 0 then
k
else
compare_sets a_tl b_tl
in
fun a b ->
match (a, b) with
| ([], []) -> 0
| (_ :: _, []) -> -1
| ([], _ :: _) -> 1
| (_, _) -> compare_sets a b
let compare_precedence =
let compare_ints a b () = a - b in
let compare_pre a b () = compare_identifier_lists a b in
let ( >>= ) k f =
if k <> 0 then
k
else
f ()
in
fun { major = a_major; minor = a_minor; patch = a_patch; prerelease = a_pre; build = _ }
{ major = b_major; minor = b_minor; patch = b_patch; prerelease = b_pre; build = _ } ->
( 0
>>= compare_ints a_major b_major
>>= compare_ints a_minor b_minor
>>= compare_ints a_patch b_patch
>>= compare_pre a_pre b_pre
: int )
let compare a b =
let k = compare_precedence a b in
if k <> 0 then
k
else
let { build = a_build; _ } = a in
let { build = b_build; _ } = b in
compare_identifier_lists a_build b_build
let incr_major { major; _ } = { zero with major = succ major }
let incr_minor { major; minor; _ } = { zero with major; minor = succ minor }
let incr_patch { major; minor; patch; _ } = { zero with major; minor; patch = succ patch }
let string_of_identifier = function
| Int x -> string_of_int x
| Str x -> x
let to_string { major; minor; patch; prerelease; build } =
let prerelease =
match prerelease with
| [] -> ""
| parts -> "-" ^ (parts |> List.map string_of_identifier |> String.concat ".")
in
let build =
match build with
| [] -> ""
| parts -> "+" ^ (parts |> List.map string_of_identifier |> String.concat ".")
in
Printf.sprintf "%d.%d.%d%s%s" major minor patch prerelease build