Rust/Codegen: allow to "detach" property emission

By using the `rust.detach` pragma on a property, we make that property
not appear in the generated struct as a field, and provide instead
a `generated::Class::emit_property` function that can be used to emit
the corresponding TRAP entry independently.
This commit is contained in:
Paolo Tranquilli
2024-09-20 16:00:48 +02:00
parent d7aa5f1022
commit c74b6be136
4 changed files with 41 additions and 5 deletions

View File

@@ -72,13 +72,28 @@ class Processor:
def _get_class(self, name: str) -> rust.Class: def _get_class(self, name: str) -> rust.Class:
cls = self._classmap[name] cls = self._classmap[name]
properties = [
(c, p)
for c, p in _get_properties(cls, self._classmap)
if "rust_skip" not in p.pragmas and not p.synth
]
fields = []
detached_fields = []
for c, p in properties:
if "rust_detach" in p.pragmas:
# only generate detached fields in the actual class defining them, not the derived ones
if c is cls:
# TODO lift this restriction if required (requires change in dbschemegen as well)
assert c.derived or not p.is_single, \
f"property {p.name} in concrete class marked as detached but not optional"
detached_fields.append(_get_field(c, p))
elif not cls.derived:
# for non-detached ones, only generate fields in the concrete classes
fields.append(_get_field(c, p))
return rust.Class( return rust.Class(
name=name, name=name,
fields=[ fields=fields,
_get_field(c, p) detached_fields=detached_fields,
for c, p in _get_properties(cls, self._classmap)
if "rust_skip" not in p.pragmas and not p.synth
] if not cls.derived else [],
ancestors=sorted(set(a.name for a in _get_ancestors(cls, self._classmap))), ancestors=sorted(set(a.name for a in _get_ancestors(cls, self._classmap))),
entry_table=inflection.tableize(cls.name) if not cls.derived else None, entry_table=inflection.tableize(cls.name) if not cls.derived else None,
) )

View File

@@ -1,6 +1,7 @@
import dataclasses import dataclasses
import re import re
import typing import typing
import inflection
# taken from https://doc.rust-lang.org/reference/keywords.html # taken from https://doc.rust-lang.org/reference/keywords.html
keywords = { keywords = {
@@ -106,12 +107,17 @@ class Field:
def is_label(self): def is_label(self):
return self.base_type == "trap::Label" return self.base_type == "trap::Label"
@property
def singular_field_name(self) -> str:
return inflection.singularize(self.field_name.rstrip("_"))
@dataclasses.dataclass @dataclasses.dataclass
class Class: class Class:
name: str name: str
entry_table: str | None = None entry_table: str | None = None
fields: list[Field] = dataclasses.field(default_factory=list) fields: list[Field] = dataclasses.field(default_factory=list)
detached_fields: list[Field] = dataclasses.field(default_factory=list)
ancestors: list[str] = dataclasses.field(default_factory=list) ancestors: list[str] = dataclasses.field(default_factory=list)
@property @property
@@ -128,6 +134,10 @@ class Class:
ret.setdefault(f.table_name, []).append(f) ret.setdefault(f.table_name, []).append(f)
return [{"table_name": k, "fields": v} for k, v in ret.items()] return [{"table_name": k, "fields": v} for k, v in ret.items()]
@property
def has_detached_fields(self) -> bool:
return bool(self.detached_fields)
@dataclasses.dataclass @dataclasses.dataclass
class ClassList: class ClassList:

View File

@@ -239,6 +239,7 @@ ql.add(_Pragma("internal"))
cpp.add(_Pragma("skip")) cpp.add(_Pragma("skip"))
rust.add(_Pragma("detach"))
rust.add(_Pragma("skip_doc_test")) rust.add(_Pragma("skip_doc_test"))
rust.add(_ParametrizedClassPragma("doc_test_signature", lambda signature: signature)) rust.add(_ParametrizedClassPragma("doc_test_signature", lambda signature: signature))

View File

@@ -59,6 +59,16 @@ pub struct {{name}} {
_unused: () _unused: ()
} }
{{/is_entry}} {{/is_entry}}
{{#has_detached_fields}}
impl {{name}} {
{{#detached_fields}}
pub fn emit_{{singular_field_name}}(id: trap::Label<Self>, {{#is_repeated}}{{^is_unordered}}i: usize, {{/is_unordered}}{{/is_repeated}}value: {{base_type}}, out: &mut trap::Writer) {
out.add_tuple("{{table_name}}", vec![id.into(), {{#is_repeated}}{{^is_unordered}}i.into(), {{/is_unordered}}{{/is_repeated}}value.into()]);
}
{{/detached_fields}}
}
{{/has_detached_fields}}
impl trap::TrapClass for {{name}} { impl trap::TrapClass for {{name}} {
fn class_name() -> &'static str { "{{name}}" } fn class_name() -> &'static str { "{{name}}" }