• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Sphinx directives for Pigweed SEEDs"""
15
16
17import docutils
18from docutils import nodes
19import docutils.statemachine
20
21# pylint: disable=consider-using-from-import
22import docutils.parsers.rst.directives as directives  # type: ignore
23
24# pylint: enable=consider-using-from-import
25from sphinx.application import Sphinx as SphinxApplication
26from sphinx.util.docutils import SphinxDirective
27
28from sphinx_design.cards import CardDirective
29
30
31def status_choice(arg) -> str:
32    return directives.choice(
33        arg,
34        (
35            'draft',
36            'open_for_comments',
37            'intent_approved',
38            'last_call',
39            'accepted',
40            'rejected',
41        ),
42    )
43
44
45def parse_status(arg) -> str:
46    """Support variations on the status choices.
47
48    For example, you can use capital letters and spaces.
49    """
50
51    return status_choice('_'.join([token.lower() for token in arg.split(' ')]))
52
53
54def status_badge(seed_status: str, badge_status) -> str:
55    """Given a SEED status, return the status badge for rendering."""
56
57    return (
58        ':bdg-primary:'
59        if seed_status == badge_status
60        else ':bdg-secondary-line:'
61    )
62
63
64def cl_link(cl_num):
65    return (
66        f'`pwrev/{cl_num} '
67        '<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/'
68        f'{cl_num}>`_'
69    )
70
71
72class PigweedSeedDirective(SphinxDirective):
73    """Directive registering & rendering SEED metadata."""
74
75    required_arguments = 0
76    final_argument_whitespace = True
77    has_content = True
78    option_spec = {
79        'number': directives.positive_int,
80        'name': directives.unchanged_required,
81        'status': parse_status,
82        'proposal_date': directives.unchanged_required,
83        'cl': directives.positive_int_list,
84        'authors': directives.unchanged_required,
85        'facilitator': directives.unchanged_required,
86    }
87
88    def _try_get_option(self, option: str):
89        """Try to get an option by name and raise on failure."""
90
91        try:
92            return self.options[option]
93        except KeyError:
94            raise self.error(f' :{option}: option is required')
95
96    def run(self) -> list[nodes.Node]:
97        seed_number = '{:04d}'.format(self._try_get_option('number'))
98        seed_name = self._try_get_option('name')
99        status = self._try_get_option('status')
100        proposal_date = self._try_get_option('proposal_date')
101        cl_nums = self._try_get_option('cl')
102        authors = self._try_get_option('authors')
103        facilitator = self._try_get_option('facilitator')
104
105        title = (
106            f':fas:`seedling` SEED-{seed_number}: :ref:'
107            f'`{seed_name}<seed-{seed_number}>`\n'
108        )
109
110        authors_heading = 'Authors' if len(authors.split(',')) > 1 else 'Author'
111
112        self.content = docutils.statemachine.StringList(
113            [
114                ':octicon:`comment-discussion` Status:',
115                f'{status_badge(status, "open_for_comments")}'
116                '`Open for Comments`',
117                ':octicon:`chevron-right`',
118                f'{status_badge(status, "intent_approved")}'
119                '`Intent Approved`',
120                ':octicon:`chevron-right`',
121                f'{status_badge(status, "last_call")}`Last Call`',
122                ':octicon:`chevron-right`',
123                f'{status_badge(status, "accepted")}`Accepted`',
124                ':octicon:`kebab-horizontal`',
125                f'{status_badge(status, "rejected")}`Rejected`',
126                '\n',
127                f':octicon:`calendar` Proposal Date: {proposal_date}',
128                '\n',
129                ':octicon:`code-review` CL: ',
130                ', '.join([cl_link(cl_num) for cl_num in cl_nums]),
131                '\n',
132                f':octicon:`person` {authors_heading}: {authors}',
133                '\n',
134                f':octicon:`person` Facilitator: {facilitator}',
135            ]
136        )
137
138        card = CardDirective.create_card(
139            inst=self,
140            arguments=[title],
141            options={},
142        )
143
144        return [card]
145
146
147def setup(app: SphinxApplication):
148    app.add_directive('seed', PigweedSeedDirective)
149
150    return {
151        'parallel_read_safe': True,
152        'parallel_write_safe': True,
153    }
154