# Source code for rdflib.plugins.sparql.results.txtresults

    from __future__ import annotations

    from io import StringIO
    from typing import IO, List, Optional, Union

    from rdflib.namespace import NamespaceManager
    from rdflib.query import ResultSerializer
    from rdflib.term import BNode, Literal, URIRef, Variable

    def _termString(
        t: Optional[Union[URIRef, Literal, BNode]],
        namespace_manager: Optional[NamespaceManager],
    ) -> str:
        if t is None:
            return "-"
        if namespace_manager:
            if isinstance(t, URIRef):
                return namespace_manager.normalizeUri(t)
            elif isinstance(t, BNode):
                return t.n3()
            elif isinstance(t, Literal):
                return t._literal_n3(qname_callback=namespace_manager.normalizeUri)
        else:
            return t.n3()

    class TXTResultSerializer(ResultSerializer):
        """
        A write-only QueryResult serializer for text/ascii tables
        """

        def serialize(
            self,
            stream: IO,
            encoding: str = "utf-8",
            *,
            namespace_manager: Optional[NamespaceManager] = None,
            **kwargs,
        ) -> None:
            """
            return a text table of query results
            """

            def c(s, w):
                """
                center the string s in w wide string
                """
                w -= len(s)
                h1 = h2 = w // 2
                if w % 2:
                    h2 += 1
                return " " * h1 + s + " " * h2

            if self.result.type != "SELECT":
                raise Exception("Can only pretty print SELECT results!")
            string_stream = StringIO()
            if not self.result:
                string_stream.write("(no results)\n")
            else:
                keys: List[Variable] = self.result.vars  # type: ignore[assignment]
                maxlen = [0] * len(keys)
                b = [
                    # type error: Value of type "Union[Tuple[Node, Node, Node], bool, ResultRow]" is not indexable
                    # type error: Argument 1 to "_termString" has incompatible type "Union[Node, Any]"; expected "Union[URIRef, Literal, BNode, None]"  [arg-type]
                    # type error: No overload variant of "__getitem__" of "tuple" matches argument type "Variable"
                    # NOTE on type error: The problem here is that r can be more types than _termString expects because result can be a result of multiple types.
                    [_termString(r[k], namespace_manager) for k in keys]  # type: ignore[index, arg-type, call-overload]
                    for r in self.result
                ]
                for r in b:
                    for i in range(len(keys)):
                        maxlen[i] = max(maxlen[i], len(r[i]))
                string_stream.write(
                    "|".join([c(k, maxlen[i]) for i, k in enumerate(keys)]) + "\n"
                )
                string_stream.write("-" * (len(maxlen) + sum(maxlen)) + "\n")
                for r in sorted(b):
                    string_stream.write(
                        "|".join([t + " " * (i - len(t)) for i, t in zip(maxlen, r)]) + "\n"
                    )
            text_val = string_stream.getvalue()
            try:
                stream.write(text_val.encode(encoding))
            except (TypeError, ValueError):
                stream.write(text_val)