# SPDX-License-Identifier: BSD-2-Clause
""" This module provides functions for glossary of terms generation. """
# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import glob
import re
from typing import Any, Dict, NamedTuple
from rtemsspec.sphinxcontent import SphinxContent, SphinxInterfaceMapper
from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper
ItemMap = Dict[str, Item]
class _Glossary(NamedTuple):
""" A glossary of terms. """
uid_to_item: ItemMap
term_to_item: ItemMap
def _gather_glossary_terms(item: Item, glossary: _Glossary) -> None:
for child in item.children():
_gather_glossary_terms(child, glossary)
if item["type"] == "glossary" and item["glossary-type"] == "term":
glossary.uid_to_item[item.uid] = item
term = item["term"]
assert term not in glossary.term_to_item
glossary.term_to_item[term] = item
def _generate_glossary_content(terms: ItemMap, header: str,
target: str) -> None:
content = SphinxContent()
content.add_header(header, level=1)
content.add(".. glossary::")
with content.indent():
content.add(":sorted:")
for item in sorted(terms.values(), key=lambda x: x["term"].lower()):
content.register_license_and_copyrights_of_item(item)
text = SphinxInterfaceMapper(item).substitute(item["text"])
content.add_definition_item(item["term"], text)
content.add_licence_and_copyrights()
content.write(target)
_TERM = re.compile(r":term:`([^`]+)`")
_TERM_2 = re.compile(r"^[^<]+<([^>]+)>")
def _find_glossary_terms(path: str, document_terms: ItemMap,
glossary: _Glossary) -> None:
for src in glob.glob(path + "/**/*.rst", recursive=True):
if src.endswith("glossary.rst"):
continue
with open(src, "r") as out:
for term in _TERM.findall(out.read()):
match = _TERM_2.search(term)
if match:
term = match.group(1)
item = glossary.term_to_item[term]
document_terms[item.uid] = item
class _GlossaryMapper(ItemMapper):
def __init__(self, item: Item, document_terms: ItemMap):
super().__init__(item)
self._document_terms = document_terms
self.add_get_value("glossary/term:/term", self._add_to_terms)
self.add_get_value("glossary/term:/plural", self._add_to_terms)
def _add_to_terms(self, ctx: ItemGetValueContext) -> Any:
if ctx.item.uid not in self._document_terms:
self._document_terms[ctx.item.uid] = ctx.item
_GlossaryMapper(ctx.item,
self._document_terms).substitute(ctx.item["text"])
# The value of this substitute is unused.
return ""
def _resolve_glossary_terms(document_terms: ItemMap) -> None:
for term in list(document_terms.values()):
_GlossaryMapper(term, document_terms).substitute(term["text"])
def _generate_project_glossary(glossary: _Glossary, header: str,
target: str) -> None:
if target:
_generate_glossary_content(glossary.uid_to_item, header, target)
def _generate_document_glossary(config: dict, glossary: _Glossary) -> None:
document_terms = {} # type: ItemMap
for path in config["rest-source-paths"]:
_find_glossary_terms(path, document_terms, glossary)
_resolve_glossary_terms(document_terms)
_generate_glossary_content(document_terms, config["header"],
config["target"])
def generate(config: dict, item_cache: ItemCache) -> None:
"""
Generates glossaries of terms according to the configuration.
:param config: A dictionary with configuration entries.
:param item_cache: The specification item cache containing the glossary
groups and terms.
"""
groups = {} # type: ItemMap
for uid, item in item_cache.all.items():
if item.type == "glossary/group":
groups[uid] = item
project_glossary = _Glossary({}, {})
for group in config["project-groups"]:
_gather_glossary_terms(groups[group], project_glossary)
_generate_project_glossary(project_glossary, config["project-header"],
config["project-target"])
for document_config in config["documents"]:
_generate_document_glossary(document_config, project_glossary)