Skip to main content

src/version_bump@plugins@commit_analyzer.erl

-module(version_bump@plugins@commit_analyzer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/version_bump/plugins/commit_analyzer.gleam").
-export([analyze/1, plugin/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(
    " The `commit-analyzer` plugin: determines the release type implied by a set\n"
    " of conventional commits, mirroring `@semantic-release/commit-analyzer`.\n"
    "\n"
    " Rules (the highest matching type wins):\n"
    "   - any breaking change            -> Major\n"
    "   - else any `feat`                -> Minor\n"
    "   - else any `fix` or `perf`       -> Patch\n"
    "   - otherwise                      -> no release\n"
    "\n"
    " The pure `analyze` function holds all the logic so it can be unit-tested\n"
    " without a context; `plugin/0` wraps it as the `analyze_commits` hook.\n"
).

-file("src/version_bump/plugins/commit_analyzer.gleam", 23).
?DOC(" Classify a single commit into the release type it warrants, if any.\n").
-spec classify(version_bump@commit_parser:conventional_commit()) -> gleam@option:option(version_bump@semver:release_type()).
classify(Commit) ->
    case erlang:element(6, Commit) of
        true ->
            {some, major};

        false ->
            case erlang:element(3, Commit) of
                {some, <<"feat"/utf8>>} ->
                    {some, minor};

                {some, <<"fix"/utf8>>} ->
                    {some, patch};

                {some, <<"perf"/utf8>>} ->
                    {some, patch};

                _ ->
                    none
            end
    end.

-file("src/version_bump/plugins/commit_analyzer.gleam", 37).
?DOC(
    " Keep whichever of two optional release types is higher in precedence.\n"
    " `None` means \"no release\", which is lower than any concrete type.\n"
).
-spec keep_highest(
    gleam@option:option(version_bump@semver:release_type()),
    gleam@option:option(version_bump@semver:release_type())
) -> gleam@option:option(version_bump@semver:release_type()).
keep_highest(Current, Candidate) ->
    case {Current, Candidate} of
        {none, Other} ->
            Other;

        {Other@1, none} ->
            Other@1;

        {{some, A}, {some, B}} ->
            case version_bump@semver:release_type_rank(B) > version_bump@semver:release_type_rank(
                A
            ) of
                true ->
                    {some, B};

                false ->
                    {some, A}
            end
    end.

-file("src/version_bump/plugins/commit_analyzer.gleam", 56).
?DOC(
    " Determine the release type for a list of conventional commits.\n"
    "\n"
    " Returns the highest release type any commit warrants, or `None` when no\n"
    " commit triggers a release. This is the pure core of the plugin.\n"
).
-spec analyze(list(version_bump@commit_parser:conventional_commit())) -> gleam@option:option(version_bump@semver:release_type()).
analyze(Commits) ->
    gleam@list:fold(
        Commits,
        none,
        fun(Acc, Commit) -> keep_highest(Acc, classify(Commit)) end
    ).

-file("src/version_bump/plugins/commit_analyzer.gleam", 64).
?DOC(
    " The `analyze_commits` hook: read already-parsed commits from the context and\n"
    " report the implied release type.\n"
).
-spec analyze_commits(
    version_bump@config:plugin_spec(),
    version_bump@context:context()
) -> {ok, gleam@option:option(version_bump@semver:release_type())} |
    {error, version_bump@error:release_error()}.
analyze_commits(_, Context) ->
    {ok, analyze(erlang:element(7, Context))}.

-file("src/version_bump/plugins/commit_analyzer.gleam", 72).
?DOC(" The `commit-analyzer` plugin, implementing only `analyze_commits`.\n").
-spec plugin() -> version_bump@plugin:plugin().
plugin() ->
    _record = version_bump@plugin:new(<<"commit-analyzer"/utf8>>),
    {plugin,
        erlang:element(2, _record),
        erlang:element(3, _record),
        {some, fun analyze_commits/2},
        erlang:element(5, _record),
        erlang:element(6, _record),
        erlang:element(7, _record),
        erlang:element(8, _record),
        erlang:element(9, _record),
        erlang:element(10, _record),
        erlang:element(11, _record)}.