Generate extension API shims
This commit is contained in:
@@ -21,11 +21,16 @@ COVERAGE = ROOT / "api/SHIM-COVERAGE.md"
|
||||
TARGETS = {
|
||||
"LibreMetaverse.Imaging.Abstractions": ROOT / "crates/libremetaverse-imaging/src/generated.rs",
|
||||
"LibreMetaverse.Imaging.Skia": ROOT / "crates/libremetaverse-imaging-skia/src/generated.rs",
|
||||
"LibreMetaverse.LslTools": ROOT / "crates/libremetaverse-lsl-tools/src/generated.rs",
|
||||
"LibreMetaverse.PrimMesher": ROOT / "crates/libremetaverse-prim-mesher/src/generated.rs",
|
||||
"LibreMetaverse.RLV": ROOT / "crates/libremetaverse-rlv/src/generated.rs",
|
||||
"LibreMetaverse.Rendering.MeshFoundry": ROOT / "crates/libremetaverse-rendering-mesh-foundry/src/generated.rs",
|
||||
"LibreMetaverse.Rendering.Simple": ROOT / "crates/libremetaverse-rendering-simple/src/generated.rs",
|
||||
"LibreMetaverse.Types": ROOT / "crates/libremetaverse-types/src/generated.rs",
|
||||
"LibreMetaverse.StructuredData": ROOT / "crates/libremetaverse-structured-data/src/generated.rs",
|
||||
"LibreMetaverse.Utilities": ROOT / "crates/libremetaverse-utilities/src/generated.rs",
|
||||
"LibreMetaverse.Voice.Vivox": ROOT / "crates/libremetaverse-voice-vivox/src/generated.rs",
|
||||
"LibreMetaverse.Voice.WebRTC": ROOT / "crates/libremetaverse-voice-webrtc/src/generated.rs",
|
||||
}
|
||||
|
||||
MAIN_TARGET = ROOT / "crates/libremetaverse/src/generated.rs"
|
||||
@@ -297,12 +302,6 @@ PRIVATE_LAYOUTS = {
|
||||
"T:LibreMetaverse.StructuredData.OSDUri": [("value", "libremetaverse_types::compat::Uri")],
|
||||
"T:LibreMetaverse.Imaging.ManagedImage": [("data", "Vec<u8>")],
|
||||
}
|
||||
BOXED_FIELDS = {
|
||||
"F:LibreMetaverse.Caps.Simulator",
|
||||
"F:LibreMetaverse.Simulator.Caps",
|
||||
}
|
||||
|
||||
|
||||
def generic_names(item: dict) -> list[str]:
|
||||
return [parameter["name"] for parameter in item.get("generic_parameters", [])]
|
||||
|
||||
@@ -418,16 +417,14 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
|
||||
lines.append(f"pub struct {rust_name};")
|
||||
else:
|
||||
lines.append(f"pub struct {rust_name}{suffix} {{")
|
||||
generics = set(names)
|
||||
for field in fields:
|
||||
field_type = mapper.type(field["type"], field.get("nullability"), generics)
|
||||
if field["doc_id"] in BOXED_FIELDS:
|
||||
field_type = (
|
||||
f"Option<Box<{field_type[7:-1]}>>"
|
||||
if field_type.startswith("Option<")
|
||||
else f"Box<{field_type}>"
|
||||
)
|
||||
lines += [f" /// C# member: `{field['doc_id']}`.", f" pub {mapping.snake(field['name'])}: {field_type},"]
|
||||
field_signature = member_rows[field["doc_id"]]["rust_signature"]
|
||||
field_name_match = re.fullmatch(r"pub ([^:]+): (.+)", field_signature)
|
||||
if not field_name_match:
|
||||
raise ValueError(f"invalid mapped field signature: {field_signature}")
|
||||
field_name = field_name_match.group(1)
|
||||
field_type = field_name_match.group(2)
|
||||
lines += [f" /// C# member: `{field['doc_id']}`.", f" pub {field_name}: {field_type},"]
|
||||
for field_name, field_type in private_fields:
|
||||
lines.append(f" {field_name}: {field_type},")
|
||||
if names:
|
||||
@@ -534,10 +531,25 @@ def render_modules(items: list[tuple[dict, dict, bool]], member_rows: dict[str,
|
||||
def generate_sources(catalog: dict) -> tuple[dict[Path, str], dict[str, tuple[int, int, bool]]]:
|
||||
type_rows, resolved = mapping.build_type_rows(catalog)
|
||||
type_by_id = {row["csharp_type_id"]: row for row in type_rows}
|
||||
member_by_id = {
|
||||
member["doc_id"]: member
|
||||
for assembly in catalog["assemblies"]
|
||||
for item in assembly["types"]
|
||||
for member in item["members"]
|
||||
}
|
||||
configured_main_ids = WIRE_ROOT_TYPES | CORE_ROOT_TYPES | AVATAR_ROOT_TYPES
|
||||
missing_configured_ids = configured_main_ids - type_by_id.keys()
|
||||
if missing_configured_ids:
|
||||
raise ValueError(f"unknown configured main-slice IDs: {sorted(missing_configured_ids)}")
|
||||
missing_boxed_fields = mapping.BOXED_FIELD_IDS - member_by_id.keys()
|
||||
if missing_boxed_fields:
|
||||
raise ValueError(f"unknown configured boxed fields: {sorted(missing_boxed_fields)}")
|
||||
if invalid_boxed_fields := [
|
||||
member_id
|
||||
for member_id in mapping.BOXED_FIELD_IDS
|
||||
if member_by_id[member_id]["kind"] != "field"
|
||||
]:
|
||||
raise ValueError(f"configured boxed members are not fields: {sorted(invalid_boxed_fields)}")
|
||||
interfaces = {
|
||||
*{mapping.type_parts(item["signature"])[0] for assembly in catalog["assemblies"] for item in assembly["types"] if item["kind"] == "interface"},
|
||||
*{
|
||||
|
||||
@@ -170,6 +170,21 @@ SUPPORT_TYPES = {
|
||||
MEMBER_RETURN_OVERRIDES = {
|
||||
"M:LibreMetaverse.Caps.Capabilities": "Vec<String>",
|
||||
}
|
||||
BOXED_FIELD_IDS = {
|
||||
"F:LibreMetaverse.Caps.Simulator",
|
||||
"F:LibreMetaverse.Simulator.Caps",
|
||||
"F:LibreMetaverse.LslTools.CommentList.tail",
|
||||
"F:LibreMetaverse.LslTools.CSymbol.m_parser",
|
||||
"F:LibreMetaverse.LslTools.CSymbol.m_prod",
|
||||
"F:LibreMetaverse.LslTools.CSymbol.m_refSymbol",
|
||||
"F:LibreMetaverse.LslTools.Dfa.Action.a_next",
|
||||
"F:LibreMetaverse.LslTools.LineList.tail",
|
||||
"F:LibreMetaverse.LslTools.ParseState.m_sgen",
|
||||
"F:LibreMetaverse.LslTools.Precedence.m_next",
|
||||
"F:LibreMetaverse.LslTools.Regex.m_sub",
|
||||
"F:LibreMetaverse.LslTools.SymbolSet.m_next",
|
||||
"F:LibreMetaverse.LslTools.SymbolSet.m_symbols",
|
||||
}
|
||||
RUST_KEYWORDS = {
|
||||
"as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn",
|
||||
"for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref",
|
||||
@@ -592,6 +607,12 @@ def member_signature(mapper: Mapper, owner: dict, item: dict, rust_name: str) ->
|
||||
error_model = "infallible_placeholder"
|
||||
if kind in {"field", "constant", "enum_value"}:
|
||||
value_type = mapper.type(item["type"], item.get("nullability"), generics)
|
||||
if item["doc_id"] in BOXED_FIELD_IDS:
|
||||
value_type = (
|
||||
f"Option<Box<{value_type[7:-1]}>>"
|
||||
if value_type.startswith("Option<")
|
||||
else f"Box<{value_type}>"
|
||||
)
|
||||
if kind == "enum_value":
|
||||
signature = f"{rust_name}: {value_type}"
|
||||
elif kind == "constant":
|
||||
@@ -757,7 +778,20 @@ def validate(catalog: dict, type_rows: list[dict[str, str]], member_rows: list[d
|
||||
raise ValueError("invalid type mapping status")
|
||||
if {row["status"] for row in member_rows} - VALID_MEMBER_STATUSES:
|
||||
raise ValueError("invalid member mapping status")
|
||||
catalog_ids = {member["doc_id"] for assembly in catalog["assemblies"] for item in assembly["types"] for member in item["members"]}
|
||||
catalog_members = {
|
||||
member["doc_id"]: member
|
||||
for assembly in catalog["assemblies"]
|
||||
for item in assembly["types"]
|
||||
for member in item["members"]
|
||||
}
|
||||
catalog_ids = catalog_members.keys()
|
||||
missing_boxed_fields = BOXED_FIELD_IDS - catalog_ids
|
||||
if missing_boxed_fields:
|
||||
raise ValueError(f"unknown configured boxed fields: {sorted(missing_boxed_fields)}")
|
||||
if invalid_boxed_fields := [
|
||||
member_id for member_id in BOXED_FIELD_IDS if catalog_members[member_id]["kind"] != "field"
|
||||
]:
|
||||
raise ValueError(f"configured boxed members are not fields: {sorted(invalid_boxed_fields)}")
|
||||
mapped_ids = {row["csharp_id"] for row in member_rows}
|
||||
if catalog_ids != mapped_ids:
|
||||
raise ValueError(f"catalog/mapping ID mismatch: missing={len(catalog_ids - mapped_ids)}, stale={len(mapped_ids - catalog_ids)}")
|
||||
|
||||
Reference in New Issue
Block a user