Path:
strictdoc/export/rst/writer.py
Lines:
145
Non-empty lines:
128
Non-empty lines covered with requirements:
128 / 128 (100.0%)
Functions:
8
Functions covered by requirements:
8 / 8 (100.0%)
1
from enum import Enum
2
from typing import Optional
3
4
from strictdoc.backend.sdoc.models.anchor import Anchor
5
from strictdoc.backend.sdoc.models.document import SDocDocument
6
from strictdoc.backend.sdoc.models.inline_link import InlineLink
7
from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField
8
from strictdoc.core.document_iterator import SDocDocumentIterator
9
from strictdoc.core.traceability_index import TraceabilityIndex
10
from strictdoc.export.rst.rst_templates import RSTTemplates
11
from strictdoc.helpers.rst import escape_str_after_inline_markup
12
13
14
class TAG(Enum):
15
SECTION = 1
16
REQUIREMENT = 2
17
COMPOSITE_REQUIREMENT = 3
18
19
20
class RSTWriter:
21
def __init__(self, index: TraceabilityIndex):
22
self.index = index
23
24
def write(self, document: SDocDocument, single_document: bool) -> str:
25
document_iterator = SDocDocumentIterator(document)
26
output = ""
27
28
if not single_document:
29
document_uid: Optional[str] = None
30
if document.config.uid is not None:
31
document_uid = document.config.uid
32
output += self._print_rst_header(
33
document.title, 0, reference_uid=document_uid
34
)35
36
for content_node, node_context_ in document_iterator.all_content():
37
if (
38
isinstance(content_node, SDocNode)
39
and content_node.node_type == "SECTION"
40
):41
assert node_context_.get_level() is not None
42
assert content_node.reserved_title is not None
43
output += self._print_rst_header(
44
content_node.reserved_title,
45
node_context_.get_level(),
46
content_node.reserved_uid,
47
)48
49
elif isinstance(content_node, SDocNode):
50
output += self._print_requirement_fields(content_node)
51
52
if output.endswith("\n\n"):
53
output = output[:-1]
54
return output.lstrip()
55
56
@staticmethod57
def _print_rst_header_2(string: str, level: int) -> str:
58
assert isinstance(string, str), string
59
assert isinstance(level, int), level
60
chars = {
61
0: "$",
62
1: "=",
63
2: "-",
64
3: "~",
65
4: "^",
66
5: '"',
67
6: "#",
68
7: "'",
69
}70
header_char = chars[level]
71
output = ""
72
output += string
73
output += "\n"
74
output += header_char.rjust(len(string), header_char)
75
return output
76
77
@staticmethod78
def _print_rst_header(
79
string: str, level: int, reference_uid: Optional[str]
80
) -> str:
81
assert isinstance(string, str), string
82
assert isinstance(level, int), level
83
chars = {
84
0: "$",
85
1: "=",
86
2: "-",
87
3: "~",
88
4: "^",
89
5: '"',
90
6: "#",
91
7: "'",
92
}93
header_char = chars[level]
94
output = ""
95
96
# An RST reference looks like this:97
# .. _SDOC-HIGH-VALIDATION:98
if reference_uid is not None:
99
assert len(reference_uid) > 0, reference_uid
100
output += f".. _{reference_uid}:\n\n"
101
102
output += string
103
output += "\n"
104
output += header_char.rjust(len(string), header_char)
105
output += "\n\n"
106
return output
107
108
def _print_requirement_fields(self, section_content: SDocNode) -> str:
109
requirement_template = RSTTemplates.jinja_environment.get_template(
110
"requirement.jinja.rst"111
)112
output = requirement_template.render(
113
requirement=section_content,
114
index=self.index,
115
_print_rst_header=self._print_rst_header_2,
116
_print_node_field=self._print_node_field,
117
)118
return output
119
120
def _print_node_field(self, object_with_parts: SDocNodeField) -> str:
121
output = ""
122
prev_part = None
123
for part in object_with_parts.parts:
124
if isinstance(part, str):
125
if isinstance(prev_part, InlineLink):
126
output += escape_str_after_inline_markup(part)
127
else:
128
output += part
129
elif isinstance(part, InlineLink):
130
node_or_none = self.index.get_linkable_node_by_uid(part.link)
131
# Labels that aren't placed before a section title can still be132
# referenced, but you must give the link an explicit title,133
# using this syntax: :ref:`Link title <label-name>`.134
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html135
node_display_title = node_or_none.get_display_title(
136
include_toc_number=False
137
)138
output += f":ref:`{node_display_title} <{part.link}>`"
139
elif isinstance(part, Anchor):
140
output += f".. _{part.value}:\n"
141
else:
142
raise NotImplementedError
143
prev_part = part
144
145
return output