Analyzes a list of :class:`~braulio.git.Commit` objects searching for
    messages that match a given message convention and extract metadata from
    them.

    A message convention is determined by ``label_pattern``, which is not a
    regular expression pattern. Instead it must be a string literals with
    placeholders that indicates metadata information in a given position of
    the commit message. The possible placeholders are ``{type}``, ``{scope}``
    and ``{subject}``.

    The ``label_position`` argument dictates where (header|footer) to look in
    the commit message for the pattern passed in ```label_pattern``.

    ``{subject}`` must be included in ``label_pattern`` just if the metadata is
    in the header, otherwise must be omitted.

    Examples.

    If ``label_position`` is equal to **header**, in order to match the next
    commit message::

        fix(cli): Ensure --help option doesn't hang

    The pattern must be ``{type}({scope}): {subject}``, where the metadata
    information extracted will be::

        {
            'type': 'fix',
            'scope': 'cli',
            'subject': 'Ensure --help option doesn't hang'
        }

def commit_analyzer(commits, label_pattern, label_position="footer"):
    """Analyzes a list of :class:`~braulio.git.Commit` objects searching for
    messages that match a given message convention and extract metadata from
    them.

    A message convention is determined by ``label_pattern``, which is not a
    regular expression pattern. Instead it must be a string literals with
    placeholders that indicates metadata information in a given position of
    the commit message. The possible placeholders are ``{type}``, ``{scope}``
    and ``{subject}``.

    The ``label_position`` argument dictates where (header|footer) to look in
    the commit message for the pattern passed in ```label_pattern``.

    ``{subject}`` must be included in ``label_pattern`` just if the metadata is
    in the header, otherwise must be omitted.

    Examples.

    If ``label_position`` is equal to **header**, in order to match the next
    commit message::

        fix(cli): Ensure --help option doesn't hang

    The pattern must be ``{type}({scope}): {subject}``, where the metadata
    information extracted will be::

        {
            'type': 'fix',
            'scope': 'cli',
            'subject': 'Ensure --help option doesn't hang'
        }
    """

    # Internally, a real regular expression pattern is used
    pattern_string = re.escape(label_pattern)

    # Capturing group patterns
    action_cgp = r"(?P<type>\w+)"
    scope_cgp = r"(?P<scope>\w*)"
    subject_cgp = r"(?P<subject>.+)"

    pattern_string = pattern_string.replace(r"\{type\}", action_cgp).replace(
        r"\{scope\}", scope_cgp
    )
    if label_position == "header":
        pattern_string = pattern_string.replace(r"\{subject\}", subject_cgp)

    regexp_pattern = re.compile(pattern_string)

    semantic_commits = []

    for commit in commits:
        text = commit.header if label_position == "header" else commit.footer
        match = regexp_pattern.search(text)

        if not match:
            continue

        metadata = match.groupdict()

        if label_position == "footer":
            metadata["subject"] = commit.header

        sc = SemanticCommit(
            subject=metadata["subject"].strip(),
            type=metadata["type"],
            scope=metadata.get("scope") or None,
            message=commit.message,
        )

        semantic_commits.append(sc)

    return semantic_commits