Generate core runtime API shims

This commit is contained in:
2026-08-08 12:28:41 +02:00
parent 7086af9a6f
commit a007313785
12 changed files with 7223 additions and 536 deletions

View File

@@ -66,6 +66,97 @@ WIRE_ROOT_TYPES = {
"T:LibreMetaverse.Tree",
"T:LibreMetaverse.TreeDefinition",
}
CORE_NAMESPACES = (
"LibreMetaverse.Http",
"LibreMetaverse.Stats",
"LibreMetaverse.Threading",
)
CORE_ROOT_TYPES = {
"T:LibreMetaverse.CapabilitiesReceivedEventArgs",
"T:LibreMetaverse.Caps",
"T:LibreMetaverse.Caps.EventQueueCallback",
"T:LibreMetaverse.CapsCategory",
"T:LibreMetaverse.CapsEventDictionary",
"T:LibreMetaverse.CapsRateLimiter",
"T:LibreMetaverse.CapsRateLimiterOptions",
"T:LibreMetaverse.ConnectionSettings",
"T:LibreMetaverse.DisconnectedEventArgs",
"T:LibreMetaverse.DisposalHelper.DisposalGuard",
"T:LibreMetaverse.DisposalHelper",
"T:LibreMetaverse.EventQueueRunningEventArgs",
"T:LibreMetaverse.EventSubscription`1",
"T:LibreMetaverse.EventSubscriptionHelper",
"T:LibreMetaverse.FileHelper",
"T:LibreMetaverse.DictionaryChangeCallback",
"T:LibreMetaverse.DictionaryEventAction",
"T:LibreMetaverse.GenericStreamingMessageEventArgs",
"T:LibreMetaverse.GenericStreamingMethod",
"T:LibreMetaverse.GridClient",
"T:LibreMetaverse.GridClientBakingTextureProvider",
"T:LibreMetaverse.GridClientServiceCollectionExtensions",
"T:LibreMetaverse.Helpers",
"T:LibreMetaverse.HomeInfo",
"T:LibreMetaverse.HttpCapsClient",
"T:LibreMetaverse.IBakingTextureProvider",
"T:LibreMetaverse.IGridClient",
"T:LibreMetaverse.ImageCodec",
"T:LibreMetaverse.ImageDownload",
"T:LibreMetaverse.ImageReceiveProgressEventArgs",
"T:LibreMetaverse.ImageRequest",
"T:LibreMetaverse.ImageType",
"T:LibreMetaverse.IncomingPacketIDCollection",
"T:LibreMetaverse.Interfaces.IByteBufferPool",
"T:LibreMetaverse.Logger",
"T:LibreMetaverse.Logger.LogCallback",
"T:LibreMetaverse.LoggingSettings",
"T:LibreMetaverse.LoginCredential",
"T:LibreMetaverse.LoginException",
"T:LibreMetaverse.LoginParams",
"T:LibreMetaverse.LoginProgressEventArgs",
"T:LibreMetaverse.LoginResponseData",
"T:LibreMetaverse.LoginResponseData.ParseMessage",
"T:LibreMetaverse.LoginResponseData.ParseMessageLevel",
"T:LibreMetaverse.LoginResponseData.ParseResult",
"T:LibreMetaverse.LoginState",
"T:LibreMetaverse.LoginStatus",
"T:LibreMetaverse.LoggedOutEventArgs",
"T:LibreMetaverse.NetworkManager",
"T:LibreMetaverse.NetworkManager.DisconnectType",
"T:LibreMetaverse.NetworkManager.IncomingPacket",
"T:LibreMetaverse.NetworkManager.LoginResponseCallback",
"T:LibreMetaverse.NetworkManager.OutgoingPacket",
"T:LibreMetaverse.LastExecStatus",
"T:LibreMetaverse.ObservableDictionary`2",
"T:LibreMetaverse.PacketEventDictionary",
"T:LibreMetaverse.PacketReceivedEventArgs",
"T:LibreMetaverse.PacketSentEventArgs",
"T:LibreMetaverse.PacketSettings",
"T:LibreMetaverse.HttpCapsClient.ProgressReport",
"T:LibreMetaverse.ProtocolManager",
"T:LibreMetaverse.Repeat",
"T:LibreMetaverse.Settings",
"T:LibreMetaverse.SimChangedEventArgs",
"T:LibreMetaverse.SimConnectedEventArgs",
"T:LibreMetaverse.SimConnectingEventArgs",
"T:LibreMetaverse.SimDisconnectedEventArgs",
"T:LibreMetaverse.Simulator",
"T:LibreMetaverse.Simulator.SimStats",
"T:LibreMetaverse.SimulatorDataPool",
"T:LibreMetaverse.SimulatorFeatures",
"T:LibreMetaverse.SourceType",
"T:LibreMetaverse.StatusCode",
"T:LibreMetaverse.TargetType",
"T:LibreMetaverse.TexturePipeline",
"T:LibreMetaverse.TexturePipelineSettings",
"T:LibreMetaverse.TextureRequestState",
"T:LibreMetaverse.TimingSettings",
"T:LibreMetaverse.Transfer",
"T:LibreMetaverse.TransferError",
"T:LibreMetaverse.UDPBase",
"T:LibreMetaverse.UDPPacketBuffer",
"T:LibreMetaverse.XferDownload",
"T:LibreMetaverse.XferReceivedEventArgs",
}
UNDERLYING = {
"System.SByte": "i8", "System.Byte": "u8", "System.Int16": "i16", "System.UInt16": "u16",
@@ -98,6 +189,10 @@ 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]:
@@ -162,7 +257,7 @@ def render_enum(item: dict, rust_name: str) -> list[str]:
return lines
def render_body(signature: str, member_id: str, error_model: str, trait: bool) -> str:
def render_body(signature: str, member_id: str, error_model: str, asyncness: str, trait: bool) -> str:
if trait:
signature = signature.removeprefix("pub ")
failure = (
@@ -170,6 +265,8 @@ def render_body(signature: str, member_id: str, error_model: str, trait: bool) -
if error_model.startswith("Result")
else f"libremetaverse_types::unimplemented_api!({json.dumps(member_id)})"
)
if asyncness == "boxed_future":
failure = f"Box::pin(async {{ {failure} }})"
return f" {signature} {{ {failure} }}"
@@ -212,6 +309,12 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
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},"]
for field_name, field_type in private_fields:
lines.append(f" {field_name}: {field_type},")
@@ -233,7 +336,15 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
for index, signature in enumerate(signatures):
if index:
lines.append(f" /// Setter for C# member: `{member['doc_id']}`.")
lines.append(render_body(signature, member["doc_id"], row["error_model"], trait))
lines.append(
render_body(
signature,
member["doc_id"],
row["error_model"],
row["asyncness"],
trait,
)
)
lines.append("}")
if not trait:
for interface in item.get("interfaces", []):
@@ -269,6 +380,10 @@ def wire_type(item: dict) -> bool:
)
def core_type(item: dict) -> bool:
return item["doc_id"] in CORE_ROOT_TYPES or item["namespace"].startswith(CORE_NAMESPACES)
def render_modules(items: list[tuple[dict, dict, bool]], member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str:
tree: dict = {"items": [], "children": {}}
for item, type_row, complete in items:
@@ -295,6 +410,10 @@ 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}
configured_main_ids = WIRE_ROOT_TYPES | CORE_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)}")
interfaces = {
*{mapping.type_parts(item["signature"])[0] for assembly in catalog["assemblies"] for item in assembly["types"] if item["kind"] == "interface"},
*{
@@ -320,7 +439,11 @@ def generate_sources(catalog: dict) -> tuple[dict[Path, str], dict[str, tuple[in
"#![allow(non_snake_case)]",
"",
]
selected = assembly["types"] if name in TARGETS else [item for item in assembly["types"] if wire_type(item)]
selected = (
assembly["types"]
if name in TARGETS
else [item for item in assembly["types"] if wire_type(item) or core_type(item)]
)
selected_ids = {item["doc_id"] for item in selected}
rendered_items = [
(item, type_by_id[item["doc_id"]], item["doc_id"] in selected_ids)
@@ -364,11 +487,23 @@ def coverage_report(catalog: dict, coverage: dict[str, tuple[int, int, bool]]) -
status = "pending milestone issue"
lines.append(f"| `{name}` | {types:,} | {members:,} | {status} |")
if "LibreMetaverse" in coverage and not coverage["LibreMetaverse"][2]:
main_types = next(
assembly["types"]
for assembly in catalog["assemblies"]
if assembly["identity"]["name"] == "LibreMetaverse"
)
wire_types = [item for item in main_types if wire_type(item)]
core_types = [item for item in main_types if core_type(item) and not wire_type(item)]
lines += [
"",
"The partial `LibreMetaverse` row is milestone issue #7's wire/data slice: packets, messages, assets,",
"GLTF, archives/import-export, materials, rendering DTOs, bit packing, permissions, primitives, and",
"texture/media/particle records. Other main-assembly types remain pending shells for issues #8-#10.",
"The partial `LibreMetaverse` row contains the completed issue #7 wire/data slice and issue #8 core",
"runtime/networking slice. Avatar-facing and world/social/service manager types remain pending shells",
"for issues #9 and #10.",
"",
"| Main assembly slice | Types | Members |",
"|---|---:|---:|",
f"| Issue #7 wire/data | {len(wire_types):,} | {sum(len(item['members']) for item in wire_types):,} |",
f"| Issue #8 core runtime/networking | {len(core_types):,} | {sum(len(item['members']) for item in core_types):,} |",
]
lines += ["", f"Current callable coverage: **{total_types:,} types / {total_members:,} members**.", ""]
return "\n".join(lines)