diff --git a/.gitignore b/.gitignore index 65c6b5c..443f45b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ ebin/* _build erl_crash.dump *.pyc +src/ec_semver_parser.erl diff --git a/rebar.config b/rebar.config index ded76c0..6284cba 100644 --- a/rebar.config +++ b/rebar.config @@ -1,9 +1,9 @@ %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*- %% These are all only compile time dependencies -{deps, [ - {proper, "", {git, "https://github.com/manopapad/proper.git", {branch, master}}} - ]}. +{deps, [{neotoma, "", + {git, "https://github.com/seancribbs/neotoma.git", {tag, "1.5"}}}, + {proper, "", {git, "https://github.com/manopapad/proper.git", {branch, master}}}]}. {erl_first_files, ["ec_dictionary"]}. diff --git a/src/ec_semver.erl b/src/ec_semver.erl index 49a16a9..4dc83e9 100644 --- a/src/ec_semver.erl +++ b/src/ec_semver.erl @@ -7,95 +7,213 @@ %%%------------------------------------------------------------------- -module(ec_semver). --exports([ - compare/2 - ]). +-export([parse/1, + eql/2, + gt/2, + gte/2, + lt/2, + lte/2, + pes/2, + between/3]). --export_type([ - semvar/0 - ]). +%% For internal use by the ec_semver_parser peg +-export([internal_parse_version/1]). + +-export_type([semver/0, + version_string/0, + any_version/0]). %%%=================================================================== %%% Public Types %%%=================================================================== --type semvar() :: string(). --type parsed_semvar() :: {MajorVsn::integer(), - MinorVsn::integer(), - PatchVsn::integer(), - PathString::string()}. +-type major_minor_patch() :: + non_neg_integer() + | {non_neg_integer(), non_neg_integer()} + | {non_neg_integer(), non_neg_integer(), non_neg_integer()}. --type semver_tokens() :: {string(), string(), string(), string()}. +-type alpha_part() :: integer() | binary(). + +-type semver() :: {major_minor_patch(), {PreReleaseVersion::[alpha_part()], + BuildVersion::[alpha_part()]}}. + +-type version_string() :: string() | binary(). + +-type any_version() :: version_string() | semver(). %%%=================================================================== %%% API %%%=================================================================== -%% @doc Is semver version string A bigger than version string B? -%%
-%% Example: compare("3.2.5alpha", "3.10.6") returns: false
-%% 
--spec compare(VsnA::string(), VsnB::string()) -> boolean(). -compare(VsnA, VsnB) -> - compare_toks(tokens(VsnA),tokens(VsnB)). +%% @doc parse a string or binary into a valid semver representation +-spec parse(any_version()) -> semver(). +parse(Version) when erlang:is_list(Version) -> + ec_semver_parser:parse(Version); +parse(Version) when erlang:is_binary(Version) -> + ec_semver_parser:parse(Version); +parse(Version) -> + Version. + +%% @doc test for quality 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) -> + {MMPA, {AlphaA, PatchA}} = normalize(parse(VsnA)), + {MMPB, {AlphaB, PatchB}} = normalize(parse(VsnB)), + ((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)), + gt(NVsnA, NVsnB) orelse eql(NVsnA, NVsnB). + +%% @doc Test that VsnA is less than VsnB +-spec lt(any_version(), any_version()) -> boolean(). +lt(VsnA, VsnB) -> + {MMPA, {AlphaA, PatchA}} = normalize(parse(VsnA)), + {MMPB, {AlphaB, PatchB}} = normalize(parse(VsnB)), + ((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)), + lt(NVsnA, NVsnB) orelse eql(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) -> + NVsnA = normalize(parse(Vsn1)), + NVsnB = normalize(parse(Vsn2)), + NVsnMatch = normalize(parse(VsnMatch)), + gte(NVsnMatch, NVsnA) andalso + lte(NVsnMatch, NVsnB). + +%% @doc check that VsnA is Approximately greater than VsnB +%% +%% Specifying ">= 2.6.5" is an optimistic version constraint. All +%% versions greater than the one specified, including major releases +%% (e.g. 3.0.0) are allowed. +%% +%% Conversely, specifying "~> 2.6" is pessimistic about future major +%% revisions and "~> 2.6.5" is pessimistic about future minor +%% revisions. +%% +%% "~> 2.6" matches cookbooks >= 2.6.0 AND < 3.0.0 +%% "~> 2.6.5" matches cookbooks >= 2.6.5 AND < 2.7.0 +pes(VsnA, VsnB) -> + internal_pes(parse(VsnA), parse(VsnB)). + +%%%=================================================================== +%%% Friend Functions +%%%=================================================================== +%% @doc helper function for the peg grammer to parse the iolist into a semver +-spec internal_parse_version(iolist()) -> semver(). +internal_parse_version([MMP, AlphaPart, BuildPart, _]) -> + {parse_major_minor_patch(MMP), {parse_alpha_part(AlphaPart), + parse_alpha_part(BuildPart)}}. + +%% @doc helper function for the peg grammer to parse the iolist into a major_minor_patch +-spec parse_major_minor_patch(iolist()) -> major_minor_patch(). +parse_major_minor_patch([MajVsn, [], []]) -> + MajVsn; +parse_major_minor_patch([MajVsn, [<<".">>, MinVsn], []]) -> + {MajVsn, MinVsn}; +parse_major_minor_patch([MajVsn, [<<".">>, MinVsn], [<<".">>, PatchVsn]]) -> + {MajVsn, MinVsn, PatchVsn}. + +%% @doc helper function for the peg grammer to parse the iolist into an alpha part +-spec parse_alpha_part(iolist()) -> [alpha_part()]. +parse_alpha_part([]) -> + []; +parse_alpha_part([_, AV1, Rest]) -> + [erlang:iolist_to_binary(AV1) | + [format_alpha_part(Part) || Part <- Rest]]. + +%% @doc according to semver alpha parts that can be treated like +%% numbers must be. We implement that here by taking the alpha part +%% and trying to convert it to a number, if it succeeds we use +%% it. Otherwise we do not. +-spec format_alpha_part(iolist()) -> integer() | binary(). +format_alpha_part([<<".">>, AlphaPart]) -> + Bin = erlang:iolist_to_binary(AlphaPart), + try + erlang:list_to_integer(erlang:binary_to_list(Bin)) + catch + error:badarg -> + Bin + end. %%%=================================================================== %%% Internal Functions %%%=================================================================== +%% @doc normalize the semver so they can be compared +-spec normalize(semver()) -> semver(). +normalize({Vsn, Rest}) + when erlang:is_integer(Vsn) -> + {{Vsn, 0, 0}, Rest}; +normalize({{Maj, Min}, Rest}) -> + {{Maj, Min, 0}, Rest}; +normalize(Other) -> + Other. --spec tokens(semvar()) -> semver_tokens(). -tokens(Vsn) -> - [MajorVsn, MinorVsn, RawPatch] = string:tokens(Vsn, "."), - {PatchVsn, PatchString} = split_patch(RawPatch), - {MajorVsn, MinorVsn, PatchVsn, PatchString}. +%% @doc to do the pessimistic compare we need a parsed semver. This is +%% the internal implementation of the of the pessimistic run. The +%% external just ensures that versions are parsed. +internal_pes(VsnA, {{LM, LMI}, _}) -> + gte(VsnA, {{LM, LMI, 0}, {[], []}}) andalso + lt(VsnA, {{LM + 1, 0, 0}, {[], []}}); +internal_pes(VsnA, {{LM, LMI, LP}, _}) -> + gte(VsnA, {{LM, LMI, LP}, {[], []}}) + andalso + lt(VsnA, {{LM, LMI + 1, 0}, {[], []}}); +internal_pes(Vsn, LVsn) -> + gte(Vsn, LVsn). --spec split_patch(string()) -> - {PatchVsn::string(), PatchStr::string()}. -split_patch(RawPatch) -> - {PatchVsn, PatchStr} = split_patch(RawPatch, {"", ""}), - {lists:reverse(PatchVsn), PatchStr}. --spec split_patch(string(), {AccPatchVsn::string(), AccPatchStr::string()}) -> - {PatchVsn::string(), PatchStr::string()}. -split_patch([], Acc) -> - Acc; -split_patch([Dig|T], {PatchVsn, PatchStr}) when Dig >= $0 andalso Dig =< $9 -> - split_patch(T, {[Dig|PatchVsn], PatchStr}); -split_patch(PatchStr, {PatchVsn, ""}) -> - {PatchVsn, PatchStr}. --spec compare_toks(semver_tokens(), semver_tokens()) -> boolean(). -compare_toks({MajA, MinA, PVA, PSA}, {MajB, MinB, PVB, PSB}) -> - compare_toks2({to_int(MajA), to_int(MinA), to_int(PVA), PSA}, - {to_int(MajB), to_int(MinB), to_int(PVB), PSB}). - --spec compare_toks2(parsed_semvar(), parsed_semvar()) -> boolean(). -compare_toks2({MajA, _MinA, _PVA, _PSA}, {MajB, _MinB, _PVB, _PSB}) - when MajA > MajB -> - true; -compare_toks2({_Maj, MinA, _PVA, _PSA}, {_Maj, MinB, _PVB, _PSB}) - when MinA > MinB -> - true; -compare_toks2({_Maj, _Min, PVA, _PSA}, {_Maj, _Min, PVB, _PSB}) - when PVA > PVB -> - true; -compare_toks2({_Maj, _Min, _PV, ""}, {_Maj, _Min, _PV, PSB}) when PSB /= ""-> - true; -compare_toks2({_Maj, _Min, _PV, PSA}, {_Maj, _Min, _PV, ""}) when PSA /= ""-> - false; -compare_toks2({_Maj, _Min, _PV, PSA}, {_Maj, _Min, _PV, PSB}) when PSA > PSB -> - true; -compare_toks2(_ToksA, _ToksB) -> - false. - --spec to_int(string()) -> integer(). -to_int(String) -> - try - list_to_integer(String) - catch - error:badarg -> - throw(invalid_semver_string) - end. %%%=================================================================== %%% Test Functions @@ -104,18 +222,305 @@ to_int(String) -> -ifndef(NOTEST). -include_lib("eunit/include/eunit.hrl"). -split_patch_test() -> - ?assertMatch({"123", "alpha1"}, split_patch("123alpha1")). +eql_test() -> + ?assertMatch(true, eql("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, eql("1", + "1.0.0")), + ?assertMatch(true, eql("1.0", + "1.0.0")), + ?assertMatch(true, eql("1.0.0", + "1")), + ?assertMatch(true, eql("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, eql("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, not eql("1.0.0", + "1.0.1")), + ?assertMatch(true, not eql("1.0.0-alpha", + "1.0.1+alpha")), + ?assertMatch(true, not eql("1.0.0+build.1", + "1.0.1+build.2")). -compare_test() -> - ?assertMatch(true, compare("1.2.3", "1.2.3alpha")), - ?assertMatch(true, compare("1.2.3beta", "1.2.3alpha")), - ?assertMatch(true, compare("1.2.4", "1.2.3")), - ?assertMatch(true, compare("1.3.3", "1.2.3")), - ?assertMatch(true, compare("2.2.3", "1.2.3")), - ?assertMatch(true, compare("4.2.3", "3.10.3")), - ?assertMatch(false, compare("1.2.3", "2.2.3")), - ?assertThrow(invalid_semver_string, compare("1.b.2", "1.3.4")), - ?assertThrow(invalid_semver_string, compare("1.2.2", "1.3.t")). + +gt_test() -> + ?assertMatch(true, gt("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, gt("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, gt("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, gt("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, gt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, gt("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, gt("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, gt("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, gt("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, gt("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, not gt("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, not gt("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, not gt("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, not gt("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, not gt("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, not gt("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, not gt("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, not gt("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, not gt("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, not gt("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")), + ?assertMatch(true, not gt("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, not gt("1", + "1.0.0")), + ?assertMatch(true, not gt("1.0", + "1.0.0")), + ?assertMatch(true, not gt("1.0.0", + "1")), + ?assertMatch(true, not gt("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, not gt("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")). + +lt_test() -> + ?assertMatch(true, lt("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, lt("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, lt("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, lt("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, lt("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, lt("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, lt("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, lt("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, lt("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, lt("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")), + ?assertMatch(true, not lt("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, not lt("1", + "1.0.0")), + ?assertMatch(true, not lt("1.0", + "1.0.0")), + ?assertMatch(true, not lt("1.0.0", + "1")), + ?assertMatch(true, not lt("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, not lt("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, not lt("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, not lt("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, not lt("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, not lt("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, not lt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, not lt("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, not lt("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, not lt("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, not lt("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, not lt("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")). + + +gte_test() -> + ?assertMatch(true, gte("1.0.0-alpha", + "1.0.0-alpha")), + + ?assertMatch(true, gte("1", + "1.0.0")), + + ?assertMatch(true, gte("1.0", + "1.0.0")), + + ?assertMatch(true, gte("1.0.0", + "1")), + + ?assertMatch(true, gte("1.0+alpha.1", + "1.0.0+alpha.1")), + + ?assertMatch(true, gte("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + + ?assertMatch(true, gte("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, gte("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, gte("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, gte("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, gte("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, gte("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, gte("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, gte("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, gte("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, gte("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, not gte("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, not gte("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, not gte("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, not gte("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, not gte("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, not gte("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, not gte("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, not gte("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, not gte("1.0.0", + "1.0.0+build.1")), + ?assertMatch(true, not gte("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, not gte("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")). +lte_test() -> + ?assertMatch(true, lte("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, lte("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, lte("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, lte("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, lte("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, lte("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, lte("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, lte("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, lte("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, lte("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")), + ?assertMatch(true, lte("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, lte("1", + "1.0.0")), + ?assertMatch(true, lte("1.0", + "1.0.0")), + ?assertMatch(true, lte("1.0.0", + "1")), + ?assertMatch(true, lte("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, lte("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, not lt("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, not lt("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, not lt("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, not lt("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, not lt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, not lt("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, not lt("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, not lt("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, not lt("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, not lt("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")). + + +between_test() -> + ?assertMatch(true, between("1.0.0-alpha", + "1.0.0-alpha.3", + "1.0.0-alpha.2")), + ?assertMatch(true, between("1.0.0-alpha.1", + "1.0.0-beta.2", + "1.0.0-alpha.25")), + ?assertMatch(true, between("1.0.0-beta.2", + "1.0.0-beta.11", + "1.0.0-beta.7")), + ?assertMatch(true, between("1.0.0-beta.11", + "1.0.0-rc.3", + "1.0.0-rc.1")), + ?assertMatch(true, between("1.0.0-rc.1", + "1.0.0-rc.1+build.3", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, between("1.0.0-rc.1+build.1", + "1.0.0", + "1.0.0-rc.33")), + ?assertMatch(true, between("1.0.0", + "1.0.0+0.3.7", + "1.0.0+0.2")), + ?assertMatch(true, between("1.0.0+0.3.7", + "1.3.7+build", + "1.2")), + ?assertMatch(true, between("1.3.7+build", + "1.3.7+build.2.b8f12d7", + "1.3.7+build.1")), + ?assertMatch(true, between("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a", + "1.3.7+build.10.a36faa")), + ?assertMatch(true, between("1.0.0-alpha", + "1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, between("1", + "1.0.0", + "1.0.0")), + ?assertMatch(true, between("1.0", + "1.0.0", + "1.0.0")), + ?assertMatch(true, between("1.0.0", + "1", + "1")), + ?assertMatch(true, between("1.0+alpha.1", + "1.0.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, between("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, not between("1.0.0-alpha.1", + "1.0.0-alpha.22", + "1.0.0")), + ?assertMatch(true, not between("1.0.0", + "1.0.0-alpha.1", + "2.0")), + ?assertMatch(true, not between("1.0.0-beta.1", + "1.0.0-beta.11", + "1.0.0-alpha")), + ?assertMatch(true, not between("1.0.0-beta.11", "1.0.0-rc.1", "1.0.0-rc.22")). + +pes_test() -> + ?assertMatch(true, pes("2.6.0", "2.6")), + ?assertMatch(true, pes("2.7", "2.6")), + ?assertMatch(true, pes("2.8", "2.6")), + ?assertMatch(true, pes("2.9", "2.6")), + ?assertMatch(true, not pes("3.0.0", "2.6")), + ?assertMatch(true, not pes("2.5", "2.6")), + ?assertMatch(true, pes("2.6.5", "2.6.5")), + ?assertMatch(true, pes("2.6.6", "2.6.5")), + ?assertMatch(true, pes("2.6.7", "2.6.5")), + ?assertMatch(true, pes("2.6.8", "2.6.5")), + ?assertMatch(true, pes("2.6.9", "2.6.5")), + ?assertMatch(true, not pes("2.7", "2.6.5")), + ?assertMatch(true, not pes("2.5", "2.6.5")). -endif. diff --git a/src/ec_semver_parser.peg b/src/ec_semver_parser.peg new file mode 100644 index 0000000..9636d95 --- /dev/null +++ b/src/ec_semver_parser.peg @@ -0,0 +1,13 @@ +semver <- major_minor_patch ("-" alpha_part ("." alpha_part)*)? ("+" alpha_part ("." alpha_part)*)? !. + ` ec_semver:internal_parse_version(Node) ` ; + +major_minor_patch <- version_part ("." version_part)? ("." version_part)? ; + +version_part <- [0-9]+ `erlang:list_to_integer(erlang:binary_to_list(erlang:iolist_to_binary(Node)))` ; + +alpha_part <- [A-Za-z0-9-]+ ; + +%% This only exists to get around a bug in erlang where if +%% warnings_as_errors is specified `nowarn` directives are ignored + + `-compile(export_all).` \ No newline at end of file diff --git a/src/erlware_commons.app.src b/src/erlware_commons.app.src index a10e939..8a46f9c 100644 --- a/src/erlware_commons.app.src +++ b/src/erlware_commons.app.src @@ -2,19 +2,6 @@ {application, erlware_commons, [{description, "Additional standard library for Erlang"}, {vsn, "0.7.0"}, - {modules, [ - ec_talk, - ec_lists, - ec_plists, - ec_file, - ec_string, - ec_semver, - ec_date, - ec_dictionary, - ec_assoc_list, - ec_dict, - ec_gb_trees, - ec_rbdict, - ec_orddict]}, + {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}.