#!/usr/bin/env python3 """Generate callable, failure-only Rust shims from the authoritative catalog.""" from __future__ import annotations import argparse import csv import json import re import subprocess from collections import Counter from pathlib import Path import generate_rust_mapping as mapping ROOT = Path(__file__).resolve().parents[1] CATALOG = ROOT / "api/public-api.json" LEDGER = ROOT / "api/RUST-MAPPING.tsv" COVERAGE = ROOT / "api/SHIM-COVERAGE.md" COMPILE_FIXTURE = ROOT / "tests/api-compile/src/lib.rs" 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", } # Types whose failure-only generated shells have been replaced by native Rust # implementations. The generated module keeps catalog markers and re-exports # the hand-written type so coverage remains deterministic. NATIVE_TYPES = { "T:LibreMetaverse.ArchetypeParam": "crate::genepool_catalog::ArchetypeParam", "T:LibreMetaverse.AttentionData": "crate::attention_catalog::AttentionData", "T:LibreMetaverse.AttentionSet": "crate::attention_catalog::AttentionSet", "T:LibreMetaverse.BitPack": "crate::bit_pack::BitPack", "T:LibreMetaverse.DrivenParamInfo": "crate::visual_catalog::DrivenParamInfo", "T:LibreMetaverse.GrassDefinition": "crate::foliage_catalog::GrassDefinition", "T:LibreMetaverse.GrassDefinitions": "crate::foliage_catalog::GrassDefinitions", "T:LibreMetaverse.Genepool": "crate::genepool_catalog::Genepool", "T:LibreMetaverse.GenepoolArchetype": "crate::genepool_catalog::GenepoolArchetype", "T:LibreMetaverse.LindenAttentions": "crate::attention_catalog::LindenAttentions", "T:LibreMetaverse.Packets.PacketType": "crate::packet_catalog::PacketType", "T:LibreMetaverse.PrimMesher.Coord": "crate::prim_mesher::Coord", "T:LibreMetaverse.PrimMesher.Face": "crate::prim_mesher::Face", "T:LibreMetaverse.PrimMesher.Path": "crate::prim_mesher::Path", "T:LibreMetaverse.PrimMesher.PathNode": "crate::prim_mesher::PathNode", "T:LibreMetaverse.PrimMesher.PrimMesh": "crate::prim_mesher::PrimMesh", "T:LibreMetaverse.PrimMesher.Profile": "crate::prim_mesher::Profile", "T:LibreMetaverse.PrimMesher.Quat": "crate::prim_mesher::Quat", "T:LibreMetaverse.PrimMesher.UVCoord": "crate::prim_mesher::UVCoord", "T:LibreMetaverse.PrimMesher.ViewerFace": "crate::prim_mesher::ViewerFace", "T:LibreMetaverse.PrimMesher.ObjMesh": "crate::obj_mesh::ObjMesh", "T:LibreMetaverse.PrimMesher.SculptMap": "crate::sculpt::SculptMap", "T:LibreMetaverse.PrimMesher.SculptMesh": "crate::sculpt::SculptMesh", "T:LibreMetaverse.PrimMesher.VertexIndexer": "crate::vertex_indexer::VertexIndexer", "T:LibreMetaverse.PrimMesher.ViewerPolygon": "crate::vertex_indexer::ViewerPolygon", "T:LibreMetaverse.PrimMesher.ViewerVertex": "crate::vertex_indexer::ViewerVertex", "T:LibreMetaverse.Imaging.Skia.SkiaTextureCodec": "crate::skia_codec::SkiaTextureCodec", "T:LibreMetaverse.Imaging.ManagedImageCreator": "crate::j2k::ManagedImageCreator", "T:LibreMetaverse.Imaging.ManagedImageInterleavedExtensions": "crate::j2k::ManagedImageInterleavedExtensions", "T:LibreMetaverse.Imaging.Targa": "crate::targa::Targa", "T:LibreMetaverse.Imaging.ITextureCodec": "crate::managed_image::ITextureCodec", "T:LibreMetaverse.Imaging.ManagedImage": "crate::managed_image::ManagedImage", "T:LibreMetaverse.Imaging.ManagedImage.ImageChannels": "crate::managed_image::ManagedImageImageChannels", "T:LibreMetaverse.StructuredData.OSD": "crate::model::OSD", "T:LibreMetaverse.StructuredData.OSDArray": "crate::model::OSDArray", "T:LibreMetaverse.StructuredData.OSDBinary": "crate::model::OSDBinary", "T:LibreMetaverse.StructuredData.OSDBoolean": "crate::model::OSDBoolean", "T:LibreMetaverse.StructuredData.OSDDate": "crate::model::OSDDate", "T:LibreMetaverse.StructuredData.OSDException": "crate::model::OSDException", "T:LibreMetaverse.StructuredData.OSDFormat": "crate::model::OSDFormat", "T:LibreMetaverse.StructuredData.OSDInteger": "crate::model::OSDInteger", "T:LibreMetaverse.StructuredData.OSDLlsdXml": "crate::model::OSDLlsdXml", "T:LibreMetaverse.StructuredData.OSDMap": "crate::model::OSDMap", "T:LibreMetaverse.StructuredData.OSDReal": "crate::model::OSDReal", "T:LibreMetaverse.StructuredData.OSDString": "crate::model::OSDString", "T:LibreMetaverse.StructuredData.OSDType": "crate::model::OSDType", "T:LibreMetaverse.StructuredData.OSDUUID": "crate::model::OSDUUID", "T:LibreMetaverse.StructuredData.OSDUri": "crate::model::OSDUri", "T:LibreMetaverse.CacheDictionary`2": "crate::collections::CacheDictionary", "T:LibreMetaverse.CacheDictionary`2.CacheDictionaryEnumerator": "crate::collections::CacheDictionaryCacheDictionaryEnumerator", "T:LibreMetaverse.Color4": "crate::color4::Color4", "T:LibreMetaverse.CRC32": "crate::crc32::CRC32", "T:LibreMetaverse.DoubleDictionary`3": "crate::collections::DoubleDictionary", "T:LibreMetaverse.EmptyRemovalStrategy`1": "crate::collections::EmptyRemovalStrategy", "T:LibreMetaverse.EnumInfoAttribute": "crate::enum_compat::EnumInfoAttribute", "T:LibreMetaverse.EnumInfoExtensions": "crate::enum_compat::EnumInfoExtensions", "T:LibreMetaverse.ExpiringCache`2": "crate::collections::ExpiringCache", "T:LibreMetaverse.ICacheDictionaryRemovalStrategy`1": "crate::collections::ICacheDictionaryRemovalStrategy", "T:LibreMetaverse.LruRemovalStrategy`1": "crate::collections::LruRemovalStrategy", "T:LibreMetaverse.Matrix4": "crate::geometry::Matrix4", "T:LibreMetaverse.MruRemovalStrategy`1": "crate::collections::MruRemovalStrategy", "T:LibreMetaverse.MultiValueDictionary`2": "crate::collections::MultiValueDictionary", "T:LibreMetaverse.Quaternion": "crate::geometry::Quaternion", "T:LibreMetaverse.Ray": "crate::vectors::Ray", "T:LibreMetaverse.TokenBucket": "crate::collections::TokenBucket", "T:LibreMetaverse.UUID": "crate::uuid::UUID", "T:LibreMetaverse.Vector2": "crate::vectors::Vector2", "T:LibreMetaverse.Vector3": "crate::vectors::Vector3", "T:LibreMetaverse.Vector3d": "crate::vectors::Vector3d", "T:LibreMetaverse.Vector4": "crate::vectors::Vector4", "T:LibreMetaverse.Utils": "crate::utils::Utils", "T:LibreMetaverse.SkeletalBoneInfo": "crate::visual_catalog::SkeletalBoneInfo", "T:LibreMetaverse.TreeDefinition": "crate::foliage_catalog::TreeDefinition", "T:LibreMetaverse.TreeDefinitions": "crate::foliage_catalog::TreeDefinitions", "T:LibreMetaverse.AgentThrottle": "crate::udp_transport::AgentThrottle", "T:LibreMetaverse.IncomingPacketIDCollection": "crate::udp_transport::IncomingPacketIDCollection", "T:LibreMetaverse.UDPBase": "crate::udp_transport::UDPBase", "T:LibreMetaverse.UDPPacketBuffer": "crate::udp_transport::UDPPacketBuffer", "T:LibreMetaverse.VisualAlphaParam": "crate::visual_catalog::VisualAlphaParam", "T:LibreMetaverse.VisualColorParam": "crate::visual_catalog::VisualColorParam", "T:LibreMetaverse.VisualParam": "crate::visual_catalog::VisualParam", "T:LibreMetaverse.VisualParams": "crate::visual_catalog::VisualParams", "T:LibreMetaverse.VolumeMorphInfo": "crate::visual_catalog::VolumeMorphInfo", "T:LibreMetaverse.Rendering.CollisionVolume": "crate::skeleton::CollisionVolume", "T:LibreMetaverse.Rendering.Joint": "crate::skeleton::Joint", "T:LibreMetaverse.Rendering.JointBase": "crate::skeleton::JointBase", "T:LibreMetaverse.Rendering.LindenSkeleton": "crate::skeleton::LindenSkeleton", } # Native declarations whose cataloged method implementations are still emitted # below. This is used for static namespace types such as OSDParser: the type is # hand-written, while its fixed public methods remain generator-audited. NATIVE_DECLARATIONS = { "T:LibreMetaverse.GridClient": "crate::client_core::GridClient", "T:LibreMetaverse.Settings": "crate::client_core::Settings", "T:LibreMetaverse.StructuredData.OSDParser": "crate::model::OSDParser", } NATIVE_MEMBER_BODIES = { "M:LibreMetaverse.AgentSettings.#ctor": "Ok(crate::client_core::agent_settings_defaults())", "M:LibreMetaverse.AssetCacheSettings.#ctor": "Ok(crate::client_core::asset_cache_settings_defaults())", "M:LibreMetaverse.ConnectionSettings.#ctor": "Ok(crate::client_core::connection_settings_defaults())", "M:LibreMetaverse.LoggingSettings.#ctor": "Ok(crate::client_core::logging_settings_defaults())", "M:LibreMetaverse.PacketSettings.#ctor": "Ok(crate::client_core::packet_settings_defaults())", "M:LibreMetaverse.ParcelSettings.#ctor": "Ok(crate::client_core::parcel_settings_defaults())", "M:LibreMetaverse.TexturePipelineSettings.#ctor": "Ok(crate::client_core::texture_pipeline_settings_defaults())", "M:LibreMetaverse.TimingSettings.#ctor": "Ok(crate::client_core::timing_settings_defaults())", "M:LibreMetaverse.WorldSettings.#ctor": "Ok(crate::client_core::world_settings_defaults())", "M:LibreMetaverse.GridClient.#ctor": "crate::client_core::GridClientBuilder::default().build().map_err(Into::into)", "M:LibreMetaverse.GridClient.Dispose": "self.shutdown().map_err(Into::into)", "M:LibreMetaverse.GridClient.DisposeAsync": "self.shutdown().map_err(Into::into)", "M:LibreMetaverse.GridClient.ToString": "String::new()", "P:LibreMetaverse.GridClient.Settings": "&mut self.settings", "P:LibreMetaverse.GridClient.TimeProvider": "self.time_provider.clone()", "P:LibreMetaverse.GridClient.TimeProvider#set": "self.time_provider = value", "F:LibreMetaverse.Settings.BindAddress": "std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED)", "F:LibreMetaverse.Settings.LogLevel": "crate::logging::LogLevel(1)", "F:LibreMetaverse.Settings.MaxHttpConnections": "32", "F:LibreMetaverse.Settings.PacketArchiveSize": "1000", "F:LibreMetaverse.Settings.ResourceDir": '"linden".to_owned()', "F:LibreMetaverse.Settings.SimulatorPoolTimeout": "120_000", "F:LibreMetaverse.Settings.TexturePipelineRefreshInterval": "500.0", "F:LibreMetaverse.Settings.UdpReceiveQueueCapacity": "512", "F:LibreMetaverse.Settings.UserAgent": '"LibreMetaverse".to_owned()', "M:LibreMetaverse.Settings.#ctor(LibreMetaverse.GridClient)": "Ok(crate::client_core::Settings::defaults())", "P:LibreMetaverse.Settings.Agent": "self.agent.clone()", "P:LibreMetaverse.Settings.AssetCache": "self.asset_cache.clone()", "P:LibreMetaverse.Settings.Connection": "self.connection.clone()", "P:LibreMetaverse.Settings.Logging": "self.logging.clone()", "P:LibreMetaverse.Settings.Packets": "self.packets.clone()", "P:LibreMetaverse.Settings.Parcel": "self.parcel.clone()", "P:LibreMetaverse.Settings.TexturePipeline": "self.texture_pipeline.clone()", "P:LibreMetaverse.Settings.Timing": "&mut self.timing", "P:LibreMetaverse.Settings.UploadCost": "self.upload_cost", "P:LibreMetaverse.Settings.UploadCost#set": "self.upload_cost = value", "P:LibreMetaverse.Settings.World": "self.world.clone()", "M:LibreMetaverse.Avatar.#ctor": "crate::visual_catalog::new_avatar()", "M:LibreMetaverse.Avatar.DecodeVisualParams": "Ok(crate::visual_catalog::decode_visual_params(&self.visual_parameters))", "M:LibreMetaverse.AvatarAppearanceEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Boolean,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Primitive.TextureEntryFace[],System.Collections.Generic.List{System.Byte},System.Byte,System.Int32,LibreMetaverse.AppearanceFlags,System.Int32)": "Ok(Self { stored_visual_params: visual_params })", "M:LibreMetaverse.AvatarAppearanceEventArgs.DecodeVisualParams": "Ok(crate::visual_catalog::decode_visual_params(&self.stored_visual_params))", "P:LibreMetaverse.AvatarAppearanceEventArgs.VisualParams": "self.stored_visual_params.clone()", "M:LibreMetaverse.Primitive.GetGrassDefinition": "crate::foliage_catalog::grass_by_id(self.prim_data.state)", "M:LibreMetaverse.Primitive.GetTreeDefinition": "crate::foliage_catalog::TreeDefinitions::get(self.tree_species)", "M:LibreMetaverse.Primitive.TextureEntryFace.#ctor(LibreMetaverse.Primitive.TextureEntryFace)": "crate::visual_catalog::new_texture_entry_face()", "M:LibreMetaverse.StructuredData.OSDParser.BufferCharactersEqual(System.IO.StringReader,System.Char[],System.Int32)": "crate::notation::buffer_characters_equal(reader, buffer, offset)", "M:LibreMetaverse.StructuredData.OSDParser.ConsumeBytes(System.IO.Stream,System.Int32)": "crate::binary::consume_bytes(stream, consume_bytes)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDBinary(System.Byte[])": "crate::binary::deserialize_bytes(binary_data)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDBinary(System.IO.Stream)": "crate::binary::deserialize_stream(stream)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDNotation(System.IO.StringReader)": "crate::notation::deserialize_reader(reader)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDNotation(System.String)": "crate::notation::deserialize_string(notation_data)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDProtobuf(System.Byte[])": "crate::protobuf::deserialize_bytes(data)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDProtobuf(System.IO.Stream)": "crate::protobuf::deserialize_stream(stream)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeJson(System.IO.Stream)": "crate::json_codec::deserialize_stream(json)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeJson(System.String)": "crate::json_codec::deserialize_string(json)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDXml(System.Byte[])": "crate::xml_codec::deserialize_bytes(xml_data)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDXml(System.IO.Stream)": "crate::xml_codec::deserialize_stream(xml_stream)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDXml(System.String)": "crate::xml_codec::deserialize_string(xml_data)", "M:LibreMetaverse.StructuredData.OSDParser.DeserializeLLSDXml(System.Xml.XmlReader)": "crate::xml_codec::deserialize_reader(xml_data)", "M:LibreMetaverse.StructuredData.OSDParser.EscapeCharacter(System.String,System.Char)": "crate::notation::escape_character(s, c)", "M:LibreMetaverse.StructuredData.OSDParser.FindByte(System.IO.Stream,System.Byte)": "crate::binary::find_byte(stream, to_find)", "M:LibreMetaverse.StructuredData.OSDParser.FindString(System.IO.Stream,System.String)": "crate::binary::find_string(stream, to_find)", "M:LibreMetaverse.StructuredData.OSDParser.GetLengthInBrackets(System.IO.StringReader)": "crate::notation::get_length_in_brackets(reader)", "M:LibreMetaverse.StructuredData.OSDParser.GetStringDelimitedBy(System.IO.StringReader,System.Char)": "crate::notation::get_string_delimited_by(reader, delimiter)", "M:LibreMetaverse.StructuredData.OSDParser.HostToNetworkIntBytes(System.Int32)": "crate::binary::host_to_network_int_bytes(int_host_end)", "M:LibreMetaverse.StructuredData.OSDParser.NetworkToHostDouble(System.Byte[])": "crate::binary::network_to_host_double(binary_net_end)", "M:LibreMetaverse.StructuredData.OSDParser.NetworkToHostInt(System.Byte[])": "crate::binary::network_to_host_int(binary_net_end)", "M:LibreMetaverse.StructuredData.OSDParser.PeekAndSkipWhitespace(System.IO.StringReader)": "crate::notation::peek_and_skip_whitespace(reader)", "M:LibreMetaverse.StructuredData.OSDParser.ReadAndSkipWhitespace(System.IO.StringReader)": "crate::notation::read_and_skip_whitespace(reader)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDBinary(LibreMetaverse.StructuredData.OSD)": "crate::binary::serialize(osd)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDBinary(LibreMetaverse.StructuredData.OSD,System.Boolean)": "crate::binary::serialize_with_header(osd, prepend_header)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDBinaryStream(LibreMetaverse.StructuredData.OSD)": "crate::binary::serialize_stream(data)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDBinaryStream(LibreMetaverse.StructuredData.OSD,System.Boolean)": "crate::binary::serialize_stream_with_header(data, prepend_header)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeJsonString(LibreMetaverse.StructuredData.OSD,System.Boolean)": "crate::json_codec::serialize(osd, preserve_defaults)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDInnerXmlString(LibreMetaverse.StructuredData.OSD)": "crate::xml_codec::serialize_inner(data)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDNotation(LibreMetaverse.StructuredData.OSD)": "crate::notation::serialize(osd)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDNotationFormatted(LibreMetaverse.StructuredData.OSD)": "crate::notation::serialize_formatted(osd)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDNotationStream(LibreMetaverse.StructuredData.OSD)": "crate::notation::serialize_stream(osd)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDNotationStreamFormatted(LibreMetaverse.StructuredData.OSD)": "crate::notation::serialize_stream_formatted(osd)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDProtobuf(LibreMetaverse.StructuredData.OSD,System.Boolean)": "crate::protobuf::serialize(osd, prepend_header)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDXmlBytes(LibreMetaverse.StructuredData.OSD)": "crate::xml_codec::serialize_bytes(data)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDXmlElement(System.Xml.XmlWriter,LibreMetaverse.StructuredData.OSD)": "crate::xml_codec::serialize_element(writer, data)", "M:LibreMetaverse.StructuredData.OSDParser.SerializeLLSDXmlString(LibreMetaverse.StructuredData.OSD)": "crate::xml_codec::serialize_string(data)", "M:LibreMetaverse.StructuredData.OSDParser.SkipWhiteSpace(System.IO.Stream)": "crate::binary::skip_whitespace(stream)", "M:LibreMetaverse.StructuredData.OSDParser.UnescapeCharacter(System.String,System.Char)": "crate::notation::unescape_character(s, c)", "M:LibreMetaverse.StructuredData.OSDParser.Deserialize(System.Byte[])": "crate::dispatch::deserialize_bytes(data)", "M:LibreMetaverse.StructuredData.OSDParser.Deserialize(System.IO.Stream)": "crate::dispatch::deserialize_stream(stream)", "M:LibreMetaverse.StructuredData.OSDParser.Deserialize(System.String)": "crate::dispatch::deserialize_string(data)", "M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32)": "crate::byte_order::read_single_little_endian(src, pos)", "M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single)": "crate::byte_order::write_single_little_endian(dest, pos, value)", "M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32)": "crate::byte_order::read_double_little_endian(src, pos)", "M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double)": "crate::byte_order::write_double_little_endian(dest, pos, value)", } CLIENT_CORE_NATIVE_OWNERS = ( "AgentSettings", "AssetCacheSettings", "ConnectionSettings", "GridClient", "LoggingSettings", "PacketSettings", "ParcelSettings", "Settings", "TexturePipelineSettings", "TimingSettings", "WorldSettings", ) NATIVE_OWNER_BOUNDS = { "T:LibreMetaverse.CacheDictionary`2": { "TKey": "TKey: Clone + PartialEq", "TValue": "TValue: Clone + PartialEq", }, "T:LibreMetaverse.CacheDictionary`2.CacheDictionaryEnumerator": { "TKey": "TKey: Clone", "TValue": "TValue: Clone", }, "T:LibreMetaverse.DoubleDictionary`3": { "TKey1": "TKey1: Clone + PartialEq", "TKey2": "TKey2: Clone + PartialEq", "TValue": "TValue: Clone + PartialEq", }, "T:LibreMetaverse.EmptyRemovalStrategy`1": {"TKey": "TKey: Clone + PartialEq + Send"}, "T:LibreMetaverse.ExpiringCache`2": { "TKey": "TKey: Clone + PartialEq", "TValue": "TValue: Clone", }, "T:LibreMetaverse.LruRemovalStrategy`1": {"TKey": "TKey: Clone + PartialEq + Send"}, "T:LibreMetaverse.MruRemovalStrategy`1": {"TKey": "TKey: Clone + PartialEq + Send"}, "T:LibreMetaverse.MultiValueDictionary`2": { "TKey": "TKey: Clone + PartialEq", "TValue": "TValue: Clone + PartialEq", }, } MAIN_TARGET = ROOT / "crates/libremetaverse/src/generated.rs" WIRE_NAMESPACES = ( "LibreMetaverse.Assets", "LibreMetaverse.Formatters", "LibreMetaverse.Imaging", "LibreMetaverse.ImportExport", "LibreMetaverse.Materials", "LibreMetaverse.Messages", "LibreMetaverse.Packets", "LibreMetaverse.Rendering", ) WIRE_ROOT_TYPES = { "T:LibreMetaverse.Animation", "T:LibreMetaverse.BitPack", "T:LibreMetaverse.Bumpiness", "T:LibreMetaverse.FieldType", "T:LibreMetaverse.GLTFMaterialUpdate", "T:LibreMetaverse.Grass", "T:LibreMetaverse.GrassDefinition", "T:LibreMetaverse.Interfaces.IMessage", "T:LibreMetaverse.LiveParticle", "T:LibreMetaverse.MappingType", "T:LibreMetaverse.MediaControls", "T:LibreMetaverse.MediaEntry", "T:LibreMetaverse.MediaPermission", "T:LibreMetaverse.NameValue", "T:LibreMetaverse.NameValue.ClassType", "T:LibreMetaverse.NameValue.SendtoType", "T:LibreMetaverse.NameValue.ValueType", "T:LibreMetaverse.PacketFrequency", "T:LibreMetaverse.PermissionMask", "T:LibreMetaverse.PermissionWho", "T:LibreMetaverse.Permissions", "T:LibreMetaverse.Shininess", "T:LibreMetaverse.TextureAttributes", "T:LibreMetaverse.Tree", "T:LibreMetaverse.TreeDefinition", } COMPOSED_BASE_TYPES = { "T:LibreMetaverse.InventoryAnimation", "T:LibreMetaverse.InventoryAttachment", "T:LibreMetaverse.InventoryCallingCard", "T:LibreMetaverse.InventoryCategory", "T:LibreMetaverse.InventoryFolder", "T:LibreMetaverse.InventoryGesture", "T:LibreMetaverse.InventoryItem", "T:LibreMetaverse.InventoryLSL", "T:LibreMetaverse.InventoryLandmark", "T:LibreMetaverse.InventoryMaterial", "T:LibreMetaverse.InventoryNotecard", "T:LibreMetaverse.InventoryObject", "T:LibreMetaverse.InventorySettings", "T:LibreMetaverse.InventorySnapshot", "T:LibreMetaverse.InventorySound", "T:LibreMetaverse.InventoryTexture", "T:LibreMetaverse.InventoryWearable", "T:LibreMetaverse.Messages.Linden.ChatSessionAcceptInvitation", "T:LibreMetaverse.Messages.Linden.ChatSessionRequestMuteUpdate", "T:LibreMetaverse.Messages.Linden.ChatSessionRequestStartConference", "T:LibreMetaverse.Messages.Linden.MapLayerReplyVariant", "T:LibreMetaverse.Packets.DirPlacesReplyPacket", "T:LibreMetaverse.Packets.TestMessagePacket", "T:LibreMetaverse.Rendering.Joint", } NATIVE_MESSAGE_CODEC_TYPES = { "T:LibreMetaverse.Messages.Linden.AgentGroupDataUpdateMessage", "T:LibreMetaverse.Messages.Linden.AgentGroupDataUpdateMessage.GroupData", "T:LibreMetaverse.Messages.Linden.AgentGroupDataUpdateMessage.NewGroupData", "T:LibreMetaverse.Messages.Linden.ChatSessionAcceptInvitation", "T:LibreMetaverse.Messages.Linden.ChatSessionRequestBlock", "T:LibreMetaverse.Messages.Linden.ChatSessionRequestMuteUpdate", "T:LibreMetaverse.Messages.Linden.ChatSessionRequestStartConference", "T:LibreMetaverse.Messages.Linden.ChatterBoxInvitationMessage", "T:LibreMetaverse.Messages.Linden.ChatterBoxSessionAgentListUpdatesMessage", "T:LibreMetaverse.Messages.Linden.ChatterBoxSessionAgentListUpdatesMessage.AgentUpdatesBlock", "T:LibreMetaverse.Messages.Linden.ChatterBoxSessionStartReplyMessage", "T:LibreMetaverse.Messages.Linden.ChatterboxSessionEventReplyMessage", "T:LibreMetaverse.Messages.Linden.CopyInventoryFromNotecardMessage", "T:LibreMetaverse.Messages.Linden.DirLandReplyMessage", "T:LibreMetaverse.Messages.Linden.DirLandReplyMessage.QueryReply", "T:LibreMetaverse.Messages.Linden.EnableSimulatorMessage", "T:LibreMetaverse.Messages.Linden.EnableSimulatorMessage.SimulatorInfoBlock", "T:LibreMetaverse.Messages.Linden.EstablishAgentCommunicationMessage", "T:LibreMetaverse.Messages.Linden.LandStatReplyMessage", "T:LibreMetaverse.Messages.Linden.LandStatReplyMessage.ReportDataBlock", "T:LibreMetaverse.Messages.Linden.MapLayerMessageBase", "T:LibreMetaverse.Messages.Linden.MapLayerReplyVariant", "T:LibreMetaverse.Messages.Linden.MapLayerReplyVariant.LayerData", "T:LibreMetaverse.Messages.Linden.ParcelObjectOwnersReplyMessage", "T:LibreMetaverse.Messages.Linden.ParcelObjectOwnersReplyMessage.PrimOwner", "T:LibreMetaverse.Messages.Linden.ParcelPropertiesMessage", "T:LibreMetaverse.Messages.Linden.ParcelPropertiesUpdateMessage", "T:LibreMetaverse.Messages.Linden.ParcelVoiceInfoRequestMessage", "T:LibreMetaverse.Messages.Linden.PlacesReplyMessage", "T:LibreMetaverse.Messages.Linden.PlacesReplyMessage.QueryData", "T:LibreMetaverse.Messages.Linden.ProvisionVoiceAccountRequestMessage", "T:LibreMetaverse.Messages.Linden.RemoteParcelRequestReply", "T:LibreMetaverse.Messages.Linden.RequiredVoiceVersionMessage", "T:LibreMetaverse.Messages.Linden.ScriptRunningReplyMessage", "T:LibreMetaverse.Messages.Linden.SendPostcardMessage", "T:LibreMetaverse.Messages.Linden.TeleportFailedMessage", "T:LibreMetaverse.Messages.Linden.TeleportFinishMessage", "T:LibreMetaverse.Messages.Linden.UpdateAgentInformationMessage", "T:LibreMetaverse.Messages.Linden.UpdateAgentInventoryRequestMessage", "T:LibreMetaverse.Messages.Linden.UpdateAgentLanguageMessage", "T:LibreMetaverse.Messages.Linden.UpdateScriptAgentRequestMessage", "T:LibreMetaverse.Messages.Linden.UpdateScriptTaskUpdateMessage", "T:LibreMetaverse.Messages.Linden.ViewerStatsMessage", } OPEN_ENUM_IDS = {"T:LibreMetaverse.BakeType", "T:LibreMetaverse.HoleType"} TRAIT_SUPERTRAITS = { "T:LibreMetaverse.IBakingTextureProvider": "std::any::Any", "T:LibreMetaverse.Appearance.ICurrentOutfitPolicy": "Send + Sync", } 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", } AVATAR_NAMESPACES = ( "LibreMetaverse.Animesh", "LibreMetaverse.Appearance", ) AVATAR_TYPE_PREFIXES = ( "T:LibreMetaverse.Agent", "T:LibreMetaverse.Animations", "T:LibreMetaverse.AnimeshSkinning", "T:LibreMetaverse.Appearance", "T:LibreMetaverse.ArchetypeParam", "T:LibreMetaverse.Asset", "T:LibreMetaverse.Attention", "T:LibreMetaverse.Avatar", "T:LibreMetaverse.Inventory", ) AVATAR_ROOT_TYPES = { "T:LibreMetaverse.AISResponseMeta", "T:LibreMetaverse.AccountLevelBenefits", "T:LibreMetaverse.AlertMessageEventArgs", "T:LibreMetaverse.BakeType", "T:LibreMetaverse.BalanceEventArgs", "T:LibreMetaverse.BinBVHAnimationReader", "T:LibreMetaverse.BorderCrossingDirection", "T:LibreMetaverse.CallingCardOfferedEventArgs", "T:LibreMetaverse.CameraConstraintEventArgs", "T:LibreMetaverse.ChannelType", "T:LibreMetaverse.ChatAudibleLevel", "T:LibreMetaverse.ChatEventArgs", "T:LibreMetaverse.ChatSessionMember", "T:LibreMetaverse.ChatSessionMemberAddedEventArgs", "T:LibreMetaverse.ChatSessionMemberLeftEventArgs", "T:LibreMetaverse.ChatSourceType", "T:LibreMetaverse.ChatType", "T:LibreMetaverse.ClassifiedAd", "T:LibreMetaverse.ClassifiedInfoReplyEventArgs", "T:LibreMetaverse.DeRezDestination", "T:LibreMetaverse.DisplayNameUpdateEventArgs", "T:LibreMetaverse.DrivenParamInfo", "T:LibreMetaverse.EffectType", "T:LibreMetaverse.ExperienceInfoEventArgs", "T:LibreMetaverse.ExperiencePreferencesEventArgs", "T:LibreMetaverse.FindObjectByPathReplyEventArgs", "T:LibreMetaverse.FolderUpdatedEventArgs", "T:LibreMetaverse.Genepool", "T:LibreMetaverse.GenepoolArchetype", "T:LibreMetaverse.GroupChatJoinedEventArgs", "T:LibreMetaverse.HandPose", "T:LibreMetaverse.InitiateDownloadEventArgs", "T:LibreMetaverse.InstantMessage", "T:LibreMetaverse.InstantMessageDialog", "T:LibreMetaverse.InstantMessageEventArgs", "T:LibreMetaverse.InstantMessageOnline", "T:LibreMetaverse.ItemReceivedEventArgs", "T:LibreMetaverse.LindenAttentions", "T:LibreMetaverse.LoadUrlEventArgs", "T:LibreMetaverse.LookAtType", "T:LibreMetaverse.MeanCollisionEventArgs", "T:LibreMetaverse.MeanCollisionType", "T:LibreMetaverse.MoneyBalanceReplyEventArgs", "T:LibreMetaverse.MoneyTransactionType", "T:LibreMetaverse.MuteEntry", "T:LibreMetaverse.MuteFlags", "T:LibreMetaverse.MuteType", "T:LibreMetaverse.NavMeshStatusUpdateEventArgs", "T:LibreMetaverse.PayPriceReplyEventArgs", "T:LibreMetaverse.PayPriceType", "T:LibreMetaverse.PickInfoReplyEventArgs", "T:LibreMetaverse.PointAtType", "T:LibreMetaverse.ProductInfoEventArgs", "T:LibreMetaverse.ProfileFlags", "T:LibreMetaverse.ProfilePick", "T:LibreMetaverse.RebakeAvatarTexturesEventArgs", "T:LibreMetaverse.RegionCrossedEventArgs", "T:LibreMetaverse.RegionCrossingPredictionEventArgs", "T:LibreMetaverse.RegionExperiencesEventArgs", "T:LibreMetaverse.SaveAssetToInventoryEventArgs", "T:LibreMetaverse.ScriptControlChange", "T:LibreMetaverse.ScriptControlEventArgs", "T:LibreMetaverse.ScriptDialogEventArgs", "T:LibreMetaverse.ScriptPermission", "T:LibreMetaverse.ScriptQuestionEventArgs", "T:LibreMetaverse.ScriptRunningReplyEventArgs", "T:LibreMetaverse.ScriptSensorReplyEventArgs", "T:LibreMetaverse.ScriptSensorTypeFlags", "T:LibreMetaverse.SetDisplayNameReplyEventArgs", "T:LibreMetaverse.SkeletalBoneInfo", "T:LibreMetaverse.TaskInventoryReplyEventArgs", "T:LibreMetaverse.TaskItemReceivedEventArgs", "T:LibreMetaverse.TeleportEventArgs", "T:LibreMetaverse.TeleportFlags", "T:LibreMetaverse.TeleportLureFlags", "T:LibreMetaverse.TeleportStatus", "T:LibreMetaverse.TransactionFlags", "T:LibreMetaverse.TransactionInfo", "T:LibreMetaverse.UUIDNameReplyEventArgs", "T:LibreMetaverse.ViewerBenefitsEventArgs", "T:LibreMetaverse.ViewerEffectEventArgs", "T:LibreMetaverse.ViewerEffectLookAtEventArgs", "T:LibreMetaverse.ViewerEffectPointAtEventArgs", "T:LibreMetaverse.VisualAlphaParam", "T:LibreMetaverse.VisualColorOperation", "T:LibreMetaverse.VisualColorParam", "T:LibreMetaverse.VisualParam", "T:LibreMetaverse.VisualParams", "T:LibreMetaverse.VolumeMorphInfo", "T:LibreMetaverse.binBVHJoint", "T:LibreMetaverse.binBVHJointKey", } UNDERLYING = { "System.SByte": "i8", "System.Byte": "u8", "System.Int16": "i16", "System.UInt16": "u16", "System.Int32": "i32", "System.UInt32": "u32", "System.Int64": "i64", "System.UInt64": "u64", } PRIVATE_LAYOUTS = { "T:LibreMetaverse.AvatarAppearanceEventArgs": [("stored_visual_params", "Vec")], "T:LibreMetaverse.CacheDictionary`2": [("entries", "std::collections::HashMap")], "T:LibreMetaverse.DoubleDictionary`3": [ ("by_first", "std::collections::HashMap"), ("by_second", "std::collections::HashMap"), ], "T:LibreMetaverse.EmptyRemovalStrategy`1": [("keys", "Vec")], "T:LibreMetaverse.ExpiringCache`2": [("entries", "std::collections::HashMap")], "T:LibreMetaverse.LruRemovalStrategy`1": [("keys", "Vec")], "T:LibreMetaverse.MruRemovalStrategy`1": [("keys", "Vec")], "T:LibreMetaverse.MultiValueDictionary`2": [("entries", "std::collections::HashMap>")], "T:LibreMetaverse.TokenBucket": [("content", "i32"), ("max_burst", "i32"), ("drip_rate", "i32")], "T:LibreMetaverse.UUID": [("bytes", "[u8; 16]")], "T:LibreMetaverse.StructuredData.OSDArray": [("values", "Vec")], "T:LibreMetaverse.StructuredData.OSDBinary": [("value", "Vec")], "T:LibreMetaverse.StructuredData.OSDBoolean": [("value", "bool")], "T:LibreMetaverse.StructuredData.OSDDate": [("value", "std::time::SystemTime")], "T:LibreMetaverse.StructuredData.OSDException": [("message", "String")], "T:LibreMetaverse.StructuredData.OSDInteger": [("value", "i32")], "T:LibreMetaverse.StructuredData.OSDMap": [("values", "std::collections::HashMap")], "T:LibreMetaverse.StructuredData.OSDReal": [("value", "f64")], "T:LibreMetaverse.StructuredData.OSDString": [("value", "String")], "T:LibreMetaverse.StructuredData.OSDUUID": [("value", "libremetaverse_types::UUID")], "T:LibreMetaverse.StructuredData.OSDUri": [("value", "libremetaverse_types::compat::Uri")], } VALUE_DERIVES = { "T:LibreMetaverse.AgentSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.AssetCacheSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.ConnectionSettings": "Clone, Eq, PartialEq", "T:LibreMetaverse.LoggingSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.Matrix4": "Clone, Copy, Debug", "T:LibreMetaverse.Quaternion": "Clone, Copy, Debug", "T:LibreMetaverse.PacketSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.ParcelSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.TexturePipelineSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.TimingSettings": "Clone, Debug, Eq, PartialEq", "T:LibreMetaverse.UUID": "Clone, Copy, Debug, Default, Eq, Hash, PartialEq", "T:LibreMetaverse.Vector3": "Clone, Copy, Debug", "T:LibreMetaverse.Vector3d": "Clone, Copy, Debug", "T:LibreMetaverse.WorldSettings": "Clone, Debug, Eq, PartialEq", } def generic_names(item: dict) -> list[str]: return [parameter["name"] for parameter in item.get("generic_parameters", [])] def format_rust(source: str) -> str: result = subprocess.run( ["rustfmt", "--edition", "2024", "--emit", "stdout"], input=source, text=True, capture_output=True, check=False, ) if result.returncode: raise ValueError(result.stderr.strip()) return result.stdout def type_name(type_row: dict) -> str: return type_row["rust_path"].rsplit("::", 1)[-1] def generic_suffix(names: list[str]) -> str: return "<" + ", ".join(names) + ">" if names else "" def generic_declarations(item: dict, mapper: mapping.Mapper, owner_names: list[str]) -> list[str]: generics = set(owner_names) | set(generic_names(item)) declarations = [] for parameter in item.get("generic_parameters", []): bounds = [mapper.constraint(constraint, generics) for constraint in parameter.get("constraints", [])] if parameter.get("default_constructor"): bounds.append("Default") declarations.append(parameter["name"] + ((": " + " + ".join(bounds)) if bounds else "")) return declarations def function_name(signature: str) -> str: match = re.search(r"\bfn ([A-Za-z_][A-Za-z0-9_]*)", signature) if not match: raise ValueError(f"invalid mapped function signature: {signature}") return match.group(1) def render_compile_fixture( catalog: dict, type_by_id: dict[str, dict[str, str]], member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper, ) -> str: lines = [ "// @generated by tools/generate_api_shims.py; do not edit by hand.", "#![allow(clippy::all, clippy::pedantic, dead_code, unused_variables)]", "#![allow(non_camel_case_types, non_snake_case)]", "", 'fn argument() -> T { panic!("compile-only API fixture") }', "", ] type_index = member_index = 0 for assembly in catalog["assemblies"]: for item in assembly["types"]: type_index += 1 type_row = type_by_id[item["doc_id"]] owner_names = generic_names(item) owner_path = type_row["rust_path"] owner_type = owner_path + generic_suffix(owner_names) owner_expression = owner_path + (("::<" + ", ".join(owner_names) + ">") if owner_names else "") trait = item["kind"] == "interface" type_parameters = [*owner_names] if trait: type_parameters.append(f"__Probe: {owner_type}") type_argument = "&__Probe" else: type_argument = f"&{owner_type}" declarations = generic_suffix(type_parameters) lines += [ f"// C# type: `{item['doc_id']}`.", f"fn type_{type_index:04}{declarations}(_: {type_argument}) {{}}", "", ] for member in item["members"]: member_index += 1 row = member_rows[member["doc_id"]] method_names = generic_names(member) owner_bounds = NATIVE_OWNER_BOUNDS.get(item["doc_id"], {}) parameters = [ *(owner_bounds.get(name, name) for name in owner_names), *generic_declarations(member, mapper, owner_names), ] if member["doc_id"] == "M:LibreMetaverse.Utils.EnumTryParse``1(System.String,``0@)": parameters = [ *owner_names, "T: libremetaverse_types::EnumValueMetadata", ] if trait: parameters.append(f"__Probe: {owner_type}") declaration = generic_suffix(parameters) instance = not member.get("static") and member["kind"] not in { "constructor", "constant", "enum_value", } value_parameter = ( f"value: &mut {'__Probe' if trait else owner_type}" if instance else "" ) body = [] if member["kind"] == "field" and not member.get("static"): field = re.fullmatch(r"pub ([^:]+): .+", row["rust_signature"]) if not field: raise ValueError(f"invalid mapped field signature: {row['rust_signature']}") body.append(f"let _ = &value.{field.group(1)};") elif member["kind"] in {"constant", "enum_value"}: pattern = r"pub const ([A-Za-z_][A-Za-z0-9_]*)" if member["kind"] == "constant" else r"([A-Za-z_][A-Za-z0-9_]*):" name = re.match(pattern, row["rust_signature"]) if not name: raise ValueError(f"invalid mapped value signature: {row['rust_signature']}") body.append(f"let _ = {owner_expression}::{name.group(1)};") else: signatures = row["rust_signature"].split(" ; ") property_argument_counts = [] if member["kind"] in {"property", "indexer"}: accessors = {accessor["kind"] for accessor in member.get("accessors", [])} if "get" in accessors: property_argument_counts.append(len(member.get("parameters", []))) if "set" in accessors: property_argument_counts.append(len(member.get("parameters", [])) + 1) for signature_index, signature in enumerate(signatures): name = function_name(signature) generic_call = (("::<" + ", ".join(method_names) + ">") if method_names else "") if instance: target = f"value.{name}{generic_call}" elif trait: target = f"<__Probe as {owner_type}>::{name}{generic_call}" else: target = f"{owner_expression}::{name}{generic_call}" argument_count = len(member.get("parameters", [])) if member["kind"] == "event": argument_count = 1 elif property_argument_counts: argument_count = property_argument_counts[signature_index] arguments = ", ".join("argument()" for _ in range(argument_count)) body.append(f"let _ = {target}({arguments});") lines += [ f"// C# member: `{member['doc_id']}`.", f"fn member_{member_index:05}{declaration}({value_parameter}) {{", *[f" {line}" for line in body], "}", "", ] return format_rust("\n".join(lines).rstrip() + "\n") def flags_enum(item: dict) -> bool: return any(attribute["type"] == "System.FlagsAttribute" for attribute in item.get("attributes", [])) def duplicate_enum_values(item: dict) -> bool: values = [member["value"]["value"] for member in item["members"] if member["kind"] == "enum_value"] return len(values) != len(set(values)) def render_enum(item: dict, rust_name: str) -> list[str]: underlying = UNDERLYING[item["enum_underlying_type"]] values = [member for member in item["members"] if member["kind"] == "enum_value"] lines = [f"/// C# type: `{item['doc_id']}`."] if item["doc_id"] in OPEN_ENUM_IDS: lines += [ "#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]", "#[repr(transparent)]", f"pub struct {rust_name}(pub {underlying});", "#[allow(non_upper_case_globals)]", f"impl {rust_name} {{", ] for member in values: lines += [ f" /// C# member: `{member['doc_id']}`.", f" pub const {mapping.pascal(member['name'])}: Self = Self({member['value']['value']});", ] lines.append("}") return lines if flags_enum(item) or duplicate_enum_values(item): lines += [ "#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]", "#[repr(transparent)]", f"pub struct {rust_name}(pub {underlying});", f"impl {rust_name} {{", ] for member in values: name = mapping.snake(member["name"]).upper() value = member["value"]["value"] lines += [f" /// C# member: `{member['doc_id']}`.", f" pub const {name}: Self = Self({value});"] lines.append("}") return lines lines += [ "#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]", f"#[repr({underlying})]", f"pub enum {rust_name} {{", ] for member in values: lines += [f" /// C# member: `{member['doc_id']}`.", f" {mapping.pascal(member['name'])} = {member['value']['value']},"] lines.append("}") return lines def native_member_body(member_id: str) -> str | None: body = NATIVE_MEMBER_BODIES.get(member_id) if body is not None: return body message_owner = "T:" + member_id[2:].split(".#ctor", 1)[0] if member_id.startswith("M:") and ".#ctor" in member_id else None if message_owner in NATIVE_MESSAGE_CODEC_TYPES: return "Ok(::new_generated())" message_owner = "T:" + member_id[2:].rsplit(".Deserialize", 1)[0] if member_id.startswith("M:") and ".Deserialize(" in member_id else None if message_owner in NATIVE_MESSAGE_CODEC_TYPES: return "::deserialize_generated(self, &map)" message_owner = "T:" + member_id[2:].rsplit(".Serialize", 1)[0] if member_id.startswith("M:") and member_id.endswith(".Serialize") else None if message_owner in NATIVE_MESSAGE_CODEC_TYPES: return "::serialize_generated(self)" if re.fullmatch(r"M:LibreMetaverse\.Packets\.[^.]+Packet\.#ctor", member_id): return "Ok(::new_generated())" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.#ctor\(LibreMetaverse\.Packets\.Header,System\.Byte\[\],System\.Int32@\)", member_id, ): return "::new_from_header(head, &bytes, i)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.#ctor\(System\.Byte\[\],System\.Int32@\)", member_id, ): return "::new_from_bytes(&bytes, i)" if re.fullmatch(r"M:LibreMetaverse\.Packets\.[^.]+Packet\.[^.]+Block\.#ctor", member_id): return "Ok(::new_generated())" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.[^.]+Block\.#ctor\(System\.Byte\[\],System\.Int32@\)", member_id, ): return "::new_from_bytes(&bytes, i)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.[^.]+Block\.FromBytes\(System\.Byte\[\],System\.Int32@\)", member_id, ): return "::decode_from_bytes(self, &bytes, i)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.[^.]+Block\.ToBytes\(System\.Byte\[\],System\.Int32@\)", member_id, ): return "::encode_to_slice(self, bytes, i)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.[^.]+Block\.FromOSD\(LibreMetaverse\.StructuredData\.OSDMap\)", member_id, ): return "::from_osd(self, &map)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.[^.]+Block\.ToOSD", member_id, ): return "::to_osd(self)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.FromBytes\(LibreMetaverse\.Packets\.Header,System\.Byte\[\],System\.Int32@,System\.Int32@\)", member_id, ): return "::decode_from_header(self, header, &bytes, i, packet_end)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.FromBytes\(System\.Byte\[\],System\.Int32@,System\.Int32@,System\.Byte\[\]\)", member_id, ): return "::decode_from_bytes(self, &bytes, i, packet_end, zero_buffer)" if re.fullmatch(r"M:LibreMetaverse\.Packets\.[^.]+Packet\.ToBytes", member_id): return "::encode_packet(self)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.PacketFromOSD\(LibreMetaverse\.StructuredData\.OSDMap\)", member_id, ): return "::packet_from_osd(self, &body)" if re.fullmatch(r"M:LibreMetaverse\.Packets\.[^.]+Packet\.PacketToOSD", member_id): return "::packet_to_osd(self)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.ToBytes\(LibreMetaverse\.Interfaces\.IByteBufferPool,System\.Int32@\)", member_id, ): return "{ let _ = pool; let bytes = ::encode_packet(self)?; *size = i32::try_from(bytes.len()).map_err(|_| crate::Error::Argument)?; Ok(bytes) }" if re.fullmatch(r"M:LibreMetaverse\.Packets\.[^.]+Packet\.ToBytesMultiple", member_id): return "::encode_multiple(self)" if re.fullmatch( r"M:LibreMetaverse\.Packets\.[^.]+Packet\.ToBytesMultiple\(LibreMetaverse\.Interfaces\.IByteBufferPool,System\.Int32\[\]@\)", member_id, ): return "{ let _ = pool; let packets = ::encode_multiple(self)?; *sizes = packets.iter().map(|packet| i32::try_from(packet.len()).unwrap_or(i32::MAX)).collect(); Ok(packets) }" if re.fullmatch(r"P:LibreMetaverse\.Packets\.[^.]+Packet\.Length", member_id): return "::generated_length(self)" if re.fullmatch(r"P:LibreMetaverse\.Packets\.[^.]+Packet\.UsesBufferPooling", member_id): return "::USES_BUFFER_POOLING" if re.fullmatch(r"P:LibreMetaverse\.Packets\.[^.]+Packet\.[^.]+Block\.Length", member_id): return "::generated_length(self)" if member_id == "M:LibreMetaverse.Packets.Packet.GetType(System.UInt16,LibreMetaverse.PacketFrequency)": return "Ok(crate::packet_catalog::packet_type(id, frequency))" if member_id == "M:LibreMetaverse.Packets.Packet.BuildPacket(LibreMetaverse.Packets.PacketType)": return "crate::packet_catalog::build_packet(type_)" if member_id in { "M:LibreMetaverse.Packets.Packet.BuildPacket(System.String,LibreMetaverse.StructuredData.OSDMap)", "M:LibreMetaverse.Packets.Packet.BuildPacketFromOSD(System.String,LibreMetaverse.StructuredData.OSDMap)", }: return "crate::packet_catalog::build_packet_from_osd(&caps_event_name, &body)" if member_id == "M:LibreMetaverse.Packets.Packet.BuildPacket(System.Byte[],System.Int32@,System.Byte[])": return "{ let mut zero_buffer = zero_buffer; crate::packet_wire::build_packet_from_bytes(&packet_buffer, packet_end, &mut zero_buffer) }" if member_id == "M:LibreMetaverse.Packets.Header.BuildHeader(System.Byte[],System.Int32@,System.Int32@)": return "crate::packet_wire::decode_header(&bytes, pos, packet_end)" if member_id == "M:LibreMetaverse.Packets.Header.FromBytes(System.Byte[],System.Int32@,System.Int32@)": return "{ *self = crate::packet_wire::decode_header(&bytes, pos, packet_end)?; Ok(()) }" if member_id == "M:LibreMetaverse.Packets.Header.ToBytes(System.Byte[],System.Int32@)": return "crate::packet_wire::header_to_slice(self, bytes, i)" if member_id == "M:LibreMetaverse.Packets.Header.AcksToBytes(System.Byte[],System.Int32@)": return "crate::packet_wire::acks_to_slice(self, bytes, i)" if member_id == "M:LibreMetaverse.Packets.Packet.PacketFromOSD(LibreMetaverse.StructuredData.OSDMap)": return "crate::packet_catalog::ignore_packet_osd(&osd)" if member_id == "M:LibreMetaverse.Packets.Packet.PacketToOSD": return "crate::packet_catalog::empty_packet_osd()" if member_id == "M:LibreMetaverse.Helpers.ZeroDecode(System.Byte[],System.Int32,System.Byte[])": return "crate::packet_wire::zero_decode(src, srclen, dest)" if member_id == "M:LibreMetaverse.Helpers.ZeroEncode(System.Byte[],System.Int32,System.Byte[])": return "crate::packet_wire::zero_encode(src, srclen, dest)" return None def render_body(signature: str, member_id: str, error_model: str, asyncness: str, trait: bool) -> str: if trait: signature = signature.removeprefix("pub ") if body := native_member_body(member_id): marker = ( " /* native client-core implementation */" if any(f"LibreMetaverse.{owner}." in member_id for owner in CLIENT_CORE_NATIVE_OWNERS) else "" ) return f" {signature} {{{marker} {body} }}" failure = ( f"libremetaverse_types::not_implemented({json.dumps(member_id)})" if error_model.startswith("Result") else f"libremetaverse_types::unimplemented_api!({json.dumps(member_id)})" ) if asyncness == "boxed_future" or "std::pin::Pin str: value = item["value"]["value"] if item["type"] == "System.String": return json.dumps(value) if item["type"] == "System.Boolean": return value.lower() if item["type"] in {"System.Single", "System.Double"} and not any( marker in value.lower() for marker in (".", "e") ): return value + ".0" return value def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str: rust_name = type_name(type_row) if native_path := NATIVE_TYPES.get(item["doc_id"]): lines = [f"/// C# type: `{item['doc_id']}`."] lines.extend(f"/// C# member: `{member['doc_id']}`." for member in item["members"]) lines.append(f"pub use {native_path} as {rust_name};") return "\n".join(lines) if item["kind"] == "enum": return "\n".join(render_enum(item, rust_name)) names = generic_names(item) suffix = generic_suffix(names) trait = item["kind"] == "interface" lines = [f"/// C# type: `{item['doc_id']}`."] if trait: supertrait = TRAIT_SUPERTRAITS.get(item["doc_id"]) inheritance = f": {supertrait}" if supertrait else "" lines.append(f"pub trait {rust_name}{suffix}{inheritance} {{") else: fields = [member for member in item["members"] if member["kind"] == "field" and not member.get("static")] private_fields = PRIVATE_LAYOUTS.get(item["doc_id"], []) base_type = item.get("base_type") if item["doc_id"] in COMPOSED_BASE_TYPES else None native_declaration = NATIVE_DECLARATIONS.get(item["doc_id"]) if native_declaration: lines.append(f"pub use {native_declaration} as {rust_name};") lines.extend( f"/// C# member: `{field['doc_id']}`." for field in fields ) elif derives := VALUE_DERIVES.get(item["doc_id"]): lines.append(f"#[derive({derives})]") if native_declaration: pass elif item["doc_id"] == "T:LibreMetaverse.StructuredData.OSD": lines += [ "#[non_exhaustive]", "pub enum OSD {", " Undefined, Boolean(bool), Integer(i32), Real(f64), String(String),", " UUID(libremetaverse_types::UUID), Date(std::time::SystemTime),", " Uri(libremetaverse_types::compat::Uri), Binary(Vec),", " Array(Vec), Map(std::collections::HashMap), LlsdXml(String),", "}", ] elif not fields and not names and not private_fields and not base_type: lines.append(f"pub struct {rust_name};") else: lines.append(f"pub struct {rust_name}{suffix} {{") if base_type: lines += [ f" /// Rust composition for C# base type `{base_type}`.", f" pub base: {mapper.type(base_type, None, set(names))},", ] for field in fields: 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: tuple_type = "(" + ", ".join(names) + ("," if len(names) == 1 else "") + ")" lines.append(f" _marker: std::marker::PhantomData<{tuple_type}>,") lines.append("}") lines += [f"impl{suffix} {rust_name}{suffix} {{"] for member in item["members"]: if member["kind"] in {"enum_value"} or (member["kind"] == "field" and not member.get("static")): continue row = member_rows[member["doc_id"]] lines.append(f" /// C# member: `{member['doc_id']}`.") if member["kind"] == "constant": lines.append(f" {row['rust_signature']} = {rust_constant_value(member)};") continue signatures = row["rust_signature"].split(" ; ") for index, signature in enumerate(signatures): if index: lines.append(f" /// Setter for C# member: `{member['doc_id']}`.") body_member_id = member["doc_id"] setter_id = body_member_id + "#set" if index and setter_id in NATIVE_MEMBER_BODIES: body_member_id = setter_id lines.append( render_body( signature, body_member_id, row["error_model"], row["asyncness"], trait, ) ) lines.append("}") if not trait: for interface in item.get("interfaces", []): base, arguments, _ = mapping.type_parts(interface) if base not in mapper.interfaces or not base.startswith("LibreMetaverse."): continue # Native traits can have required methods. Their concrete adapters # provide explicit implementations beside the adapter code instead # of receiving an invalid empty generated impl. if f"T:{base}" in NATIVE_TYPES: continue if item["doc_id"] == "T:LibreMetaverse.GridClient" and base == "LibreMetaverse.IGridClient": continue mapped_arguments = [mapper.type(argument, None, set(names)) for argument in arguments] trait_path = mapper.resolved[base] + generic_suffix(mapped_arguments) lines.append(f"impl{suffix} {trait_path} for {rust_name}{suffix} {{}}") return "\n".join(lines) def render_pending_type(item: dict, type_row: dict) -> str: rust_name = type_name(type_row) names = generic_names(item) suffix = generic_suffix(names) lines = [f"/// Pending catalog type `{item['doc_id']}`; completed by a later milestone issue."] if item["kind"] == "interface": lines.append(f"pub trait {rust_name}{suffix} {{}}") elif names: tuple_type = "(" + ", ".join(names) + ("," if len(names) == 1 else "") + ")" lines += [f"pub struct {rust_name}{suffix} {{", f" _marker: std::marker::PhantomData<{tuple_type}>,", "}"] else: lines.append(f"pub struct {rust_name};") return "\n".join(lines) def wire_type(item: dict) -> bool: return ( item["doc_id"] in WIRE_ROOT_TYPES or item["doc_id"].startswith("T:LibreMetaverse.Primitive") or item["namespace"].startswith(WIRE_NAMESPACES) ) def core_type(item: dict) -> bool: return item["doc_id"] in CORE_ROOT_TYPES or item["namespace"].startswith(CORE_NAMESPACES) def avatar_type(item: dict) -> bool: return ( item["doc_id"] in AVATAR_ROOT_TYPES or item["doc_id"].startswith(AVATAR_TYPE_PREFIXES) or item["namespace"].startswith(AVATAR_NAMESPACES) ) def world_type(item: dict) -> bool: return not wire_type(item) and not core_type(item) and not avatar_type(item) 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: node = tree for segment in filter(None, type_row["rust_module"].split("::")): node = node["children"].setdefault(segment, {"items": [], "children": {}}) node["items"].append((item, type_row, complete)) def render_node(node: dict) -> list[str]: lines = [] for item, type_row, complete in node["items"]: rendered = render_type(item, type_row, member_rows, mapper) if complete else render_pending_type(item, type_row) lines += [rendered, ""] for name, child in sorted(node["children"].items()): body = render_node(child) lines.append(f"pub mod {name} {{") lines += [(" " + line) if line else "" for line in body] lines += ["}", ""] return lines return "\n".join(render_node(tree)) 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"}, *{ mapping.type_parts(item["signature"])[0] for item in catalog["external_types"] if item["kind"] == "interface" and not item["assembly"]["name"].startswith("System") }, } mapper = mapping.Mapper(resolved, interfaces) member_rows = {row["csharp_id"]: row for row in csv.DictReader(LEDGER.open(), delimiter="\t")} outputs = {} coverage = {} for assembly in catalog["assemblies"]: name = assembly["identity"]["name"] if name not in TARGETS and name != "LibreMetaverse": continue chunks = [ "// @generated by tools/generate_api_shims.py; do not edit by hand.", "#![allow(clippy::missing_errors_doc)]", "#![allow(clippy::must_use_candidate)]", "#![allow(clippy::all, clippy::pedantic, dead_code, unused_variables)]", "#![allow(non_camel_case_types)]", "#![allow(non_snake_case)]", "", ] selected = ( assembly["types"] if name in TARGETS else [ item for item in assembly["types"] if wire_type(item) or core_type(item) or avatar_type(item) or world_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) for item in assembly["types"] ] chunks.append(render_modules(rendered_items, member_rows, mapper)) source = format_rust("\n".join(chunks).rstrip() + "\n") expected_types = selected_ids expected_members = {member["doc_id"] for item in selected for member in item["members"]} emitted_types = set(re.findall(r"C# type: `(.*)`\.", source)) emitted_members = set(re.findall(r"C# member: `(.*)`\.", source)) if emitted_types != expected_types or emitted_members != expected_members: raise ValueError( f"generated catalog ID mismatch for {name}: " f"types missing/stale={len(expected_types - emitted_types)}/{len(emitted_types - expected_types)}, " f"members missing/stale={len(expected_members - emitted_members)}/{len(emitted_members - expected_members)}; " f"missing={sorted(expected_members - emitted_members)}, stale={sorted(emitted_members - expected_members)}" ) outputs[TARGETS.get(name, MAIN_TARGET)] = source coverage[name] = (len(selected), sum(len(item["members"]) for item in selected), len(selected) == len(assembly["types"])) outputs[COMPILE_FIXTURE] = render_compile_fixture(catalog, type_by_id, member_rows, mapper) return outputs, coverage def coverage_report(catalog: dict, coverage: dict[str, tuple[int, int, bool]]) -> str: lines = [ "# Callable shim coverage", "", "Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.", "", "| Assembly | Types | Members | Status |", "|---|---:|---:|---|", ] total_types = total_members = 0 for assembly in catalog["assemblies"]: name = assembly["identity"]["name"] types = len(assembly["types"]) members = sum(len(item["members"]) for item in assembly["types"]) if name in coverage: got_types, got_members, complete = coverage[name] if complete and (types, members) != (got_types, got_members): raise ValueError(f"incomplete generated slice for {name}") native_type_ids = { item["doc_id"] for item in assembly["types"] if item["doc_id"] in NATIVE_TYPES or item["doc_id"] in NATIVE_DECLARATIONS or (name == "LibreMetaverse.Types" and item["kind"] == "enum") } native_member_ids = { member["doc_id"] for item in assembly["types"] for member in item["members"] if item["doc_id"] in NATIVE_TYPES or (name == "LibreMetaverse.Types" and item["kind"] == "enum") or ( item["doc_id"] in NATIVE_DECLARATIONS and member["kind"] == "field" and not member.get("static") ) or ( item["doc_id"] in NATIVE_DECLARATIONS and member["kind"] == "constant" ) or native_member_body(member["doc_id"]) is not None } if native_type_ids or native_member_ids: type_noun = "type" if len(native_type_ids) == 1 else "types" if len(native_member_ids) == members: status = ( f"native implementation: {len(native_type_ids):,} {type_noun} / " f"{len(native_member_ids):,} members; no generated shims remain" ) else: status = ( f"native implementation: {len(native_type_ids):,} {type_noun} / " f"{len(native_member_ids):,} members; remaining surface is callable failure-only shims" ) else: status = "callable failure-only shim" if complete else f"partial callable shim ({got_types:,} types / {got_members:,} members)" total_types += got_types total_members += got_members else: status = "pending milestone issue" lines.append(f"| `{name}` | {types:,} | {members:,} | {status} |") if "LibreMetaverse" in coverage: 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)] avatar_types = [ item for item in main_types if avatar_type(item) and not wire_type(item) and not core_type(item) ] world_types = [item for item in main_types if world_type(item)] complete = coverage["LibreMetaverse"][2] lines += [ "", ( "The `LibreMetaverse` assembly is complete across issue #7 wire/data, issue #8 core runtime/networking," if complete else "The partial `LibreMetaverse` row contains the completed issue #7 wire/data, issue #8 core runtime/networking," ), ( "issue #9 avatar-facing manager, and issue #10 world/social/service manager slices." if complete else "and issue #9 avatar-facing manager slices. World/social/service manager types remain pending for issue #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):,} |", f"| Issue #9 avatar-facing managers | {len(avatar_types):,} | {sum(len(item['members']) for item in avatar_types):,} |", f"| Issue #10 world/social/service managers | {len(world_types):,} | {sum(len(item['members']) for item in world_types):,} |", ] lines += ["", f"Current callable coverage: **{total_types:,} types / {total_members:,} members**.", ""] return "\n".join(lines) def generate() -> dict[Path, str]: catalog = json.loads(CATALOG.read_text()) sources, coverage = generate_sources(catalog) sources[COVERAGE] = coverage_report(catalog, coverage) return sources def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--check", action="store_true") args = parser.parse_args() outputs = generate() if args.check: stale = [str(path.relative_to(ROOT)) for path, text in outputs.items() if not path.exists() or path.read_text() != text] if stale: raise SystemExit("stale generated API shims: " + ", ".join(stale)) else: for path, text in outputs.items(): path.write_text(text) print("generated " + ", ".join(str(path.relative_to(ROOT)) for path in outputs)) if __name__ == "__main__": main()