1"""
2.. autofunction:: sphinxcontrib.bibtex.style.template.join(\
3        sep='', sep2=None, last_sep=None, other=None)
4
5.. autofunction:: sphinxcontrib.bibtex.style.template.sentence(\
6        capfirst=False, capitalize=False, add_period=True, \
7        sep=', ', sep2=None, last_sep=None, other=None)
8
9.. autofunction:: sphinxcontrib.bibtex.style.template.names(\
10        role, sep='', sep2=None, last_sep=None, other=None)
11
12.. autofunction:: sphinxcontrib.bibtex.style.template.entry_label()
13
14.. autofunction:: sphinxcontrib.bibtex.style.template.reference()
15"""
16
17from pybtex.richtext import Text
18from pybtex.style.template import (
19    Node, _format_list, FieldIsMissing, field, first_of, optional, tag
20)
21from typing import TYPE_CHECKING, Dict, Any, cast, Type
22
23from sphinxcontrib.bibtex.richtext import BaseReferenceText
24
25if TYPE_CHECKING:
26    from pybtex.richtext import BaseText
27    from pybtex.style import FormattedEntry
28
29
30# extended from pybtex: also copies the docstring into the wrapped object
31def node(f):
32    n = Node(f.__name__, f)
33    n.__doc__ = f.__doc__
34    return n
35
36
37# copied from pybtex join but extended to allow "et al" formatting
38@node
39def join(children, data, sep='', sep2=None, last_sep=None, other=None):
40    """Join text fragments together."""
41
42    if sep2 is None:
43        sep2 = sep
44    if last_sep is None:
45        last_sep = sep
46    parts = [part for part in _format_list(children, data) if part]
47    if len(parts) <= 1:
48        return Text(*parts)
49    elif len(parts) == 2:
50        return Text(sep2).join(parts)
51    elif other is None:
52        return Text(last_sep).join([Text(sep).join(parts[:-1]), parts[-1]])
53    else:
54        return Text(parts[0], other)
55
56
57# copied from pybtex names but using the new join
58@node
59def sentence(children, data, capfirst=False, capitalize=False, add_period=True,
60             sep=', ', sep2=None, last_sep=None, other=None):
61    """Join text fragments, capitalize the first letter,
62    and add a period to the end.
63    """
64    text = join(sep=sep, sep2=sep2, last_sep=last_sep, other=other)[
65        children
66    ].format_data(data)
67    if capfirst:
68        text = text.capfirst()
69    if capitalize:
70        text = text.capitalize()
71    if add_period:
72        text = text.add_period()
73    return text
74
75
76# copied from pybtex names but using the new join allowing "et al" formatting
77@node
78def names(children, data, role, **kwargs):
79    """Return formatted names."""
80    assert not children
81    try:
82        persons = data['entry'].persons[role]
83    except KeyError:
84        raise FieldIsMissing(role, data['entry'])
85    style = data['style']
86    formatted_names = [
87        style.person.style_plugin.format(person, style.person.abbreviate)
88        for person in persons]
89    return join(**kwargs)[formatted_names].format_data(data)
90
91
92@node
93def entry_label(children, data) -> "BaseText":
94    """Node for inserting the label of a formatted entry."""
95    assert not children
96    entry = cast("FormattedEntry", data['formatted_entry'])
97    return Text(entry.label)
98
99
100@node
101def reference(children, data: Dict[str, Any]):
102    """Node for inserting a citation reference. The children of the node
103    comprise the content of the reference, and any referencing information
104    is stored in the *reference_info* key of the *data*.
105    The data must also contain a *style* key pointing to the corresponding
106    :class:`~sphinxcontrib.bibtex.style.referencing.BaseReferenceStyle`.
107    """
108    parts = _format_list(children, data)
109    info = data['reference_info']
110    reference_text_class: Type[BaseReferenceText] \
111        = data['reference_text_class']
112    return reference_text_class(info, *parts)
113
114
115@node
116def footnote_reference(children, data: Dict[str, Any]):
117    """Node for inserting a footnote reference. The children of the node
118    comprise the content of the reference, and any referencing information
119    is stored in the *reference_info* key of the *data*.
120    The data must also contain a *style* key pointing to the corresponding
121    :class:`~sphinxcontrib.bibtex.style.referencing.BaseReferenceStyle`.
122    """
123    assert not children
124    info = data['reference_info']
125    reference_text_class: Type[BaseReferenceText] \
126        = data['reference_text_class']
127    # we need to give the footnote text some fake content
128    # otherwise pybtex richtext engine will mess things up
129    return reference_text_class(info, '#')
130
131
132@node
133def year(children, data: Dict[str, Any]) -> "BaseText":
134    assert not children
135    return first_of[optional[field('year')], 'n.d.'].format_data(data)
136
137
138@node
139def author_or_editor_or_title(children, data, **kwargs):
140    assert not children
141    return first_of[
142        optional[names('author', **kwargs)],
143        optional[names('editor', **kwargs)],
144        tag('em')[field('title')]].format_data(data)
145