Files
Hippolyzer/hippolyzer/lib/base/namevalue.py
2021-04-30 17:30:24 +00:00

122 lines
3.6 KiB
Python

import dataclasses
from typing import *
import hippolyzer.lib.base.serialization as se
from hippolyzer.lib.base.datatypes import UUID, Vector3, StringEnum
from hippolyzer.lib.base.multidict import OrderedMultiDict
class NameValueType(StringEnum):
Null = 'NULL'
String = 'STRING'
F32 = 'F32'
S32 = 'S32'
Vector3 = 'VEC3'
U32 = 'U32'
CAMERA = 'CAMERA' # Obsolete
Asset = 'ASSET'
U64 = 'U64'
class NameValueClass(StringEnum):
Null = 'NULL'
ReadOnly = 'R'
ReadWrite = 'RW'
class NameValueSendTo(StringEnum):
Null = 'NULL'
Sim = 'S'
DataSim = 'DS'
SimViewer = 'SV'
DataSimViewer = 'DSV'
NV_FIELD_SPEC = se.CStr(terminators=(b" ", b"\t", b"\r", b"\n"))
NV_VALUE_SPEC = se.CStr(terminators=(b"\n",), write_terminator=False)
def _string_enum_field(enum_cls: Type) -> dataclasses.Field:
return se.dataclass_field(se.StringEnumAdapter(enum_cls, NV_FIELD_SPEC))
@dataclasses.dataclass
class NameValue:
name: str = se.dataclass_field(NV_FIELD_SPEC)
type: NameValueType = _string_enum_field(NameValueType)
rw: NameValueClass = _string_enum_field(NameValueClass)
sendto: NameValueSendTo = _string_enum_field(NameValueSendTo)
value: str = se.dataclass_field(NV_VALUE_SPEC)
def deserialize(self) -> Any:
# Since the floating-point data has a string representation
# we want to keep it in string format unless deserialization is specifically
# requested, so we don't lose precision and change the string representation
# when round-tripping
return {
NameValueType.Null: lambda x: x,
NameValueType.String: lambda x: x,
NameValueType.F32: lambda x: float(x),
NameValueType.S32: lambda x: int(x),
NameValueType.U32: lambda x: int(x),
NameValueType.U64: lambda x: int(x),
NameValueType.CAMERA: lambda x: x,
NameValueType.Vector3: lambda x: Vector3.parse(x),
NameValueType.Asset: lambda x: UUID(x),
}[self.type](self.value)
def serialize(self, val):
self.value = str(val)
def __str__(self):
return f"{self.name} {self.type} {self.rw} {self.sendto} {self.value}"
class NameValueCollection(List[NameValue]):
def to_dict(self) -> Dict[str, Any]:
return OrderedMultiDict(
(v.name, v.deserialize()) for v in self
)
def __str__(self):
return "\n".join(str(x) for x in self)
class NameValueSerializer(se.Dataclass):
def __init__(self):
super().__init__(NameValue)
def serialize(self, val, writer: se.BufferWriter, ctx=None):
if isinstance(val, str):
# POD form allows a bare string representation of a namevalue
writer.write_bytes(val.encode("utf8"))
else:
super().serialize(val, writer, ctx)
def deserialize(self, reader: se.Reader, ctx=None):
val = super().deserialize(reader, ctx)
if reader.pod:
return str(NameValue(**val))
return val
NV_SERIALIZER = NameValueSerializer()
class NameValuesSerializer(se.SerializableBase):
@classmethod
def serialize(cls, vals, writer: se.BufferWriter, ctx=None):
first_elem = True
for val in vals:
if not first_elem:
writer.write_bytes(b"\n")
writer.write(NV_SERIALIZER, val, ctx=ctx)
first_elem = False
@classmethod
def deserialize(cls, reader: se.Reader, ctx=None):
entries = NameValueCollection()
while reader:
entries.append(reader.read(NV_SERIALIZER, ctx=ctx))
return entries