#!/usr/bin/env python3 """Generate and validate the reviewed C# to Rust public API mapping ledgers.""" from __future__ import annotations import argparse import csv import hashlib import io import json import keyword import re from collections import Counter, defaultdict from pathlib import Path ROOT = Path(__file__).resolve().parents[1] CATALOG = ROOT / "api/public-api.json" MEMBER_LEDGER = ROOT / "api/RUST-MAPPING.tsv" TYPE_LEDGER = ROOT / "api/RUST-TYPES.tsv" COVERAGE = ROOT / "api/MAPPING-COVERAGE.md" ASSEMBLY_CRATES = { "LibreMetaverse": "libremetaverse", "LibreMetaverse.Imaging.Abstractions": "libremetaverse-imaging", "LibreMetaverse.Imaging.Skia": "libremetaverse-imaging-skia", "LibreMetaverse.LslTools": "libremetaverse-lsl-tools", "LibreMetaverse.PrimMesher": "libremetaverse-prim-mesher", "LibreMetaverse.RLV": "libremetaverse-rlv", "LibreMetaverse.Rendering.MeshFoundry": "libremetaverse-rendering-mesh-foundry", "LibreMetaverse.Rendering.Simple": "libremetaverse-rendering-simple", "LibreMetaverse.StructuredData": "libremetaverse-structured-data", "LibreMetaverse.Types": "libremetaverse-types", "LibreMetaverse.Utilities": "libremetaverse-utilities", "LibreMetaverse.Voice.Vivox": "libremetaverse-voice-vivox", "LibreMetaverse.Voice.WebRTC": "libremetaverse-voice-webrtc", } PRIMITIVES = { "System.Boolean": "bool", "System.Byte": "u8", "System.SByte": "i8", "System.Int16": "i16", "System.UInt16": "u16", "System.Int32": "i32", "System.UInt32": "u32", "System.Int64": "i64", "System.UInt64": "u64", "System.IntPtr": "isize", "System.UIntPtr": "usize", "System.Single": "f32", "System.Double": "f64", "System.Void": "()", "System.String": "String", } STDLIB = { "System.Array": "libremetaverse_types::compat::Array", "System.ArraySegment": "libremetaverse_types::compat::ArraySegment", "System.Collections.BitArray": "Vec", "System.Collections.Generic.Dictionary": "std::collections::HashMap", "System.Collections.Generic.Dictionary.KeyCollection": "libremetaverse_types::compat::DictionaryKeys", "System.Collections.Generic.HashSet": "std::collections::HashSet", "System.Collections.Generic.ICollection": "Vec", "System.Collections.Generic.IEnumerable": "Box>", "System.Collections.Generic.IEnumerator": "Box>", "System.Collections.Generic.IList": "Vec", "System.Collections.Generic.IReadOnlyCollection": "Vec", "System.Collections.Generic.IReadOnlyDictionary": "std::collections::HashMap", "System.Collections.Generic.IReadOnlyList": "Vec", "System.Collections.Generic.KeyValuePair": "(K, V)", "System.Collections.Generic.LinkedList": "std::collections::LinkedList", "System.Collections.Generic.List": "Vec", "System.Collections.Generic.Queue": "std::collections::VecDeque", "System.Collections.Generic.SortedDictionary": "std::collections::BTreeMap", "System.Collections.Generic.Stack": "Vec", "System.Collections.ObjectModel.ReadOnlyCollection": "Vec", "System.Collections.Specialized.NameValueCollection": "std::collections::HashMap>", "System.Collections.Concurrent.ConcurrentDictionary": "std::sync::RwLock>", "System.Collections.Concurrent.ConcurrentQueue": "std::sync::Mutex>", "System.Collections.Concurrent.ConcurrentStack": "std::sync::Mutex>", "System.Action": "Box", "System.Comparison": "Box std::cmp::Ordering + Send + Sync>", "System.Converter": "Box TOutput + Send + Sync>", "System.Func": "Box Output + Send + Sync>", "System.Predicate": "Box bool + Send + Sync>", "System.AsyncCallback": "Box", "System.IAsyncResult": "Box", "System.ICloneable": "Clone", "System.IComparable": "Ord", "System.IComparable`1": "Ord", "System.IConvertible": "libremetaverse_types::compat::Convertible", "System.IDisposable": "libremetaverse_types::compat::Close", "System.IEquatable": "Eq", "System.IFormattable": "std::fmt::Display", "System.ISpanFormattable": "std::fmt::Display", "System.DateTime": "std::time::SystemTime", "System.DateTimeOffset": "std::time::SystemTime", "System.TimeSpan": "std::time::Duration", "System.Guid": "libremetaverse_types::compat::Guid", "System.Uri": "libremetaverse_types::compat::Uri", "System.Version": "libremetaverse_types::compat::Version", "System.Random": "libremetaverse_types::compat::RandomSource", "System.Exception": "libremetaverse_types::compat::ExternalError", "System.EventArgs": "()", "System.EventHandler": "libremetaverse_types::compat::EventHandler", "System.Object": "libremetaverse_types::compat::Object", "System.Type": "libremetaverse_types::compat::TypeId", "System.Char": "libremetaverse_types::compat::Utf16CodeUnit", "System.Decimal": "libremetaverse_types::compat::Decimal", "System.Enum": "libremetaverse_types::compat::EnumValue", "System.Attribute": "libremetaverse_types::compat::Attribute", "System.Delegate": "libremetaverse_types::compat::Callback", "System.MulticastDelegate": "libremetaverse_types::compat::Callback", "System.ValueType": "libremetaverse_types::compat::Value", "System.Text.Encoding": "libremetaverse_types::compat::TextEncoding", "System.Text.StringBuilder": "String", "System.Text.RegularExpressions.Regex": "libremetaverse_types::compat::Regex", "System.IO.Stream": "Box", "System.IO.MemoryStream": "std::io::Cursor>", "System.IO.TextReader": "Box", "System.IO.TextWriter": "Box", "System.IO.BinaryReader": "libremetaverse_types::compat::BinaryReader", "System.IO.BinaryWriter": "libremetaverse_types::compat::BinaryWriter", "System.IO.FileStream": "std::fs::File", "System.Net.IPAddress": "std::net::IpAddr", "System.Net.IPEndPoint": "std::net::SocketAddr", "System.Net.EndPoint": "std::net::SocketAddr", "System.Net.Http.HttpClient": "libremetaverse_types::compat::HttpClient", "System.Net.Http.HttpResponseMessage": "libremetaverse_types::compat::HttpResponse", "System.Net.Http.HttpMethod": "libremetaverse_types::compat::HttpMethod", "System.Net.Http.Headers.AuthenticationHeaderValue": "libremetaverse_types::compat::AuthenticationHeaderValue", "System.Net.Sockets.Socket": "libremetaverse::net::Socket", "System.Net.Sockets.TcpClient": "libremetaverse::net::TcpStream", "System.Net.Sockets.UdpClient": "libremetaverse::net::UdpSocket", "System.Threading.CancellationToken": "libremetaverse_types::compat::CancellationToken", "System.Threading.Tasks.Task": "libremetaverse_types::compat::Task", "System.Threading.Tasks.ValueTask": "libremetaverse_types::compat::Task", "System.Threading.RateLimiting.RateLimiter": "libremetaverse::net::RateLimiter", "System.Numerics.BigInteger": "libremetaverse_types::compat::BigInteger", "System.Numerics.Complex": "libremetaverse_types::compat::Complex64", "System.Tuple": "(T1, T2)", "System.ValueTuple": "(T1, T2)", "System.Xml.XmlNode": "libremetaverse_structured_data::xml::Node", "System.Xml.XmlElement": "libremetaverse_structured_data::xml::Element", "System.Xml.XmlReader": "libremetaverse_structured_data::xml::Reader", "System.Xml.XmlWriter": "libremetaverse_structured_data::xml::Writer", "System.Text.Json.JsonElement": "libremetaverse_voice_webrtc::signaling::JsonValue", } THIRD_PARTY_ASSEMBLY_TARGET = { "CoreJ2K": ("libremetaverse-imaging", "codec"), "MessagePack": ("libremetaverse", "message_pack"), "Microsoft.Extensions.DependencyInjection.Abstractions": ("libremetaverse", "services"), "Microsoft.Extensions.Logging.Abstractions": ("libremetaverse", "logging"), "SIPSorcery": ("libremetaverse-voice-webrtc", "transport"), "SIPSorceryMedia.Abstractions": ("libremetaverse-voice-webrtc", "media"), "SIPSorceryMedia.SoundFlow": ("libremetaverse-voice-webrtc", "audio"), "SkiaSharp": ("libremetaverse-imaging-skia", "backend"), "SoundFlow.Abstracts": ("libremetaverse-voice-webrtc", "audio"), "SoundFlow.Backends.MiniAudio": ("libremetaverse-voice-webrtc", "audio"), "SoundFlow.Extensions.WebRtc.Apm": ("libremetaverse-voice-webrtc", "audio"), } VALID_MEMBER_STATUSES = {"mapped", "intentional_difference"} VALID_TYPE_STATUSES = {"mapped"} SUPPORT_TYPES = { "LibreMetaverse.RLV.IBlacklistProvider": ("libremetaverse-rlv", "provider", "libremetaverse_rlv::provider::BlacklistProvider"), "LibreMetaverse.RLV.IRestrictionProvider": ("libremetaverse-rlv", "provider", "libremetaverse_rlv::provider::RestrictionProvider"), } MEMBER_RETURN_OVERRIDES = { "M:LibreMetaverse.Caps.Capabilities": "Vec", "M:LibreMetaverse.PrimMesher.PrimMesh.GetVertexIndexer": ( "Option" ), } MEMBER_TYPE_OVERRIDES = { "F:LibreMetaverse.Primitive.Flexible": "Option", "F:LibreMetaverse.Primitive.Light": "Option", "F:LibreMetaverse.Primitive.LightMap": "Option", "F:LibreMetaverse.Primitive.NameValues": "Option>", "F:LibreMetaverse.Primitive.TextureEntry.FaceTextures": ( "Vec>" ), "F:LibreMetaverse.Rendering.Face.Weights": ( "Option>" ), "F:LibreMetaverse.PrimMesher.VertexIndexer.viewerPolygons": ( "Vec>>" ), } MEMBER_SIGNATURE_OVERRIDES = { "M:LibreMetaverse.StructuredData.OSDArray.CopyTo(LibreMetaverse.StructuredData.OSD[],System.Int32)": ( "pub fn copy_to(&self, array: &mut [libremetaverse_structured_data::OSD], index: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.StructuredData.OSDMap.CopyTo(System.Collections.Generic.KeyValuePair{System.String,LibreMetaverse.StructuredData.OSD}[],System.Int32)": ( "pub fn copy_to(&self, array: &mut [(String, libremetaverse_structured_data::OSD)], index: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.CacheDictionary`2.CopyTo(System.Collections.Generic.KeyValuePair{`0,`1}[],System.Int32)": ( "pub fn copy_to(&self, array: &mut [(TKey, Option)], array_index: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.DoubleToBytes(System.Double,System.Byte[],System.Int32)": ( "pub fn double_to_bytes_with_double_bytes_int32(value: f64, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.ExpiringCache`2.CopyTo(System.Array,System.Int32)": ( "pub fn copy_to(&self, array: &mut [(TKey, Option)], start_index: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "P:LibreMetaverse.ExpiringCache`2.Item(`0)": ( "pub fn item(&self, key: TKey) -> Option", "shared_self,owned", ), "M:LibreMetaverse.Utils.EnumTryParse``1(System.String,``0@)": ( "pub fn enum_try_parse(str_type: String, result: &mut Option) -> Result", "owned,mutable_borrow", ), "M:LibreMetaverse.Utils.FloatToBytes(System.Single,System.Byte[],System.Int32)": ( "pub fn float_to_bytes_with_single_bytes_int32(value: f32, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.Int16ToBytes(System.Int16,System.Byte[],System.Int32)": ( "pub fn int16_to_bytes_with_int16_bytes_int32(value: i16, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.Int64ToBytes(System.Int64,System.Byte[],System.Int32)": ( "pub fn int64_to_bytes_with_int64_bytes_int32(value: i64, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.IntToBytes(System.Int32,System.Byte[],System.Int32)": ( "pub fn int_to_bytes_with_int32_bytes_int32(value: i32, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.ReadSingleLittleEndian(System.Byte[],System.Int32)": ( "pub fn read_single_little_endian(src: &[u8], pos: i32) -> Result", "shared_borrow,owned", ), "M:LibreMetaverse.Utils.WriteSingleLittleEndian(System.Byte[],System.Int32,System.Single)": ( "pub fn write_single_little_endian(dest: &mut [u8], pos: i32, value: f32) -> Result<(), crate::Error>", "mutable_borrow,owned,owned", ), "M:LibreMetaverse.Utils.ReadDoubleLittleEndian(System.Byte[],System.Int32)": ( "pub fn read_double_little_endian(src: &[u8], pos: i32) -> Result", "shared_borrow,owned", ), "M:LibreMetaverse.Utils.WriteDoubleLittleEndian(System.Byte[],System.Int32,System.Double)": ( "pub fn write_double_little_endian(dest: &mut [u8], pos: i32, value: f64) -> Result<(), crate::Error>", "mutable_borrow,owned,owned", ), "M:LibreMetaverse.Utils.UInt16ToBytes(System.UInt16,System.Byte[],System.Int32)": ( "pub fn u_int16_to_bytes_with_u_int16_bytes_int32(value: u16, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.UInt16ToBytesBig(System.UInt16,System.Byte[],System.Int32)": ( "pub fn u_int16_to_bytes_big(value: u16, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.UInt64ToBytes(System.UInt64,System.Byte[],System.Int32)": ( "pub fn u_int64_to_bytes_with_u_int64_bytes_int32(value: u64, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.UInt64ToBytesBig(System.UInt64,System.Byte[],System.Int32)": ( "pub fn u_int64_to_bytes_big_with_u_int64_bytes_int32(value: u64, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.UIntToBytes(System.UInt32,System.Byte[],System.Int32)": ( "pub fn u_int_to_bytes_with_u_int32_bytes_int32(value: u32, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.Utils.UIntToBytesBig(System.UInt32,System.Byte[],System.Int32)": ( "pub fn u_int_to_bytes_big(value: u32, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "owned,mutable_borrow,owned", ), "M:LibreMetaverse.UUID.ToBytes(System.Byte[],System.Int32)": ( "pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Color4.ToBytes(System.Byte[],System.Int32)": ( "pub fn to_bytes_with_bytes_int32(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Color4.ToBytes(System.Byte[],System.Int32,System.Boolean)": ( "pub fn to_bytes_with_bytes_int32_boolean(&self, dest: &mut [u8], pos: i32, inverted: bool) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned,owned", ), "M:LibreMetaverse.Color4.ToFloatBytes(System.Byte[],System.Int32)": ( "pub fn to_float_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Quaternion.ToBytes(System.Byte[],System.Int32)": ( "pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Vector2.ToBytes(System.Byte[],System.Int32)": ( "pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Vector3.ToBytes(System.Byte[],System.Int32)": ( "pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Vector3d.ToBytes(System.Byte[],System.Int32)": ( "pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.Vector4.ToBytes(System.Byte[],System.Int32)": ( "pub fn to_bytes(&self, dest: &mut [u8], pos: i32) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned", ), "M:LibreMetaverse.AppearanceManager.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) " "-> Result", "optional_shared", ), "M:LibreMetaverse.AgentManager.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) " "-> Result", "optional_shared", ), "M:LibreMetaverse.AvatarManager.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) " "-> Result", "optional_shared", ), "M:LibreMetaverse.GridManager.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) " "-> Result", "optional_shared", ), "M:LibreMetaverse.Inventory.#ctor(LibreMetaverse.GridClient)": ( "pub fn new_with_grid_client(client: std::sync::Arc) " "-> Result", "shared", ), "M:LibreMetaverse.Inventory.#ctor(LibreMetaverse.GridClient,LibreMetaverse.UUID)": ( "pub fn new_with_grid_client_uuid(client: std::sync::Arc, " "owner: libremetaverse_types::UUID) -> Result", "shared,owned", ), "M:LibreMetaverse.Inventory.Contains(LibreMetaverse.InventoryBase)": ( "pub fn contains_with_inventory_base(&self, obj: &dyn " "libremetaverse::InventoryObjectClass) -> Result", "shared_self,shared_borrow", ), "M:LibreMetaverse.Inventory.GetContents(LibreMetaverse.InventoryFolder)": ( "pub fn get_contents_with_inventory_folder(&self, folder: &libremetaverse::InventoryFolder) " "-> Result>, crate::Error>", "shared_self,shared_borrow", ), "M:LibreMetaverse.Inventory.GetContents(LibreMetaverse.UUID)": ( "pub fn get_contents_with_uuid(&self, folder: libremetaverse_types::UUID) " "-> Result>, crate::Error>", "shared_self,owned", ), "M:LibreMetaverse.Inventory.RemoveNodeFor(LibreMetaverse.InventoryBase)": ( "pub fn remove_node_for(&self, item: &dyn libremetaverse::InventoryObjectClass) " "-> Result<(), crate::Error>", "shared_self,shared_borrow", ), "M:LibreMetaverse.Inventory.TryGetValue(LibreMetaverse.UUID,LibreMetaverse.InventoryBase@)": ( "pub fn try_get_value_with_uuid_inventory_base(&self, uuid: libremetaverse_types::UUID, " "item: &mut Option>) " "-> Result", "shared_self,owned,mutable_output", ), "M:LibreMetaverse.Inventory.UpdateNodeFor(LibreMetaverse.InventoryBase)": ( "pub fn update_node_for(&self, item: &dyn libremetaverse::InventoryObjectClass) " "-> Result<(), crate::Error>", "shared_self,shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetAllListingFolderIds(LibreMetaverse.Inventory)": ( "pub fn get_all_listing_folder_ids(inventory: &libremetaverse::Inventory) " "-> Result, crate::Error>", "shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetListingFolder(LibreMetaverse.UUID,LibreMetaverse.Inventory)": ( "pub fn get_listing_folder(folder_id: libremetaverse_types::UUID, " "inventory: &libremetaverse::Inventory) -> Result", "owned,shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetListingsRoot(LibreMetaverse.Inventory)": ( "pub fn get_listings_root(inventory: &libremetaverse::Inventory) " "-> Result", "shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetRole(LibreMetaverse.UUID,LibreMetaverse.Inventory)": ( "pub fn get_role(folder_id: libremetaverse_types::UUID, inventory: &libremetaverse::Inventory) " "-> Result", "owned,shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetStockCount(LibreMetaverse.UUID,LibreMetaverse.Inventory)": ( "pub fn get_stock_count(version_folder_uuid: libremetaverse_types::UUID, " "inventory: &libremetaverse::Inventory) -> Result", "owned,shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetVersionFolder(LibreMetaverse.UUID,LibreMetaverse.Inventory)": ( "pub fn get_version_folder(listing_folder_uuid: libremetaverse_types::UUID, " "inventory: &libremetaverse::Inventory) -> Result", "owned,shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.IsMarketplaceFolder(LibreMetaverse.UUID,LibreMetaverse.Inventory)": ( "pub fn is_marketplace_folder(folder_id: libremetaverse_types::UUID, " "inventory: &libremetaverse::Inventory) -> Result", "owned,shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.ValidateListing(LibreMetaverse.UUID,LibreMetaverse.Inventory)": ( "pub fn validate_listing(listing_folder_uuid: libremetaverse_types::UUID, " "inventory: &libremetaverse::Inventory) " "-> Result", "owned,shared_borrow", ), "M:LibreMetaverse.Marketplace.MarketplaceErrorEventArgs.#ctor(System.String,System.Exception)": ( "pub fn new(message: String, ex: Option>) " "-> Result", "owned,optional_shared", ), "M:LibreMetaverse.Marketplace.MarketplaceListingChangedEventArgs.#ctor(System.Int32,LibreMetaverse.Marketplace.MarketplaceListing)": ( "pub fn new(listing_id: i32, listing: Option>) " "-> Result", "owned,optional_shared", ), "M:LibreMetaverse.InventoryAISClient.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) " "-> Result", "optional_shared", ), "M:LibreMetaverse.InventoryAISClient.ParseEmbedded(LibreMetaverse.StructuredData.OSDMap,System.Collections.Generic.List{LibreMetaverse.InventoryFolder}@,System.Collections.Generic.List{LibreMetaverse.InventoryItem}@,System.Collections.Generic.List{LibreMetaverse.InventoryItem}@)": ( "pub fn parse_embedded(&self, response: libremetaverse_structured_data::OSD, " "folders: &mut Option>, items: &mut " "Option>, links: &mut " "Option>) " "-> Result<(), crate::Error>", "shared_self,owned,mutable_output,mutable_output,mutable_output", ), "M:LibreMetaverse.InventoryAISClient.ParseItemsFromEmbedded(LibreMetaverse.StructuredData.OSDMap)": ( "pub fn parse_items_from_embedded(&self, response: " "Option) -> Result, " "crate::Error>", "shared_self,optional_owned", ), "M:LibreMetaverse.InventoryAISClient.ParseLinksFromEmbedded(LibreMetaverse.StructuredData.OSDMap)": ( "pub fn parse_links_from_embedded(&self, response: " "Option) -> Result, " "crate::Error>", "shared_self,optional_owned", ), "M:LibreMetaverse.InventoryManager.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) " "-> Result", "optional_shared", ), "M:LibreMetaverse.ObjectManager.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) " "-> Result", "optional_shared", ), "M:LibreMetaverse.Primitive.TextureEntry.CreateFace(System.UInt32)": ( "pub fn create_face(&mut self, index: u32) " "-> Result<&mut libremetaverse::PrimitiveTextureEntryFace, crate::Error>", "mutable_self,owned", ), "M:LibreMetaverse.Primitive.TextureEntry.GetFace(System.UInt32)": ( "pub fn get_face(&self, index: u32) " "-> Result, crate::Error>", "shared_self,owned", ), "M:LibreMetaverse.SlurlParser.#ctor(System.String)": ( "pub fn new(location: Option) -> Result", "optional_owned", ), "M:LibreMetaverse.InventoryManager.CreateInventoryItem(LibreMetaverse.InventoryType,LibreMetaverse.UUID)": ( "pub fn create_inventory_item(type_: libremetaverse_types::InventoryType, id: " "libremetaverse_types::UUID) -> Result, crate::Error>", "owned,owned", ), "M:LibreMetaverse.InventoryManager.FolderContentsAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.Boolean,System.Boolean,LibreMetaverse.InventorySortOrder,System.Threading.CancellationToken,System.Boolean)": ( "pub async fn folder_contents(&self, folder: libremetaverse_types::UUID, owner: " "libremetaverse_types::UUID, fetch_folders: bool, fetch_items: bool, order: " "libremetaverse::InventorySortOrder, cancellation_token: " "Option, follow_links: Option) " "-> Result>, crate::Error>", "shared_self,owned,owned,owned,owned,owned,optional_owned,optional_owned", ), "M:LibreMetaverse.InventoryManager.GetTaskInventoryAsync(LibreMetaverse.UUID,System.UInt32,LibreMetaverse.Simulator,System.Threading.CancellationToken)": ( "pub async fn get_task_inventory(&self, object_id: libremetaverse_types::UUID, " "object_local_id: u32, simulator: Option, cancellation_token: " "Option) -> Result>, crate::Error>", "shared_self,owned,owned,optional_owned,optional_owned", ), "M:LibreMetaverse.InventoryManager.ItemCRC(LibreMetaverse.InventoryItem)": ( "pub fn item_crc(iitem: &libremetaverse::InventoryItem) -> Result", "shared_borrow", ), "M:LibreMetaverse.InventoryNode.#ctor(LibreMetaverse.InventoryBase)": ( "pub fn new_with_inventory_base(data: &dyn libremetaverse::InventoryObjectClass) " "-> Result", "shared_borrow", ), "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)": ( "pub fn new(sim: Option, avatar_id: libremetaverse_types::UUID, " "is_trial: bool, default_texture: libremetaverse::PrimitiveTextureEntryFace, face_textures: " "Vec, visual_params: Vec, appearance_version: " "u8, cof_version: i32, appearance_flags: libremetaverse::AppearanceFlags, child_count: i32) " "-> Result", "optional_owned,owned,owned,owned,owned,owned,owned,owned,owned,owned", ), "M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)": ( "pub fn add_policy(&self, policy_to_add: Option>) -> Result<&Self, crate::Error>", "shared_self,optional_shared", ), "M:LibreMetaverse.Appearance.CurrentOutfitFolder.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option>) -> Result", "optional_shared", ), "M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)": ( "pub fn can_attach(&self, item: Option>) " "-> Result", "shared_self,optional_shared", ), "M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)": ( "pub fn can_detach(&self, item: Option>) " "-> Result", "shared_self,optional_shared", ), "M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)": ( "pub fn remove_policy(&self, policy_to_remove: Option>) -> Result<(), crate::Error>", "shared_self,optional_shared", ), "M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)": ( "pub async fn report_item_change(&self, added_items: Option>>, " "removed_items: Option>>, cancellation_token: " "Option) -> Result<(), crate::Error>", "shared_self,optional_shared,optional_shared,optional_owned", ), "M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)": ( "pub fn add_policy(&self, policy_to_add: std::sync::Arc) -> Result, crate::Error>", "shared_self,shared", ), "M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)": ( "pub fn remove_policy(&self, policy_to_remove: std::sync::Arc) -> Result<(), crate::Error>", "shared_self,shared", ), "M:LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem)": ( "pub fn resolve_inventory_link(&self, item_link: std::sync::Arc) " "-> Result>, crate::Error>", "shared_self,shared", ), "M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)": ( "pub fn can_attach(&self, item: Option>) " "-> Result", "shared_self,optional_shared", ), "M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)": ( "pub fn can_detach(&self, item: Option>) " "-> Result", "shared_self,optional_shared", ), "M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)": ( "pub fn report_item_change(&self, added_items: Option>>, " "removed_items: Option>>, cancellation_token: " "Option) -> std::pin::Pin> + Send + '_>>", "shared_self,optional_shared,optional_shared,optional_owned", ), "M:LibreMetaverse.InventoryManager.CreateLinksAsync(LibreMetaverse.UUID,System.Collections.Generic.IEnumerable{System.ValueTuple{LibreMetaverse.InventoryBase,System.String}},System.Action{System.Boolean},System.Threading.CancellationToken)": ( "pub async fn create_links(&self, folder_id: libremetaverse_types::UUID, links_to_create: " "Box>, callback: Option>, cancellation_token: Option) " "-> Result<(), crate::Error>", "shared_self,owned,owned,optional_owned,optional_owned", ), "M:LibreMetaverse.Animesh.AnimationTrack.EvaluatePose(System.Collections.Generic.Dictionary{System.String,LibreMetaverse.Animesh.JointPose})": ( "pub fn evaluate_pose(&self, pose: &mut std::collections::HashMap) -> Result<(), crate::Error>", "shared_self,mutable_borrow", ), "M:LibreMetaverse.AnimeshSkinning.ComputeSkinningMatrices(System.Collections.Generic.Dictionary{System.String,LibreMetaverse.Animesh.JointPose},LibreMetaverse.Rendering.LindenSkeleton,LibreMetaverse.Rendering.MeshSkinData)": ( "pub fn compute_skinning_matrices(pose: Option>, skeleton: Option, " "skin_data: Option) " "-> Result, crate::Error>", "optional_owned,optional_owned,optional_owned", ), "M:LibreMetaverse.AnimeshSkinning.DeformVertices(LibreMetaverse.Rendering.Face,LibreMetaverse.Matrix4[],LibreMetaverse.Matrix4,System.Span{LibreMetaverse.Vector3},System.Span{LibreMetaverse.Vector3})": ( "pub fn deform_vertices(face: libremetaverse::rendering::Face, skinning_matrices: " "Vec, bind_shape_matrix: libremetaverse_types::Matrix4, " "out_positions: &mut [libremetaverse_types::Vector3], out_normals: " "Option<&mut [libremetaverse_types::Vector3]>) -> Result<(), crate::Error>", "owned,owned,owned,mutable_borrow,optional_mutable_borrow", ), "M:LibreMetaverse.Rendering.LindenSkeleton.BuildJointDictionary": ( "pub fn build_joint_dictionary(&self) -> Result>, crate::Error>", "shared_self", ), "M:LibreMetaverse.Rendering.LindenSkeleton.GetAllJoints": ( "pub fn get_all_joints(&self) -> Result>>, crate::Error>", "shared_self", ), "M:LibreMetaverse.Rendering.LindenSkeleton.GetBone(System.String)": ( "pub fn get_bone(&self, name_or_alias: String) -> Result>, crate::Error>", "shared_self,owned", ), "M:LibreMetaverse.AssetManager.#ctor(LibreMetaverse.GridClient)": ( "pub fn new(client: Option) -> Result", "optional_owned", ), "M:LibreMetaverse.Helpers.ZeroDecode(System.Byte[],System.Int32,System.Byte[])": ( "pub fn zero_decode(src: Option<&[u8]>, srclen: i32, dest: Option<&mut [u8]>) " "-> Result", "shared_borrow,owned,mutable_borrow", ), "M:LibreMetaverse.Helpers.ZeroEncode(System.Byte[],System.Int32,System.Byte[])": ( "pub fn zero_encode(src: Option<&[u8]>, srclen: i32, dest: Option<&mut [u8]>) " "-> Result", "shared_borrow,owned,mutable_borrow", ), "M:LibreMetaverse.Packets.InventoryDescendentsPacket.FromBytes(System.Byte[],System.Int32@,System.Int32@,System.Byte[])": ( "pub fn from_bytes_with_bytes_int32_int32_bytes(&mut self, bytes: Vec, " "i: &mut i32, packet_end: &mut i32, zero_buffer: Option<&mut [u8]>) " "-> Result<(), crate::Error>", "mutable_self,owned,mutable_borrow,mutable_borrow,optional_mutable_borrow", ), "M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken)": ( "pub async fn get_terrain_material_overrides(&self, cancellation_token: " "Option) " "-> Result>>, crate::Error>", "shared_self,optional_owned", ), "M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken)": ( "pub async fn set_terrain_material_overrides(&self, overrides: " "Vec>, cancellation_token: " "Option) -> Result", "shared_self,owned,optional_owned", ), "M:LibreMetaverse.Rendering.IRendering.TransformTexCoords(System.Collections.Generic.List{LibreMetaverse.Rendering.Vertex},LibreMetaverse.Vector3,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Vector3)": ( "pub fn transform_tex_coords(&self, vertices: &mut Vec, " "center: libremetaverse_types::Vector3, te_face: libremetaverse::PrimitiveTextureEntryFace, " "prim_scale: libremetaverse_types::Vector3) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned,owned,owned", ), "M:LibreMetaverse.Rendering.MeshFoundry.TerrainMesh(System.Single[0:,0:],System.Single,System.Single,System.Single,System.Single)": ( "pub fn terrain_mesh(&self, z_map: Vec>, x_begin: f32, x_end: f32, " "y_begin: f32, y_end: f32) -> Result", "shared_self,owned,owned,owned,owned,owned", ), "M:LibreMetaverse.Rendering.MeshFoundry.TransformTexCoords(System.Collections.Generic.List{LibreMetaverse.Rendering.Vertex},LibreMetaverse.Vector3,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Vector3)": ( "pub fn transform_tex_coords(&self, vertices: &mut Vec, " "center: libremetaverse_types::Vector3, te_face: libremetaverse::PrimitiveTextureEntryFace, " "prim_scale: libremetaverse_types::Vector3) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned,owned,owned", ), "M:LibreMetaverse.Rendering.SimpleRenderer.TransformTexCoords(System.Collections.Generic.List{LibreMetaverse.Rendering.Vertex},LibreMetaverse.Vector3,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Vector3)": ( "pub fn transform_tex_coords(&self, vertices: &mut Vec, " "center: libremetaverse_types::Vector3, te_face: libremetaverse::PrimitiveTextureEntryFace, " "prim_scale: libremetaverse_types::Vector3) -> Result<(), crate::Error>", "shared_self,mutable_borrow,owned,owned,owned", ), } PROPERTY_SIGNATURE_OVERRIDES = { "P:LibreMetaverse.Marketplace.MarketplaceErrorEventArgs.Exception": ( "pub fn exception(&self) -> Option>", "shared_self", ), "P:LibreMetaverse.Marketplace.MarketplaceListingChangedEventArgs.Listing": ( "pub fn listing(&self) -> Option>", "shared_self", ), "P:LibreMetaverse.InventoryNode.Data": ( "pub fn data(&self) -> Option> ; " "pub fn set_data(&mut self, value: Option>)", "shared_or_mutable_self", ), "P:LibreMetaverse.AppearanceManager.TextureProvider": ( "pub fn texture_provider(&self) -> std::sync::Arc ; " "pub fn set_texture_provider(&mut self, value: std::sync::Arc)", "shared_or_mutable_self", ), "P:LibreMetaverse.Rendering.Joint.bone": ( "pub fn bone(&self) -> Option> ; " "pub fn set_bone(&mut self, value: Option>)", "shared_or_mutable_self", ), "P:LibreMetaverse.GridClient.Self": ( "pub fn self_(&mut self) -> &mut libremetaverse::AgentManager", "mutable_self", ), "P:LibreMetaverse.GridClient.Settings": ( "pub fn settings(&mut self) -> &mut libremetaverse::Settings", "mutable_self", ), "P:LibreMetaverse.Settings.Timing": ( "pub fn timing(&mut self) -> &mut libremetaverse::TimingSettings", "mutable_self", ), } TYPE_USAGE_OVERRIDES = { "LibreMetaverse.DictionaryChangeCallback": ( "std::sync::Arc" ), } TYPE_NAME_OVERRIDES = { "T:LibreMetaverse.LslTools.Error": "LslError", } 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", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe", "use", "where", "while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized", "virtual", "yield", "try", } FAKE_BODY = re.compile( r"\b(?:Default::default|unwrap_or_default|Vec::new|String::new|UUID::nil|Uuid::nil|" r"(?:std::iter|stream)::empty)\s*\(|\b(?:return\s+)?(?:false|true|0)\s*;|" r"\b(?:Ok|Some)\(\s*(?:false|true|0|None|Vec::new\(\)|String::new\(\))\s*\)" ) def snake(name: str) -> str: name = name.lstrip("@").replace("#", "_") name = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", name) name = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name).lower() name = re.sub(r"[^a-z0-9_]", "_", name).strip("_") or "item" if name[0].isdigit() or keyword.iskeyword(name) or name in RUST_KEYWORDS: name += "_" return name def pascal(name: str) -> str: parts = re.split(r"[^A-Za-z0-9]+", name.replace("`", "_")) value = "_".join(part[:1].upper() + part[1:] for part in parts if part) or "Type" return value + "Type" if value in RUST_KEYWORDS else value def split_top_level(value: str) -> list[str]: depth = 0 start = 0 parts = [] for index, char in enumerate(value): depth += char == "<" depth -= char == ">" if char == "," and depth == 0: parts.append(value[start:index].strip()) start = index + 1 parts.append(value[start:].strip()) return [part for part in parts if part] def type_parts(value: str) -> tuple[str, list[str], str]: value = value.strip() suffix = "" while value.endswith("&") or value.endswith("*"): suffix = value[-1] + suffix value = value[:-1] while array := re.search(r"(\[[,]*\])$", value): suffix = array.group(1) + suffix value = value[: array.start()] first = value.find("<") if first < 0: return value, [], suffix depth = 0 close = -1 for index in range(first, len(value)): depth += value[index] == "<" depth -= value[index] == ">" if depth == 0: close = index break if close < 0: raise ValueError(f"unbalanced generic type: {value}") base = value[:first] + value[close + 1 :] return base, split_top_level(value[first + 1 : close]), suffix def substitute(template: str, arguments: list[str]) -> str: names = re.findall(r"\b(?:T\d*|TKey|TValue|TInput|TOutput|Args|Output|K|V|R|W)\b", template) for name, argument in zip(dict.fromkeys(names), arguments): template = re.sub(rf"\b{re.escape(name)}\b", argument, template) return template def nullable(value: str, nullability: dict | None) -> str: if nullability and (nullability.get("read") == "nullable" or nullability.get("write") == "nullable"): return f"Option<{value}>" return value def assembly_module_prefix(assembly: str, namespace: str) -> list[str]: roots = { "LibreMetaverse": "LibreMetaverse", "LibreMetaverse.Types": "LibreMetaverse", "LibreMetaverse.StructuredData": "LibreMetaverse.StructuredData", "LibreMetaverse.Imaging.Abstractions": "LibreMetaverse.Imaging", "LibreMetaverse.Imaging.Skia": "LibreMetaverse.Imaging.Skia", "LibreMetaverse.PrimMesher": "LibreMetaverse.PrimMesher", "LibreMetaverse.Rendering.Simple": "LibreMetaverse.Rendering", "LibreMetaverse.Rendering.MeshFoundry": "LibreMetaverse.Rendering", "LibreMetaverse.LslTools": "LibreMetaverse.LslTools", "LibreMetaverse.RLV": "LibreMetaverse.RLV", "LibreMetaverse.Utilities": "LibreMetaverse.Utilities", "LibreMetaverse.Voice.Vivox": "LibreMetaverse.Voice.Vivox", "LibreMetaverse.Voice.WebRTC": "LibreMetaverse.Voice.WebRTC", } root = roots[assembly] remainder = namespace[len(root) :].lstrip(".") if namespace.startswith(root) else namespace return [snake(part) for part in remainder.split(".") if part] def declared_type_path(assembly: str, item: dict) -> tuple[str, str, str]: crate = ASSEMBLY_CRATES[assembly] modules = assembly_module_prefix(assembly, item["namespace"]) base, _, _ = type_parts(item["signature"]) tail = base[len(item["namespace"]) :].lstrip(".") # Flatten nested type ownership into the type name; the containing type keeps paths unique. type_name = TYPE_NAME_OVERRIDES.get( item["doc_id"], "".join(pascal(part) for part in tail.split(".")) ) module = "::".join(modules) path = "::".join([crate.replace("-", "_"), *modules, type_name]) return crate, module, path def generic_bounds_text(item: dict) -> str: generics = {parameter["name"] for parameter in item.get("generic_parameters", [])} mapper = Mapper({}) rendered = [] 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") rendered.append(parameter["name"] + ((": " + " + ".join(bounds)) if bounds else "")) return ", ".join(rendered) or "none" def enum_decision(item: dict) -> str: if item["kind"] != "enum": return "not_applicable" if any(attribute["type"] == "System.FlagsAttribute" for attribute in item.get("attributes", [])): return f"bitflags<{item.get('enum_underlying_type', 'unknown')}>; retain unknown bits" return f"repr<{item.get('enum_underlying_type', 'unknown')}>; preserve discriminants; Unknown(raw) only when extensible" def external_type_target(item: dict) -> tuple[str, str, str, str]: base, arguments, _ = type_parts(item["signature"]) if base in PRIMITIVES: return "core", "primitive", PRIMITIVES[base], "language" if base == "System.Action": return "core", "callback", f"Box", "language" if base == "System.Func": output = arguments[-1] if arguments else "()" return "core", "callback", f"Box {output} + Send + Sync>", "language" if base == "System.EventHandler": return "libremetaverse-types", "compat", f"libremetaverse_types::compat::EventHandler<{arguments[-1] if arguments else '()'}>", "native_cross_platform_replacement" if base in {"System.Tuple", "System.ValueTuple"}: return "core", "tuple", "(" + ", ".join(arguments) + ("," if len(arguments) == 1 else "") + ")", "language" if base == "System.Nullable" and arguments: return "core", "option", f"Option<{arguments[0]}>", "language" if base in {"System.Threading.Tasks.Task", "System.Threading.Tasks.ValueTask"}: return "libremetaverse-types", "compat", f"libremetaverse_types::compat::Task<{arguments[0] if arguments else '()'}>", "native_cross_platform_replacement" if base in STDLIB: target = substitute(STDLIB[base], arguments) if arguments and "<" not in target and target not in {"Ord", "Eq", "Clone", "std::fmt::Display"}: target += "<" + ", ".join(arguments) + ">" crate = target.split("::", 1)[0].replace("_", "-") if "::" in target else "std" return crate, "", target, "stdlib_or_adopted_crate" assembly = item["assembly"]["name"] if assembly in THIRD_PARTY_ASSEMBLY_TARGET: crate, module = THIRD_PARTY_ASSEMBLY_TARGET[assembly] target = f"{crate.replace('-', '_')}::{module}::{pascal(base.rsplit('.', 1)[-1])}" if arguments: target += "<" + ", ".join(arguments) + ">" return crate, module, target, "native_cross_platform_replacement" # Remaining BCL concepts get explicit project-owned compatibility types. if assembly.startswith("System.") or assembly in {"System.Private.CoreLib", "System"}: target = f"libremetaverse_types::compat::{pascal(base.rsplit('.', 1)[-1])}" if arguments: target += "<" + ", ".join(arguments) + ">" return "libremetaverse-types", "compat", target, "native_cross_platform_replacement" raise ValueError(f"no reviewed external type rule for {item['doc_id']} from {assembly}") def render_tsv(fieldnames: list[str], rows: list[dict[str, str]]) -> str: output = io.StringIO(newline="") writer = csv.DictWriter(output, fieldnames=fieldnames, delimiter="\t", lineterminator="\n") writer.writeheader() writer.writerows(rows) return output.getvalue() def function_bodies(text: str) -> list[str]: bodies = [] function = re.compile(r"(?m)^[ \t]*(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+[A-Za-z_][A-Za-z0-9_]*") for match in function.finditer(text): brace = text.find("{", match.end()) semicolon = text.find(";", match.end()) if brace < 0 or (semicolon >= 0 and semicolon < brace): continue depth = 0 quote = None escaped = False index = brace while index < len(text): char = text[index] if quote: if escaped: escaped = False elif char == "\\": escaped = True elif char == quote: quote = None elif char == '"': quote = char elif text.startswith("//", index): newline = text.find("\n", index) index = len(text) if newline < 0 else newline elif text.startswith("/*", index): end = text.find("*/", index + 2) index = len(text) if end < 0 else end + 1 elif char == "{": depth += 1 elif char == "}": depth -= 1 if depth == 0: bodies.append(text[brace + 1 : index]) break index += 1 else: raise ValueError("unbalanced generated Rust function body") return bodies def validate_generated_shims() -> None: for path in sorted((ROOT / "crates").glob("*/src/generated*.rs")): text = path.read_text() default_types = set( re.findall( r"#\[derive\([^]]*\bDefault\b[^]]*\)\]\s*pub struct ([A-Za-z_][A-Za-z0-9_]*)", text, ) ) if default_types - {"UUID"}: raise ValueError(f"generated shim derives a plausible Default: {path.relative_to(ROOT)}") for body in function_bodies(text): if FAKE_BODY.search(body): raise ValueError(f"generated shim returns a plausible fallback: {path.relative_to(ROOT)}") if not any( marker in body for marker in ( "unimplemented_api!", "not_implemented(", "NotImplemented::new(", "crate::byte_order::", "crate::attention_catalog::", "crate::binary::", "crate::dispatch::", "crate::foliage_catalog::", "crate::genepool_catalog::", "crate::json_codec::", "crate::message_codec::", "crate::notation::", "crate::packet_catalog::", "crate::packet_wire::", "crate::protobuf::", "crate::skeleton::", "crate::visual_catalog::", "crate::xml_codec::", "stored_visual_params", ) ): raise ValueError(f"generated shim function does not use the standardized failure: {path.relative_to(ROOT)}") def build_type_rows(catalog: dict) -> tuple[list[dict[str, str]], dict[str, str]]: rows = [] resolved: dict[str, str] = {} for assembly in catalog["assemblies"]: assembly_name = assembly["identity"]["name"] for item in assembly["types"]: crate, module, path = declared_type_path(assembly_name, item) base, _, _ = type_parts(item["signature"]) if base in resolved and resolved[base] != path: raise ValueError(f"ambiguous catalog type signature: {base}") resolved[base] = path rows.append({ "csharp_type_id": item["doc_id"], "csharp_signature": item["signature"], "source_assembly": assembly_name, "source_kind": item["kind"], "rust_crate": crate, "rust_module": module, "rust_path": path, "mapping_decision": "native_metacrate_type", "generic_bounds": generic_bounds_text(item), "inheritance_mapping": "traits_and_composition: " + ", ".join(filter(None, [item.get("base_type"), *item.get("interfaces", [])])) if item.get("base_type") or item.get("interfaces") else "none", "enum_mapping": enum_decision(item), "status": "mapped", "notes": "nested types are flattened" if "." in type_parts(item["signature"])[0][len(item["namespace"]) + 1 :] else "none", }) for item in catalog["external_types"]: crate, module, target, decision = external_type_target(item) base, _, _ = type_parts(item["signature"]) if base in PRIMITIVES: resolved[base] = PRIMITIVES[base] elif base in STDLIB: resolved[base] = STDLIB[base] elif item["assembly"]["name"] in THIRD_PARTY_ASSEMBLY_TARGET: resolved[base] = f"{crate.replace('-', '_')}::{module}::{pascal(base.rsplit('.', 1)[-1])}" else: resolved.setdefault(base, target.split("<", 1)[0]) rows.append({ "csharp_type_id": item["doc_id"], "csharp_signature": item["signature"], "source_assembly": item["assembly"]["name"], "source_kind": item["kind"], "rust_crate": crate, "rust_module": module, "rust_path": target, "mapping_decision": decision, "status": "mapped", "generic_bounds": "not_applicable", "inheritance_mapping": "not_applicable", "enum_mapping": "not_applicable", "notes": "external member surface is not copied", }) for signature, (crate, module, target) in SUPPORT_TYPES.items(): resolved[signature] = target rows.append({ "csharp_type_id": f"T:{signature}", "csharp_signature": signature, "source_assembly": "LibreMetaverse.RLV", "source_kind": "referenced_support_trait", "rust_crate": crate, "rust_module": module, "rust_path": target, "mapping_decision": "native_metacrate_support_trait", "status": "mapped", "generic_bounds": "none", "inheritance_mapping": "native_trait", "enum_mapping": "not_applicable", "notes": "referenced by a public base/interface signature but absent from the public type table", }) rows.sort(key=lambda row: row["csharp_type_id"]) return rows, resolved class Mapper: def __init__(self, resolved: dict[str, str], interfaces: set[str] | None = None): self.resolved = resolved self.interfaces = interfaces or set() def type(self, value: str, nullability: dict | None = None, generics: set[str] | None = None) -> str: base, arguments, suffix = type_parts(value) generics = generics or set() child_nullability = (nullability or {}).get("generic_arguments", []) mapped_arguments = [ self.type(argument, child_nullability[index] if index < len(child_nullability) else None, generics) for index, argument in enumerate(arguments) ] if base == "System.Action": parameters = ", ".join(mapped_arguments) target = f"Box" elif base == "System.Func": output = mapped_arguments[-1] if mapped_arguments else "()" parameters = ", ".join(mapped_arguments[:-1]) target = f"Box {output} + Send + Sync>" elif base == "System.EventHandler": target = f"libremetaverse_types::compat::EventHandler<{mapped_arguments[-1] if mapped_arguments else '()'}>" elif base in {"System.Tuple", "System.ValueTuple"}: target = "(" + ", ".join(mapped_arguments) + ("," if len(mapped_arguments) == 1 else "") + ")" elif base == "System.Nullable" and mapped_arguments: target = f"Option<{mapped_arguments[0]}>" elif base in {"System.Threading.Tasks.Task", "System.Threading.Tasks.ValueTask"}: target = f"libremetaverse_types::compat::Task<{mapped_arguments[0] if mapped_arguments else '()'}>" else: target = None if base in generics or ("." not in base and base.startswith("T")): target = base elif target is None: target = TYPE_USAGE_OVERRIDES.get(base, self.resolved.get(base)) if target is None: raise ValueError(f"unresolved referenced type: {value} (base {base})") if mapped_arguments and base not in { "System.Action", "System.Func", "System.EventHandler", "System.Nullable", "System.Tuple", "System.ValueTuple", "System.Threading.Tasks.Task", "System.Threading.Tasks.ValueTask", }: target = substitute(target, mapped_arguments) if "<" not in target and not target.startswith("(") and target not in {"Ord", "Eq", "Clone", "std::fmt::Display"}: target += "<" + ", ".join(mapped_arguments) + ">" if base == "System.IDisposable": target = f"Box" elif base in self.interfaces or base == "System.IProgress": target = f"Box" for _ in re.findall(r"\[[,]*\]", suffix): target = f"Vec<{target}>" if "*" in suffix: target = f"libremetaverse_types::compat::Pointer<{target}>" return nullable(target, nullability) def constraint(self, value: str, generics: set[str]) -> str: base, arguments, _ = type_parts(value) if base == "System.IDisposable": return "libremetaverse_types::compat::Close" if base == "System.Collections.Generic.ICollection" and arguments: return f"libremetaverse_types::compat::Collection<{self.type(arguments[0], None, generics)}>" raise ValueError(f"no Rust generic-bound rule for {value}") def generic_names(item: dict, owner: dict) -> set[str]: names = set() for source in (owner.get("generic_parameters", []), item.get("generic_parameters", [])): for parameter in source: names.add(parameter["name"] if isinstance(parameter, dict) else str(parameter)) return names def method_receiver(owner: dict, item: dict) -> tuple[str, str]: if item.get("static") or item["kind"] == "constructor": return "", "none" if owner["doc_id"] == "T:LibreMetaverse.CRC32" and item["name"] == "Update": return "&mut self", "mutable_self" if item["name"] == "Deserialize": return "&mut self", "mutable_self" if owner["doc_id"] == "T:LibreMetaverse.Assets.AssetMaterial" and item["name"].startswith( ("Apply", "Decode", "Encode", "Set") ): return "&mut self", "mutable_self" if owner["doc_id"] == "T:LibreMetaverse.Imaging.ManagedImage" and item["name"] in { "Clear", "ConvertChannels", "ResizeBilinear", "ResizeNearestNeighbor", }: return "&mut self", "mutable_self" if owner["doc_id"] == "T:LibreMetaverse.Animesh.AnimationTrack" and item["name"] == "Advance": return "&mut self", "mutable_self" if owner["doc_id"] == "T:LibreMetaverse.Animesh.AnimeshPlayer" and item["name"] == "Update": return "&mut self", "mutable_self" if owner["doc_id"] == "T:LibreMetaverse.AppearanceManager" and item["name"] == "UpdateLastReceivedCOFVersion": return "&mut self", "mutable_self" if owner["doc_id"] == "T:LibreMetaverse.Imaging.Baker" and item["name"] in {"AddTexture", "Bake"}: return "&mut self", "mutable_self" if owner["doc_id"] == "T:LibreMetaverse.PrimMesher.PrimMesh" and item["name"] in { "AddPos", "AddRot", "CalcNormals", "Extrude", "Scale", }: return "&mut self", "mutable_self" if owner["doc_id"] in { "T:LibreMetaverse.PrimMesher.Coord", "T:LibreMetaverse.PrimMesher.Quat", } and item["name"] == "Length": return "&self", "shared_self" if owner["doc_id"] == "T:LibreMetaverse.BitPack" and item["name"].startswith(("Pack", "Unpack")): return "&mut self", "mutable_self" if owner["doc_id"].startswith("T:LibreMetaverse.Packets.") and ( item["name"].startswith("FromBytes") or item["name"] in {"FromOSD", "PacketFromOSD"} ): return "&mut self", "mutable_self" observers = ("Get", "TryGet", "Find", "Contains", "Has", "Is", "Can", "To", "Equals", "Compare") if owner["kind"] == "struct" and not item["name"].startswith(observers) and not item["name"].startswith("op_"): return "&mut self", "mutable_self" return "&self", "shared_self" def map_parameter(mapper: Mapper, parameter: dict, generics: set[str]) -> tuple[str, str]: name = snake(parameter["name"]) rust_type = mapper.type(parameter["type"], parameter.get("nullability"), generics) passing = parameter.get("passing", "value") ownership = "owned" if passing == "out": return "mutable_output", f"{name}: &mut {rust_type}" if passing in {"ref", "in"}: mutable = "mut " if passing == "ref" else "" return ("mutable_borrow" if passing == "ref" else "shared_borrow"), f"{name}: &{mutable}{rust_type}" if parameter.get("optional"): rust_type = rust_type if rust_type.startswith("Option<") else f"Option<{rust_type}>" ownership = "optional_owned" return ownership, f"{name}: {rust_type}" def member_base_name(item: dict) -> str: if item["kind"] == "enum_value": return pascal(item["name"]) if item["kind"] == "constant": return snake(item["name"]).upper() if item["kind"] == "constructor": return "new" operators = { "op_Addition": "add", "op_Subtraction": "sub", "op_Multiply": "mul", "op_Division": "div", "op_Equality": "eq", "op_Inequality": "ne", "op_Implicit": "from", "op_Explicit": "try_from", "op_UnaryNegation": "neg", "op_LessThan": "lt", "op_GreaterThan": "gt", "op_LessThanOrEqual": "le", "op_GreaterThanOrEqual": "ge", "op_Modulus": "rem", "op_BitwiseAnd": "bitand", "op_BitwiseOr": "bitor", "op_ExclusiveOr": "bitxor", "op_LeftShift": "shl", "op_RightShift": "shr", "op_OnesComplement": "not", } return operators.get(item["name"], snake(item["name"].removesuffix("Async"))) def parameter_slug(value: str) -> str: base, _, suffix = type_parts(value) slug = snake(base.rsplit(".", 1)[-1]) if "[" in suffix: return "bytes" if base == "System.Byte" else f"{slug}_array" return slug def member_signature(mapper: Mapper, owner: dict, item: dict, rust_name: str) -> tuple[str, str, str, str, str]: if item["doc_id"] in MEMBER_SIGNATURE_OVERRIDES: signature, ownership = MEMBER_SIGNATURE_OVERRIDES[item["doc_id"]] return signature, ownership, "sync", "Result<_, crate::Error>", "method" generics = generic_names(item, owner) mapped_parameters = [map_parameter(mapper, parameter, generics) for parameter in item.get("parameters", [])] packet_owner = owner["doc_id"].startswith("T:LibreMetaverse.Packets.") if ( packet_owner and item["name"] == "FromBytes" and len(mapped_parameters) == 4 and item["doc_id"].endswith(",System.Byte[])") ): mapped_parameters[3] = ("optional_mutable_borrow", "zero_buffer: Option<&mut [u8]>") if packet_owner and item["name"] in {"AcksToBytes", "ToBytes"} and mapped_parameters: if owner["doc_id"] == "T:LibreMetaverse.Packets.Header" or owner["doc_id"].endswith("Block"): mapped_parameters[0] = ("mutable_borrow", "bytes: &mut [u8]") own = [value[0] for value in mapped_parameters] parameters = ", ".join(value[1] for value in mapped_parameters) kind = item["kind"] asyncness = "sync" error_model = "infallible_placeholder" if kind in {"field", "constant", "enum_value"}: value_type = MEMBER_TYPE_OVERRIDES.get( item["doc_id"], mapper.type(item["type"], item.get("nullability"), generics) ) if item["doc_id"] in BOXED_FIELD_IDS: value_type = ( f"Option>" if value_type.startswith("Option<") else f"Box<{value_type}>" ) if kind == "enum_value": signature = f"{rust_name}: {value_type}" elif kind == "constant": if item["type"] == "System.String": value_type = "&'static str" signature = f"pub const {rust_name}: {value_type}" elif item.get("static"): signature = f"pub fn {rust_name}() -> {value_type}" return signature, "none", asyncness, error_model, "static_field_accessor" else: signature = f"pub {rust_name}: {value_type}" return signature, "owned_value", asyncness, "none", "direct" if kind in {"property", "indexer"}: if item["doc_id"] in PROPERTY_SIGNATURE_OVERRIDES: signature, ownership = PROPERTY_SIGNATURE_OVERRIDES[item["doc_id"]] return signature, ownership, asyncness, error_model, "accessor_methods" value_type = mapper.type(item["type"], item.get("nullability"), generics) accessors = {accessor["kind"] for accessor in item.get("accessors", [])} pieces = [] if "get" in accessors: getter = parameters if item.get("static") else ", ".join(filter(None, ["&self", parameters])) pieces.append(f"pub fn {rust_name}({getter}) -> {value_type}") if "set" in accessors: setter = ", ".join(filter(None, ["" if item.get("static") else "&mut self", parameters, f"value: {value_type}"])) pieces.append(f"pub fn set_{rust_name}({setter})") self_ownership = [] if item.get("static") else (["shared_self"] if accessors == {"get"} else ["shared_or_mutable_self"]) return " ; ".join(pieces), ",".join([*self_ownership, *own]) or "none", asyncness, error_model, "accessor_methods" if kind == "event": event_type = mapper.type(item["type"], item.get("nullability"), generics) arguments = f"handler: {event_type}" if item.get("static") else f"&self, handler: {event_type}" ownership = "stored_callback" if item.get("static") else "shared_self,stored_callback" return f"pub fn subscribe_{rust_name}({arguments}) -> libremetaverse_types::compat::Subscription", ownership, asyncness, error_model, "subscription_guard" self_argument, self_ownership = method_receiver(owner, item) arguments = ", ".join(filter(None, [self_argument, parameters])) ownership = ",".join(([self_ownership] if self_ownership != "none" else []) + own) or "none" raw_return = item.get("type", "System.Void") return_type = MEMBER_RETURN_OVERRIDES.get( item["doc_id"], mapper.type(raw_return, item.get("nullability"), generics) ) if raw_return.startswith(("System.Threading.Tasks.Task", "System.Threading.Tasks.ValueTask")): asyncness = "boxed_future" if owner["kind"] == "interface" else "async" base, task_arguments, _ = type_parts(raw_return) child = item.get("nullability", {}).get("generic_arguments", []) return_type = mapper.type(task_arguments[0], child[0] if child else None, generics) if task_arguments else "()" error_model = "Result<_, crate::Error>" elif kind in {"method", "constructor"} and not ( item["name"].startswith("Try") or item["name"] in {"Equals", "GetHashCode", "ToString"} or item["name"].startswith("op_") ): error_model = "Result<_, crate::Error>" if kind == "constructor": return_type = "Self" if error_model.startswith("Result"): return_type = f"Result<{return_type}, crate::Error>" generic_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") generic_declarations.append(parameter["name"] + ((": " + " + ".join(bounds)) if bounds else "")) generic_suffix = "<" + ", ".join(generic_declarations) + ">" if generic_declarations else "" if asyncness == "boxed_future": return_type = ( "std::pin::Pin + Send + '_>>" ) prefix = "pub async fn" if asyncness == "async" else "pub fn" return f"{prefix} {rust_name}{generic_suffix}({arguments}) -> {return_type}", ownership, asyncness, error_model, "method" def csharp_signature(owner: dict, item: dict) -> str: parameters = ", ".join( " ".join(filter(None, [parameter.get("passing") if parameter.get("passing") != "value" else "", parameter["type"], parameter["name"]])) + (" = default" if parameter.get("optional") else "") for parameter in item.get("parameters", []) ) if item["kind"] == "constructor": return f"{owner['signature']}({parameters})" if item["kind"] in {"method"}: return f"{item.get('type', 'System.Void')} {owner['signature']}.{item['name']}({parameters})" return f"{item.get('type', 'System.Void')} {owner['signature']}.{item['name']}" def build_member_rows(catalog: dict, mapper: Mapper) -> list[dict[str, str]]: rows = [] for assembly in catalog["assemblies"]: assembly_name = assembly["identity"]["name"] crate = ASSEMBLY_CRATES[assembly_name] for owner in assembly["types"]: _, module, owner_path = declared_type_path(assembly_name, owner) groups = Counter(member_base_name(member) for member in owner["members"]) synthetic_names = { f"set_{member_base_name(member)}" for member in owner["members"] if member["kind"] in {"property", "indexer"} and any(accessor["kind"] == "set" for accessor in member.get("accessors", [])) } | { f"subscribe_{member_base_name(member)}" for member in owner["members"] if member["kind"] == "event" } enum_values = [member.get("value", {}).get("value") for member in owner["members"] if member["kind"] == "enum_value"] enum_constants = owner["kind"] == "enum" and ( any(attribute["type"] == "System.FlagsAttribute" for attribute in owner.get("attributes", [])) or len(enum_values) != len(set(enum_values)) ) used = set() for item in owner["members"]: base_name = member_base_name(item) if item["kind"] == "enum_value" and enum_constants: base_name = snake(item["name"]).upper() rust_name = base_name decision = "direct_snake_case" if groups[base_name] > 1 or (item["kind"] == "method" and base_name in synthetic_names): suffixes = [] for parameter in item.get("parameters", []): suffixes.append(parameter_slug(parameter["type"])) suffix = "_".join(suffixes) or item["kind"] rust_name = f"{base_name}_with_{suffix}" decision = "descriptive_overload_name" destination = f"{owner_path}::{rust_name}" if destination in used: digest = hashlib.sha256(item["doc_id"].encode()).hexdigest()[:8] rust_name += f"_{digest}" destination = f"{owner_path}::{rust_name}" decision = "descriptive_overload_name_with_stable_id" used.add(destination) signature, ownership, asyncness, error_model, mapping_kind = member_signature(mapper, owner, item, rust_name) status = "intentional_difference" if item["name"] in {"BeginInvoke", "EndInvoke"} else "mapped" if status == "intentional_difference": mapping_kind = "native_callback_replaces_delegate_apm" rows.append({ "csharp_id": item["doc_id"], "csharp_kind": item["kind"], "csharp_signature": csharp_signature(owner, item), "rust_crate": crate, "rust_module": module, "rust_item_path": destination, "rust_signature": signature, "ownership": ownership, "asyncness": asyncness, "error_model": error_model, "overload_decision": decision, "mapping_kind": mapping_kind, "status": status, }) rows.sort(key=lambda row: row["csharp_id"]) return rows def validate(catalog: dict, type_rows: list[dict[str, str]], member_rows: list[dict[str, str]], mapper: Mapper) -> None: validate_generated_shims() def unique(rows: list[dict[str, str]], field: str) -> None: duplicates = [value for value, count in Counter(row[field] for row in rows).items() if count > 1] if duplicates: raise ValueError(f"duplicate {field}: {duplicates[:3]}") unique(type_rows, "csharp_type_id") unique(member_rows, "csharp_id") unique(member_rows, "rust_item_path") for row in type_rows: for field in ("csharp_type_id", "csharp_signature", "source_assembly", "source_kind", "rust_crate", "rust_path", "mapping_decision", "generic_bounds", "inheritance_mapping", "enum_mapping", "status"): if not row[field]: raise ValueError(f"empty required type field {field}: {row['csharp_type_id']}") for row in member_rows: for field in row: if not row[field] and field != "rust_module": raise ValueError(f"empty required member field {field}: {row['csharp_id']}") signature = row["rust_signature"].replace("->", "") for opening, closing in (("(", ")"), ("<", ">")): if signature.count(opening) != signature.count(closing): raise ValueError(f"unbalanced Rust signature: {row['csharp_id']}") if {row["status"] for row in type_rows} - VALID_TYPE_STATUSES: raise ValueError("invalid type mapping status") if {row["status"] for row in member_rows} - VALID_MEMBER_STATUSES: raise ValueError("invalid member mapping status") 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)}") external_ids = {item["doc_id"] for item in catalog["external_types"]} mapped_type_ids = {row["csharp_type_id"] for row in type_rows} if not external_ids <= mapped_type_ids: raise ValueError(f"unresolved external types: {sorted(external_ids - mapped_type_ids)[:3]}") catalog_type_ids = {item["doc_id"] for assembly in catalog["assemblies"] for item in assembly["types"]} expected_type_ids = catalog_type_ids | external_ids | {f"T:{name}" for name in SUPPORT_TYPES} if mapped_type_ids != expected_type_ids: raise ValueError(f"catalog/type mapping ID mismatch: missing={len(expected_type_ids - mapped_type_ids)}, stale={len(mapped_type_ids - expected_type_ids)}") represented = {row["rust_crate"] for row in member_rows} missing_assemblies = {crate for crate in ASSEMBLY_CRATES.values() if crate not in represented} if missing_assemblies: raise ValueError(f"assemblies without representative mappings: {sorted(missing_assemblies)}") forbidden = re.compile(r"macos|darwin|cocoa|appkit", re.IGNORECASE) for row in [*type_rows, *member_rows]: if forbidden.search("\t".join(row.values())): raise ValueError(f"platform-specific mapping is prohibited: {row}") # Re-walk every public type reference. This intentionally includes bases and interfaces. for assembly in catalog["assemblies"]: for owner in assembly["types"]: owner_generics = generic_names({}, owner) for parameter in owner.get("generic_parameters", []): for constraint in parameter.get("constraints", []): mapper.constraint(constraint, owner_generics) for value in filter(None, [owner.get("base_type"), *owner.get("interfaces", [])]): mapper.type(value, None, owner_generics) if owner.get("delegate_signature"): delegate = owner["delegate_signature"] mapper.type(delegate["type"], delegate.get("nullability"), owner_generics) for parameter in delegate.get("parameters", []): mapper.type(parameter["type"], parameter.get("nullability"), owner_generics) for item in owner["members"]: generics = generic_names(item, owner) for parameter in item.get("generic_parameters", []): for constraint in parameter.get("constraints", []): mapper.constraint(constraint, generics) for value in filter(None, [item.get("type"), item.get("return_type")]): mapper.type(value, item.get("nullability") or item.get("return_nullability"), generics) for parameter in item.get("parameters", []): mapper.type(parameter["type"], parameter.get("nullability"), generics) def coverage_report(catalog: dict, type_rows: list[dict[str, str]], member_rows: list[dict[str, str]]) -> str: by_crate = Counter(row["rust_crate"] for row in member_rows) representatives = {} for row in member_rows: representatives.setdefault(row["rust_crate"], row) external = [row for row in type_rows if not row["source_assembly"].startswith("LibreMetaverse")] lines = [ "# Rust mapping coverage", "", "Generated by `python3 tools/generate_rust_mapping.py`; do not edit by hand.", "", "## Gate", "", f"- C# public types mapped: **{sum(row['mapping_decision'] == 'native_metacrate_type' for row in type_rows):,} / {catalog['summary']['public_type_count']:,} (100%)**", f"- C# public members mapped: **{len(member_rows):,} / {catalog['summary']['public_member_count']:,} (100%)**", f"- External signature types resolved: **{len(external):,} / {len(catalog['external_types']):,} (100%)**", "- Duplicate C# IDs: **0**", "- Duplicate Rust destinations: **0**", "- Stale or missing catalog IDs: **0**", "- Unresolved referenced types: **0**", "- Platform-specific mappings: **0**", "- Plausible generated fallback bodies/default derives: **0**", "", "## Assembly representatives", "", "| Assembly | Rust crate | Members | Representative mapping |", "|---|---|---:|---|", ] for assembly in catalog["assemblies"]: name = assembly["identity"]["name"] crate = ASSEMBLY_CRATES[name] row = representatives[crate] lines.append(f"| `{name}` | `{crate}` | {by_crate[crate]:,} | `{row['csharp_id']}` -> `{row['rust_item_path']}` |") lines += ["", "## External replacement decisions", "", "External member APIs are not copied. Each signature type resolves to Rust core/std, an adopted cross-platform crate, or a project-owned native compatibility type.", "", "| Decision | Types |", "|---|---:|"] for decision, count in sorted(Counter(row["mapping_decision"] for row in external).items()): lines.append(f"| `{decision}` | {count:,} |") members = [member for assembly in catalog["assemblies"] for item in assembly["types"] for member in item["members"]] parameters = [parameter for member in members for parameter in member.get("parameters", [])] types = [item for assembly in catalog["assemblies"] for item in assembly["types"]] flags = sum( any(attribute["type"] == "System.FlagsAttribute" for attribute in item.get("attributes", [])) for item in types if item["kind"] == "enum" ) lines += [ "", "## Schema decisions exercised", "", f"- Properties/indexers/events: **{sum(m['kind'] == 'property' for m in members):,} / {sum(m['kind'] == 'indexer' for m in members):,} / {sum(m['kind'] == 'event' for m in members):,}**", f"- Delegates and flags enums: **{sum(t['kind'] == 'delegate' for t in types):,} / {flags:,}**", f"- Optional/ref/out/in parameters: **{sum(p.get('optional') for p in parameters):,} / {sum(p.get('passing') == 'ref' for p in parameters):,} / {sum(p.get('passing') == 'out' for p in parameters):,} / {sum(p.get('passing') == 'in' for p in parameters):,}**", f"- Async Rust signatures: **{sum(row['asyncness'] == 'async' for row in member_rows):,}**", f"- Descriptively renamed overloads: **{sum(row['overload_decision'] != 'direct_snake_case' for row in member_rows):,}**", f"- Explicit CLR delegate APM differences: **{sum(row['status'] == 'intentional_difference' for row in member_rows):,}**", "", "Run `python3 tools/generate_rust_mapping.py --check` to regenerate in memory and enforce the complete gate.", "", ] return "\n".join(lines) def generate() -> dict[Path, str]: catalog = json.loads(CATALOG.read_text()) type_rows, resolved = build_type_rows(catalog) interfaces = { *{type_parts(item["signature"])[0] for assembly in catalog["assemblies"] for item in assembly["types"] if item["kind"] == "interface"}, *{ type_parts(item["signature"])[0] for item in catalog["external_types"] if item["kind"] == "interface" and not item["assembly"]["name"].startswith("System") }, } mapper = Mapper(resolved, interfaces) member_rows = build_member_rows(catalog, mapper) validate(catalog, type_rows, member_rows, mapper) type_fields = ["csharp_type_id", "csharp_signature", "source_assembly", "source_kind", "rust_crate", "rust_module", "rust_path", "mapping_decision", "generic_bounds", "inheritance_mapping", "enum_mapping", "status", "notes"] member_fields = ["csharp_id", "csharp_kind", "csharp_signature", "rust_crate", "rust_module", "rust_item_path", "rust_signature", "ownership", "asyncness", "error_model", "overload_decision", "mapping_kind", "status"] return { TYPE_LEDGER: render_tsv(type_fields, type_rows), MEMBER_LEDGER: render_tsv(member_fields, member_rows), COVERAGE: coverage_report(catalog, type_rows, member_rows), } def self_check() -> None: assert split_top_level("A>, F") == ["A>", "F"] assert type_parts("A, D>.Nested[]") == ("A.Nested", ["B", "D"], "[]") assert snake("HTTPRequest2") == "http_request2" assert function_bodies("pub fn value() -> bool { false }") == [" false "] assert FAKE_BODY.search(function_bodies("pub fn value() -> bool { false; }")[0]) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--check", action="store_true", help="fail if committed ledgers differ") args = parser.parse_args() self_check() outputs = generate() if args.check: stale = [str(path.relative_to(ROOT)) for path, value in outputs.items() if not path.exists() or path.read_text() != value] if stale: raise SystemExit("stale generated mapping files: " + ", ".join(stale)) else: for path, value in outputs.items(): path.write_text(value) print("generated " + ", ".join(str(path.relative_to(ROOT)) for path in outputs)) if __name__ == "__main__": main()