"""ResultForest class."""
from typing import TYPE_CHECKING, Dict, Optional, Sequence, Tuple, Union
from text_lint.config import LOOKUP_STATIC_VALUE_MARKER
from text_lint.exceptions.results import ResultDoesNotExist
from text_lint.sequencers.lookups import LookupsSequencer
from .cursor import ResultTreeCursor
if TYPE_CHECKING: # pragma: no cover
from text_lint.linter import Linter
from text_lint.operations.validators.args.lookup_expression import (
LookupExpression,
)
from .tree import AliasTreeValue, ResultTree
AliasLookupResult = Union[
str,
Sequence["AliasLookupResult"],
Dict[str, "AliasLookupResult"],
]
AliasTreesKey = Union[str, Tuple[str, ...]]
[Doku]
class ResultForest:
"""A composite of TreeResult instances."""
[Doku]
def __init__(self) -> None:
"""Initialize ResultForest instances."""
self._trees: Dict["AliasTreesKey", "ResultTree"] = {}
self.cursor = ResultTreeCursor()
self.lookup_results: AliasLookupResult = []
[Doku]
def __len__(self) -> int:
"""Implement the len operator for ResultForest instances.
:returns: The number of trees in this forest.
"""
return len(self._trees)
[Doku]
def add(self, tree: Optional["ResultTree"]) -> None:
"""Add a ResultTree instance to the ResultForest.
:param tree: The instance to add.
"""
if tree is not None:
value = self._hash_tree_value(tree.value)
if value in self._trees:
self._trees[value].children += tree.children
else:
self._trees[value] = tree
[Doku]
def get(self, key: "AliasTreeValue") -> "ResultTree":
"""Retrieve a ResultTree instance from the ResultForest.
:param key: The value of the ResultTree required.
:returns: The ResultTree matching the specified key.
"""
return self._trees[self._hash_tree_value(key)]
def _hash_tree_value(self, value: "AliasTreeValue") -> "AliasTreesKey":
if isinstance(value, list):
return tuple(value)
return value
[Doku]
def lookup_expression(
self,
linter: "Linter",
lookup_expression: "LookupExpression",
) -> "AliasLookupResult":
"""Perform a lookup against the ResultForest.
:param linter: The linter instance calling this lookup expression.
:param lookup_expression: The lookup expression to execute.
:returns: The evaluated lookup result.
"""
if lookup_expression.source.startswith(LOOKUP_STATIC_VALUE_MARKER):
return lookup_expression.source[1:]
try:
self.get(lookup_expression.source)
except KeyError as exc:
raise ResultDoesNotExist(
lookup_expression=lookup_expression,
requesting_operation_name=linter.validators.last.name,
) from exc
self.cursor.location = [self.get(lookup_expression.source)]
self.cursor = self.cursor.clone()
self.lookup_results = [[lookup_expression.source]]
lookups = LookupsSequencer(lookup_expression, linter.validators.last.name)
for operation in lookups:
operation.apply(linter.states.lookup())
return self.lookup_results