1# Copyright 2024 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"""Generates the modules index table on //docs/modules.rst.""" 15 16import json 17import os 18import sys 19 20from sphinx.application import Sphinx 21 22 23try: # Bazel location for the data 24 from python.runfiles import runfiles # type: ignore 25 26 r = runfiles.Create() 27 modules_file = r.Rlocation('pigweed/PIGWEED_MODULES') 28 r = runfiles.Create() 29 metadata_file = r.Rlocation('pigweed/docs/module_metadata.json') 30except ImportError: # GN location for the data 31 modules_file = f'{os.environ["PW_ROOT"]}/PIGWEED_MODULES' 32 metadata_file = f'{os.environ["PW_ROOT"]}/docs/module_metadata.json' 33with open(modules_file, 'r') as f: 34 # The complete, authoritative list of modules. 35 complete_pigweed_modules_list = f.read().splitlines() 36with open(metadata_file, 'r') as f: 37 # Module metadata such as supported languages and status. 38 metadata = json.load(f) 39 40 41def build_status_badge(status): 42 """Styles the module status as a clickable badge.""" 43 # This default value should never get used but it's a valid 44 # neutral styling in case something goes wrong and it leaks through. 45 badge_type = 'info' 46 if status == 'stable': 47 badge_type = 'primary' 48 elif status == 'unstable': 49 badge_type = 'secondary' 50 elif status == 'experimental': 51 badge_type = 'warning' 52 elif status == 'deprecated': 53 badge_type = 'danger' 54 else: 55 msg = f'[modules_index.py] error: invalid module status ("{status}")' 56 sys.exit(msg) 57 # Use bdg-ref-* to make the badge link to the glossary definition for 58 # "stable", "experimental", etc. 59 # https://sphinx-design.readthedocs.io/en/latest/badges_buttons.html 60 return f':bdg-{badge_type}:`{status}`' 61 62 63def build_row(module_name: str): 64 """Builds a row of data for the table. Each module gets a row.""" 65 ref = f':ref:`module-{module_name}`' 66 if module_name not in metadata: 67 return f' "{ref}", "", "", ""\n' 68 tagline = metadata[module_name]['tagline'] 69 status = build_status_badge(metadata[module_name]['status']) 70 if 'languages' in metadata[module_name]: 71 languages = ', '.join(metadata[module_name]['languages']) 72 else: 73 languages = '' 74 return f' "{ref}", "{status}", "{tagline}", "{languages}"\n' 75 76 77def generate_modules_index(_, docname: str, source: list[str]) -> None: 78 """Inserts the metadata table into //docs/modules.rst.""" 79 if docname != 'modules': # Only run this logic on //docs/modules.rst. 80 return 81 skip = ['docker'] # Items in //PIGWEED_MODULES that should be skipped. 82 # Transform //docs/module_metadata.json into a csv-table reST directive. 83 # https://docutils.sourceforge.io/docs/ref/rst/directives.html#csv-table-1 84 content = '\n\n.. csv-table::\n' 85 content += ' :header: "Name", "Status", "Description", "Languages"\n\n' 86 # Loop through the complete, authoritative list (as opposed to the metadata) 87 # to guarantee that every module is listed on the modules index page. 88 for module in complete_pigweed_modules_list: 89 if module in skip: 90 continue 91 content += build_row(module) 92 # Modify the reST of //docs/modules.rst in-place. The auto-generated table 93 # is just appended to the reST source text. 94 source[0] += content 95 96 97def setup(app: Sphinx) -> dict[str, bool]: 98 """Hooks this extension into Sphinx.""" 99 app.connect('source-read', generate_modules_index) 100 return { 101 'parallel_read_safe': True, 102 'parallel_write_safe': True, 103 } 104