Source code for nix_daemon_protocol.wire_string
"""Abstract base: a single length-prefixed string on the wire."""
from __future__ import annotations
from typing import TYPE_CHECKING
from pydantic import ConfigDict, model_serializer, model_validator
from .logging import deserialization_scope
from .wire_message import WireModel, _find_reader
if TYPE_CHECKING:
from .context import ReadContext, WriteContext
[docs]
class WireString(WireModel):
"""Abstract base: a single length-prefixed string on the wire.
Subclasses define their own fields. JSON is native Pydantic
(the destructed object). Wire format is ``str(self)``.
``to_writer`` writes ``str(self)`` as a single wire string.
``__hash__`` / ``__eq__`` delegate to ``str(self)``.
Override ``from_str`` / ``to_str`` in subclasses that need custom
wire format (e.g. ``WireSignature`` splits on ``:``).
"""
model_config = ConfigDict(frozen=True)
[docs]
async def to_writer(self, ctx: WriteContext) -> None:
ctx.writer.write_string(str(self))
def __str__(self) -> str:
return self.to_str()
def __hash__(self) -> int:
field_names = tuple(type(self).model_fields)
if type(self).to_str is WireString.to_str and len(field_names) == 1:
return hash(getattr(self, field_names[0]))
return hash(str(self))
def __eq__(self, other: object) -> bool:
if isinstance(other, WireString):
return str(self) == str(other)
if isinstance(other, str):
return str(self) == other
return NotImplemented
[docs]
@classmethod
async def from_reader(cls, ctx: ReadContext):
with deserialization_scope(ctx, cls):
reader = _find_reader(cls, version=ctx.version)
return await reader(ctx)
[docs]
@model_serializer
def to_str(self) -> str:
"""Override in subclasses to customize wire output. Default: single-field value."""
if len(type(self).model_fields) == 1:
return str(getattr(self, next(iter(type(self).model_fields))))
raise NotImplementedError(f"{type(self).__name__}: override to_str")
[docs]
@classmethod
def from_str(cls, data: str) -> object:
"""Default: map string to the single field."""
if len(cls.model_fields) == 1:
return {next(iter(cls.model_fields)): data}
raise NotImplementedError(f"{cls.__name__} has multiple fields, override from_str")
@model_validator(mode="before")
@classmethod
def transform(cls, data: object) -> object:
if isinstance(data, str):
return cls.from_str(data)
return data