Packages
rabbit_common
4.3.4
4.3.4
4.2.1
4.2.0
retired
4.2.0-rc.1
retired
4.1.6
4.1.5
retired
4.1.5-rc.2
retired
4.1.5-rc.1
4.0.3
4.0.3-rc.1
4.0.2
4.0.2-rc.2
4.0.2-rc.1
4.0.1
4.0.0
4.0.0-rc.2
4.0.0-rc.1
3.13.7
3.13.6
3.13.5
3.13.4
3.13.3
3.13.2
3.13.2-rc.1
3.13.1
3.13.0
3.13.0-rc.6
3.13.0-rc.5
3.13.0-rc.4
3.13.0-rc.3
3.13.0-rc.2
3.13.0-rc.1
3.12.14
3.12.13
3.12.12
3.12.11
3.12.10
3.12.9
3.12.8
3.12.7
3.12.6
3.12.5
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.12.0-rc.4
3.12.0-rc.3
3.12.0-rc.2
3.12.0-rc.1
3.11.28
3.11.27
3.11.26
3.11.25
3.11.24
3.11.23
3.11.22
3.11.21
3.11.20
3.11.19
3.11.18
3.11.17
3.11.16
3.11.15
3.11.14
3.11.13
3.11.12
3.11.11
3.11.10
3.11.9
3.11.8
3.11.7
3.11.6
3.11.5
3.11.4
3.11.3
3.11.2
3.11.1
3.11.0
3.11.0-rc.2
3.11.0-rc.1
3.11.0-1
3.10.25
3.10.24
3.10.23
3.10.22
3.10.21
3.10.20
3.10.19
3.10.18
3.10.17
3.10.16
3.10.15
3.10.14
3.10.13
3.10.12
3.10.11
3.10.10
3.10.9
3.10.8
3.10.7
3.10.6
3.10.5
3.10.4
3.10.3
3.10.2
3.10.1
3.10.0
3.10.0-rc.6
3.10.0-rc.5
3.9.29
3.9.28
3.9.27
3.9.26
3.9.25
3.9.24
3.9.23
3.9.22
3.9.21
3.9.20
3.9.19
3.9.18
3.9.17
3.9.16
3.9.15
3.9.11
3.9.10
3.9.9
3.9.8
3.9.7
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.8.35
3.8.34
3.8.33
3.8.32
3.8.31
3.8.30
3.8.26
3.8.25
3.8.24
3.8.23
3.8.22
3.8.21
3.8.20
3.8.19
3.8.14
3.8.12-rc.3
3.8.12-rc.2
3.8.12-rc.1
3.8.11
3.8.10
3.8.10-rc.6
3.8.10-rc.5
3.8.10-rc.1
3.8.9
3.8.8
3.8.7
3.8.6
3.8.6-rc.2
3.8.6-rc.1
3.8.5
3.8.5-rc.2
3.8.5-rc.1
3.8.4
3.8.4-rc.3
3.8.4-rc.1
3.8.3
3.8.3-rc.2
3.8.3-rc.1
3.8.2
3.8.2-rc.1
3.8.1
3.8.1-rc.1
3.8.0
3.8.0-rc.3
3.8.0-rc.2
3.8.0-rc.1
3.7.28
3.7.27
3.7.27-rc.2
3.7.27-rc.1
3.7.26
3.7.25
3.7.25-rc.1
3.7.24
3.7.24-rc.2
3.7.24-rc.1
3.7.23
3.7.23-rc.1
3.7.22
3.7.22-rc.2
3.7.22-rc.1
3.7.21
3.7.20
3.7.20-rc.2
3.7.20-rc.1
3.7.19
3.7.18
3.7.18-rc.1
3.7.17
3.7.17-rc.3
3.7.17-rc.2
3.7.17-rc.1
retired
3.7.16
retired
3.7.16-rc.4
retired
3.7.16-rc.3
retired
3.7.16-rc.2
retired
3.7.16-rc.1
retired
3.7.16-beta.1
3.7.15
3.7.14
3.7.14-rc.2
3.7.14-rc.1
3.7.13
3.7.13-rc.2
3.7.13-rc.1
3.7.12
3.7.12-rc.2
3.7.12-rc.1
3.7.11
3.7.11-rc.2
3.7.11-rc.1
3.7.10-rc.4
3.7.10-rc.3
3.7.10-rc.2
3.7.10-rc.1
3.7.9
3.7.9-rc.3
3.7.9-rc.2
3.7.8
3.7.8-rc.4
3.7.8-rc.3
3.7.8-rc.2
3.7.8-rc.1
3.7.7
3.7.7-rc.2
3.7.7-rc.1
3.7.6
3.7.6-rc.2
3.7.6-rc.1
3.7.5
3.7.5-rc.1
3.7.4
3.7.4-rc.4
3.7.4-rc.3
3.7.4-rc.2
3.7.4-rc.1
3.7.3
3.7.3-rc.2
3.7.3-rc.1
3.7.2
3.7.1
3.7.0-rc.2
3.7.0-alpha.544
3.7.0-alpha.542
3.6.16
3.6.16-rc.1
3.6.15
3.6.15-rc.1
3.6.14
3.6.13
3.6.12
3.6.11
3.6.10
3.6.9
3.6.8
3.6.7
3.6.7-pre.1
3.5.6
3.5.0
3.4.0
3.3.5
3.0.2
0.0.0-rc.1
Modules shared by rabbitmq-server and rabbitmq-erlang-client
Current section
Files
Jump to
Current section
Files
src/rabbit_semver.erl
%%% vi:ts=4 sw=4 et
%%% Imported from https://github.com/erlware/erlware_commons.git
%%% Commit 09168347525916e291c8aa6e3073e260e5f4a116
%%% - We export normalize/1.
%%% - We add a few more testcases around string/binary comparison.
%%%-------------------------------------------------------------------
%%% @copyright (C) 2011, Erlware LLC
%%% @doc
%%% Helper functions for working with semver versioning strings.
%%% See https://semver.org/ for the spec.
%%% @end
%%%-------------------------------------------------------------------
-module(rabbit_semver).
-export([parse/1,
format/1,
normalize_then_format/1,
eql/2,
gt/2,
gte/2,
lt/2,
lte/2,
pes/2,
normalize/1,
between/3]).
%% For internal use by the rabbit_semver_parser peg
-export([internal_parse_version/1]).
-export_type([semver/0,
version_string/0,
any_version/0]).
%%%===================================================================
%%% Public Types
%%%===================================================================
-type version_element() :: non_neg_integer() | binary().
-type major_minor_patch_minpatch() ::
version_element()
| {version_element(), version_element()}
| {version_element(), version_element(), version_element()}
| {version_element(), version_element(),
version_element(), version_element()}.
-type alpha_part() :: integer() | binary() | string().
-type alpha_info() :: {PreRelease::[alpha_part()],
BuildVersion::[alpha_part()]}.
-type semver() :: {major_minor_patch_minpatch(), alpha_info()}.
-type version_string() :: string() | binary().
-type any_version() :: version_string() | semver().
%%%===================================================================
%%% API
%%%===================================================================
%% @doc Parse a version string or binary into a semver tuple.
-spec parse(any_version()) -> semver().
parse(Version) when is_list(Version) ->
case rabbit_semver_parser:parse(Version) of
{fail, _} ->
{iolist_to_binary(Version), {[],[]}};
Good ->
Good
end;
parse(Version) when is_binary(Version) ->
case rabbit_semver_parser:parse(Version) of
{fail, _} ->
{Version, {[],[]}};
Good ->
Good
end;
parse(Version) ->
Version.
-spec format(semver()) -> iolist().
format({Maj, {AlphaPart, BuildPart}})
when is_integer(Maj);
is_binary(Maj) ->
[format_version_part(Maj),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)];
format({{Maj, Min}, {AlphaPart, BuildPart}}) ->
[format_version_part(Maj), ".",
format_version_part(Min),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)];
format({{Maj, Min, Patch}, {AlphaPart, BuildPart}}) ->
[format_version_part(Maj), ".",
format_version_part(Min), ".",
format_version_part(Patch),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)];
format({{Maj, Min, Patch, MinPatch}, {AlphaPart, BuildPart}}) ->
[format_version_part(Maj), ".",
format_version_part(Min), ".",
format_version_part(Patch), ".",
format_version_part(MinPatch),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)].
-spec normalize_then_format(any_version()) -> binary().
normalize_then_format(Version) ->
{{Maj, Min, Patch, MinPatch}, _} = normalize(parse(Version)),
iolist_to_binary(format({{Maj, Min, Patch, MinPatch}, {[], []}})).
-spec format_version_part(integer() | binary()) -> iolist().
format_version_part(Vsn) when is_integer(Vsn) ->
integer_to_list(Vsn);
format_version_part(Vsn) when is_binary(Vsn) ->
Vsn.
%% @doc Test for equality between semver versions.
-spec eql(any_version(), any_version()) -> boolean().
eql(VsnA, VsnB) ->
NVsnA = normalize(parse(VsnA)),
NVsnB = normalize(parse(VsnB)),
NVsnA =:= NVsnB.
%% @doc Test that VsnA is greater than VsnB.
-spec gt(any_version(), any_version()) -> boolean().
gt(VsnA, VsnB) ->
gt_normalized(normalize(parse(VsnA)), normalize(parse(VsnB))).
-spec gt_normalized(semver(), semver()) -> boolean().
gt_normalized({MMPA, {AlphaA, PatchA}}, {MMPB, {AlphaB, PatchB}}) ->
(MMPA > MMPB)
orelse
((MMPA =:= MMPB)
andalso
((AlphaA =:= [] andalso AlphaB =/= [])
orelse
((not (AlphaB =:= [] andalso AlphaA =/= []))
andalso
(AlphaA > AlphaB))))
orelse
((MMPA =:= MMPB)
andalso
(AlphaA =:= AlphaB)
andalso
((PatchB =:= [] andalso PatchA =/= [])
orelse
PatchA > PatchB)).
%% @doc Test that VsnA is greater than or equal to VsnB.
-spec gte(any_version(), any_version()) -> boolean().
gte(VsnA, VsnB) ->
NVsnA = normalize(parse(VsnA)),
NVsnB = normalize(parse(VsnB)),
NVsnA =:= NVsnB orelse gt_normalized(NVsnA, NVsnB).
%% @doc Test that VsnA is less than VsnB.
-spec lt(any_version(), any_version()) -> boolean().
lt(VsnA, VsnB) ->
lt_normalized(normalize(parse(VsnA)), normalize(parse(VsnB))).
-spec lt_normalized(semver(), semver()) -> boolean().
lt_normalized({MMPA, {AlphaA, PatchA}}, {MMPB, {AlphaB, PatchB}}) ->
(MMPA < MMPB)
orelse
((MMPA =:= MMPB)
andalso
((AlphaB =:= [] andalso AlphaA =/= [])
orelse
((not (AlphaA =:= [] andalso AlphaB =/= []))
andalso
(AlphaA < AlphaB))))
orelse
((MMPA =:= MMPB)
andalso
(AlphaA =:= AlphaB)
andalso
((PatchA =:= [] andalso PatchB =/= [])
orelse
PatchA < PatchB)).
%% @doc Test that VsnA is less than or equal to VsnB.
-spec lte(any_version(), any_version()) -> boolean().
lte(VsnA, VsnB) ->
NVsnA = normalize(parse(VsnA)),
NVsnB = normalize(parse(VsnB)),
NVsnA =:= NVsnB orelse lt_normalized(NVsnA, NVsnB).
%% @doc Test that VsnMatch is greater than or equal to Vsn1 and
%% less than or equal to Vsn2.
-spec between(any_version(), any_version(), any_version()) -> boolean().
between(Vsn1, Vsn2, VsnMatch) ->
NVsn1 = normalize(parse(Vsn1)),
NVsn2 = normalize(parse(Vsn2)),
NVsnMatch = normalize(parse(VsnMatch)),
(NVsnMatch =:= NVsn1 orelse gt_normalized(NVsnMatch, NVsn1)) andalso
(NVsnMatch =:= NVsn2 orelse lt_normalized(NVsnMatch, NVsn2)).
%% @doc Pessimistic version constraint check (the ~> operator).
%%
%% "~> 2.6" matches versions >= 2.6.0 and < 3.0.0
%% "~> 2.6.5" matches versions >= 2.6.5 and < 2.7.0
-spec pes(any_version(), any_version()) -> boolean().
pes(VsnA, VsnB) ->
internal_pes(parse(VsnA), parse(VsnB)).
%%%===================================================================
%%% Friend Functions
%%%===================================================================
%% @doc Callback for the PEG parser to build a semver tuple from parsed tokens.
-spec internal_parse_version(iolist()) -> semver().
internal_parse_version([MMP, AlphaPart, BuildPart, _]) ->
{parse_major_minor_patch_minpatch(MMP), {parse_alpha_part(AlphaPart),
parse_alpha_part(BuildPart)}}.
-spec parse_major_minor_patch_minpatch(iolist()) -> major_minor_patch_minpatch().
parse_major_minor_patch_minpatch([MajVsn, [], [], []]) ->
strip_maj_version(MajVsn);
parse_major_minor_patch_minpatch([MajVsn, [<<".">>, MinVsn], [], []]) ->
{strip_maj_version(MajVsn), MinVsn};
parse_major_minor_patch_minpatch([MajVsn,
[<<".">>, MinVsn],
[<<".">>, PatchVsn], []]) ->
{strip_maj_version(MajVsn), MinVsn, PatchVsn};
parse_major_minor_patch_minpatch([MajVsn,
[<<".">>, MinVsn],
[<<".">>, PatchVsn],
[<<".">>, MinPatch]]) ->
{strip_maj_version(MajVsn), MinVsn, PatchVsn, MinPatch}.
-spec parse_alpha_part(iolist()) -> [alpha_part()].
parse_alpha_part([]) ->
[];
parse_alpha_part([_, AV1, Rest]) ->
[iolist_to_binary(AV1) |
[format_alpha_part(Part) || Part <- Rest]].
%% Per semver spec, numeric identifiers must be compared as integers.
-spec format_alpha_part(iolist()) -> integer() | binary().
format_alpha_part([<<".">>, AlphaPart]) ->
Bin = iolist_to_binary(AlphaPart),
try
binary_to_integer(Bin)
catch
error:badarg ->
Bin
end.
%%%===================================================================
%%% Internal Functions
%%%===================================================================
-spec strip_maj_version(iolist()) -> version_element().
strip_maj_version([<<"v">>, MajVsn]) ->
MajVsn;
strip_maj_version([[], MajVsn]) ->
MajVsn;
strip_maj_version(MajVsn) ->
MajVsn.
-spec to_list(integer() | binary() | string()) -> string() | binary().
to_list(Detail) when is_integer(Detail) ->
integer_to_list(Detail);
to_list(Detail) when is_list(Detail); is_binary(Detail) ->
Detail.
-spec format_vsn_rest(binary() | string(), [integer() | binary()]) -> iolist().
format_vsn_rest(_TypeMark, []) ->
[];
format_vsn_rest(TypeMark, [Head | Rest]) ->
[TypeMark, Head |
[[".", to_list(Detail)] || Detail <- Rest]].
%% @doc Normalize a semver tuple to a 4-part version for comparison.
-spec normalize(semver()) -> semver().
normalize({Vsn, {Alpha, Build}}) when is_binary(Vsn) ->
case extract_version_from_build(Build) of
{ok, ExtractedVsn} ->
{normalize_version(ExtractedVsn), {Alpha, Build}};
none ->
{{Vsn, 0, 0, 0}, {Alpha, Build}}
end;
normalize({Vsn, Rest}) when is_integer(Vsn) ->
{{Vsn, 0, 0, 0}, Rest};
normalize({{Maj, Min}, Rest}) ->
{{Maj, Min, 0, 0}, Rest};
normalize({{Maj, Min, Patch}, Rest}) ->
{{Maj, Min, Patch, 0}, Rest};
normalize({{_, _, _, _}, {_, _}} = Vsn) ->
Vsn.
-spec normalize_version(major_minor_patch_minpatch()) ->
{version_element(), non_neg_integer(), non_neg_integer(), non_neg_integer()}.
normalize_version(Vsn) when is_integer(Vsn) ->
{Vsn, 0, 0, 0};
normalize_version({Maj, Min}) ->
{Maj, Min, 0, 0};
normalize_version({Maj, Min, Patch}) ->
{Maj, Min, Patch, 0};
normalize_version({Maj, Min, Patch, MinPatch}) ->
{Maj, Min, Patch, MinPatch}.
%% Extracts version from build metadata for prefixed versions like
%% "tanzu+rabbitmq.v3.13.12.dev" where the actual version is embedded.
-spec extract_version_from_build([alpha_part()]) ->
{ok, major_minor_patch_minpatch()} | none.
extract_version_from_build([]) ->
none;
extract_version_from_build([<<"v", Rest/binary>> | T]) ->
try
Major = binary_to_integer(Rest),
build_version_tuple(Major, T)
catch
error:badarg ->
extract_version_from_build(T)
end;
extract_version_from_build([_ | T]) ->
extract_version_from_build(T).
-spec build_version_tuple(non_neg_integer(), [alpha_part()]) ->
{ok, major_minor_patch_minpatch()}.
build_version_tuple(Major, [Minor, Patch, MinPatch | _])
when is_integer(Minor), is_integer(Patch), is_integer(MinPatch) ->
{ok, {Major, Minor, Patch, MinPatch}};
build_version_tuple(Major, [Minor, Patch | _])
when is_integer(Minor), is_integer(Patch) ->
{ok, {Major, Minor, Patch}};
build_version_tuple(Major, [Minor | _]) when is_integer(Minor) ->
{ok, {Major, Minor}};
build_version_tuple(Major, _) ->
{ok, Major}.
-spec internal_pes(semver(), semver()) -> boolean().
internal_pes(VsnA, {{LM, LMI}, _})
when is_integer(LM), is_integer(LMI) ->
gte(VsnA, {{LM, LMI, 0}, {[], []}}) andalso
lt(VsnA, {{LM + 1, 0, 0, 0}, {[], []}});
internal_pes(VsnA, {{LM, LMI, LP}, _})
when is_integer(LM), is_integer(LMI), is_integer(LP) ->
gte(VsnA, {{LM, LMI, LP}, {[], []}}) andalso
lt(VsnA, {{LM, LMI + 1, 0, 0}, {[], []}});
internal_pes(VsnA, {{LM, LMI, LP, LMP}, _})
when is_integer(LM), is_integer(LMI), is_integer(LP), is_integer(LMP) ->
gte(VsnA, {{LM, LMI, LP, LMP}, {[], []}}) andalso
lt(VsnA, {{LM, LMI, LP + 1, 0}, {[], []}});
internal_pes(Vsn, LVsn) ->
gte(Vsn, LVsn).