"""Documentation writers."""
import re
from winevtrc import versions
[docs]
class DocumentationFileWriter:
"""Documentation file writer."""
[docs]
def __init__(self, path):
"""Initializes a documentation file writer."""
super().__init__()
self._file_object = None
self._path = path
[docs]
def __enter__(self):
"""Make this work with the 'with' statement."""
self.Open()
return self
[docs]
def __exit__(self, exception_type, value, traceback):
"""Make this work with the 'with' statement."""
self.Close()
def _WriteHeader(self):
"""Writes a header."""
return
[docs]
def Close(self):
"""Closes the documentation file writer object."""
self._file_object.close()
self._file_object = None
[docs]
def Open(self):
"""Opens the documentation file writer object."""
# pylint: disable=consider-using-with
self._file_object = open(self._path, "w", encoding="utf-8")
self._WriteHeader()
[docs]
class EventLogProviderMarkdownWriter(DocumentationFileWriter):
"""Event Log provider Markdown file writer."""
_WINDOWS_VERSIONS_KEY_FUNCTION = versions.WindowsVersions.KeyFunction
[docs]
def WriteEventLogProvider(self, event_log_provider, windows_versions):
"""Writes an Event Log provider to a Markdown file.
Args:
event_log_provider (EventLogProvider): Event Log provider.
windows_versions (list[str]): strings that identify the Windows
versions.
"""
name = event_log_provider.name
if not name:
log_sources = sorted(event_log_provider.log_sources)
name = log_sources[0]
lines = []
if self._file_object.tell() == 0:
lines = [f"## {name:s}", ""]
if windows_versions:
versions_per_prefix = {}
for version in sorted(windows_versions):
for prefix in ("Windows 10", "Windows 11", None):
if prefix and version.startswith(prefix):
break
if not prefix:
versions_per_prefix[version] = []
else:
if prefix not in versions_per_prefix:
versions_per_prefix[prefix] = []
versions_per_prefix[prefix].append(version[len(prefix) + 2 : -1])
lines.append("Seen on:")
for prefix, sub_versions in sorted(
versions_per_prefix.items(),
key=lambda item: self._WINDOWS_VERSIONS_KEY_FUNCTION(item[0]),
):
if not sub_versions:
line = f"* {prefix:s}"
else:
sub_versions_string = ", ".join(sub_versions)
line = f"* {prefix:s} ({sub_versions_string:s})"
lines.append(line)
lines.append("")
lines.extend(['<table border="1" class="docutils">', " <tbody>"])
if event_log_provider.name:
lines.extend(
[
" <tr>",
" <td><b>Name:</b></td>",
f" <td>{event_log_provider.name:s}</td>",
" </tr>",
]
)
if event_log_provider.identifier:
lines.extend(
[
" <tr>",
" <td><b>Identifier:</b></td>",
f" <td>{event_log_provider.identifier:s}</td>",
" </tr>",
]
)
if event_log_provider.additional_identifier:
lines.extend(
[
" <tr>",
" <td><b>Additional identifier:</b></td>",
f" <td>{event_log_provider.additional_identifier:s}</td>",
" </tr>",
]
)
for index, log_type in enumerate(event_log_provider.log_types):
if index == 0:
lines.extend(
[
" <tr>",
" <td><b>Log type(s):</b></td>",
f" <td>{log_type:s}</td>",
" </tr>",
]
)
else:
lines.extend(
[
" <tr>",
" <td> </td>",
f" <td>{log_type:s}</td>",
" </tr>",
]
)
for index, log_source in enumerate(event_log_provider.log_sources):
if index == 0:
lines.extend(
[
" <tr>",
" <td><b>Log source(s):</b></td>",
f" <td>{log_source:s}</td>",
" </tr>",
]
)
else:
lines.extend(
[
" <tr>",
" <td> </td>",
f" <td>{log_source:s}</td>",
" </tr>",
]
)
for index, path in enumerate(
sorted((event_log_provider.category_message_files))
):
if index == 0:
lines.extend(
[
" <tr>",
" <td><b>Category message file(s):</b></td>",
f" <td>{path:s}</td>",
" </tr>",
]
)
else:
lines.extend(
[
" <tr>",
" <td> </td>",
f" <td>{path:s}</td>",
" </tr>",
]
)
for index, path in enumerate(sorted((event_log_provider.event_message_files))):
if index == 0:
lines.extend(
[
" <tr>",
" <td><b>Event message file(s):</b></td>",
f" <td>{path:s}</td>",
" </tr>",
]
)
else:
lines.extend(
[
" <tr>",
" <td> </td>",
f" <td>{path:s}</td>",
" </tr>",
]
)
for index, path in enumerate(
sorted((event_log_provider.parameter_message_files))
):
if index == 0:
lines.extend(
[
" <tr>",
" <td><b>Parameter message file(s):</b></td>",
f" <td>{path:s}</td>",
" </tr>",
]
)
else:
lines.extend(
[
" <tr>",
" <td> </td>",
f" <td>{path:s}</td>",
" </tr>",
]
)
lines.extend([" </tbody>", "</table>", "", " ", "", ""])
text = "\n".join(lines)
self._file_object.write(text)
[docs]
class EventLogProvidersIndexRstWriter(DocumentationFileWriter):
"""Event Log providers index.rst file writer."""
_HEADER_TEXT = "\n".join(
[
"###################",
"Event Log providers",
"###################",
"",
".. toctree::",
" :maxdepth: 1",
"",
"",
]
)
def _WriteHeader(self):
"""Writes a header."""
self._file_object.write(self._HEADER_TEXT)
[docs]
def WriteEventLogProvider(self, name):
"""Writes an Event Log provider to the index.rst file.
Args:
name (str): name of the Event Log provider.
"""
output_filename = f"Provider-{name:s}".replace(" ", "-").replace("/", "-")
self._file_object.write(f" {name:s} <{output_filename:s}>\n")
[docs]
class MessageFileMarkdownWriter(DocumentationFileWriter):
"""Message file Markdown file writer."""
def _WriteMessageTable(self, message_table):
"""Writes a message table.
Args:
message_table (MessageTable): message table.
"""
file_versions = ", ".join(message_table.file_versions)
lines = [
"",
f"### {file_versions:s}",
"",
"Message identifier | Message string",
"--- | ---",
]
for identifier, string in message_table.message_strings.items():
string = re.sub(r"\n", "\\\\n", string)
string = re.sub(r"\r", "\\\\r", string)
string = re.sub(r"\t", "\\\\t", string)
lines.append(f"0x{identifier:08x} | {string:s}")
lines.append("")
text = "\n".join(lines)
self._file_object.write(text)
[docs]
def WriteMessageFile(self, message_file):
"""Writes a message file.
Args:
message_file (ExportMessageFile): message file.
"""
lines = [
f"## {message_file.name:s}",
"",
f"Path: {message_file.windows_path:s}",
"",
]
text = "\n".join(lines)
self._file_object.write(text)
for message_table in message_file.GetMessageTables():
self._WriteMessageTable(message_table)
[docs]
class MessageFilesIndexRstWriter(DocumentationFileWriter):
"""Message files index.rst file writer."""
_HEADER_TEXT = "\n".join(
[
"#############",
"Message files",
"#############",
"",
".. toctree::",
" :maxdepth: 1",
"",
"",
]
)
def _WriteHeader(self):
"""Writes a header."""
self._file_object.write(self._HEADER_TEXT)
[docs]
def WriteMessageFile(self, name):
"""Writes a message file to the index.rst file.
Args:
name (str): name of the message file.
"""
output_filename = f"MessageFile-{name:s}".replace(" ", "-").replace("/", "-")
self._file_object.write(f" {name:s} <{output_filename:s}>\n")