blob: 44f09045207ee92593e0f91d8f83eb9092d4ba2b [file]
# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Generates the modules index table on //docs/sphinx/modules.rst."""
import json
import os
import sys
from sphinx.application import Sphinx
try: # Bazel location for the data
from python.runfiles import runfiles # type: ignore
script_dir = os.path.dirname(os.path.abspath(__file__))
modules_file = f"{script_dir}/../PIGWEED_MODULES"
metadata_file = f"{script_dir}/../module_metadata.json"
except ImportError: # GN location for the data
modules_file = f'{os.environ["PW_ROOT"]}/PIGWEED_MODULES'
metadata_file = f'{os.environ["PW_ROOT"]}/docs/module_metadata.json'
with open(modules_file, 'r') as f:
# The complete, authoritative list of modules.
complete_pigweed_modules_list = f.read().splitlines()
with open(metadata_file, 'r') as f:
# Module metadata such as supported languages and status.
metadata = json.load(f)
def build_status_badge(status):
"""Styles the module status as a clickable badge."""
# This default value should never get used but it's a valid
# neutral styling in case something goes wrong and it leaks through.
badge_type = 'info'
if status == 'stable':
badge_type = 'primary'
elif status == 'unstable':
badge_type = 'secondary'
elif status == 'experimental':
badge_type = 'warning'
elif status == 'deprecated':
badge_type = 'danger'
else:
msg = f'[modules_index.py] error: invalid module status ("{status}")'
sys.exit(msg)
# Use bdg-ref-* to make the badge link to the glossary definition for
# "stable", "experimental", etc.
# https://sphinx-design.readthedocs.io/en/latest/badges_buttons.html
return f':bdg-{badge_type}:`{status}`'
def build_row(module_name: str):
"""Builds a row of data for the table. Each module gets a row."""
ref = f':ref:`module-{module_name}`'
if module_name not in metadata:
return f' "{ref}", "", "", ""\n'
tagline = metadata[module_name].get('tagline', '')
status = build_status_badge(metadata[module_name]['status'])
if 'languages' in metadata[module_name]:
languages = ', '.join(metadata[module_name]['languages'])
else:
languages = ''
return f' "{ref}", "{status}", "{tagline}", "{languages}"\n'
def generate_modules_index(_, docname: str, source: list[str]) -> None:
"""Inserts the metadata table into //docs/sphinx/modules.rst."""
if (
docname != 'modules'
): # Only run this logic on //docs/sphinx/modules.rst.
return
skip = ['docker'] # Items in //PIGWEED_MODULES that should be skipped.
# Transform //docs/sphinx/module_metadata.json into a csv-table reST directive.
# https://docutils.sourceforge.io/docs/ref/rst/directives.html#csv-table-1
content = '\n\n.. csv-table::\n'
content += ' :header: "Name", "Status", "Description", "Languages"\n\n'
# Loop through the complete, authoritative list (as opposed to the metadata)
# to guarantee that every module is listed on the modules index page.
for module in complete_pigweed_modules_list:
if module in skip:
continue
content += build_row(module)
# Modify the reST of //docs/sphinx/modules.rst in-place. The auto-generated table
# is just appended to the reST source text.
source[0] += content
def setup(app: Sphinx) -> dict[str, bool]:
"""Hooks this extension into Sphinx."""
app.connect('source-read', generate_modules_index)
return {
'parallel_read_safe': True,
'parallel_write_safe': True,
}