1619 lines
89 KiB
Python
1619 lines
89 KiB
Python
#!/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<T>",
|
|
"System.Collections.BitArray": "Vec<bool>",
|
|
"System.Collections.Generic.Dictionary": "std::collections::HashMap<K, V>",
|
|
"System.Collections.Generic.Dictionary.KeyCollection": "libremetaverse_types::compat::DictionaryKeys<K, V>",
|
|
"System.Collections.Generic.HashSet": "std::collections::HashSet<T>",
|
|
"System.Collections.Generic.ICollection": "Vec<T>",
|
|
"System.Collections.Generic.IEnumerable": "Box<dyn Iterator<Item = T>>",
|
|
"System.Collections.Generic.IEnumerator": "Box<dyn Iterator<Item = T>>",
|
|
"System.Collections.Generic.IList": "Vec<T>",
|
|
"System.Collections.Generic.IReadOnlyCollection": "Vec<T>",
|
|
"System.Collections.Generic.IReadOnlyDictionary": "std::collections::HashMap<K, V>",
|
|
"System.Collections.Generic.IReadOnlyList": "Vec<T>",
|
|
"System.Collections.Generic.KeyValuePair": "(K, V)",
|
|
"System.Collections.Generic.LinkedList": "std::collections::LinkedList<T>",
|
|
"System.Collections.Generic.List": "Vec<T>",
|
|
"System.Collections.Generic.Queue": "std::collections::VecDeque<T>",
|
|
"System.Collections.Generic.SortedDictionary": "std::collections::BTreeMap<K, V>",
|
|
"System.Collections.Generic.Stack": "Vec<T>",
|
|
"System.Collections.ObjectModel.ReadOnlyCollection": "Vec<T>",
|
|
"System.Collections.Specialized.NameValueCollection": "std::collections::HashMap<String, Vec<String>>",
|
|
"System.Collections.Concurrent.ConcurrentDictionary": "std::sync::RwLock<std::collections::HashMap<K, V>>",
|
|
"System.Collections.Concurrent.ConcurrentQueue": "std::sync::Mutex<std::collections::VecDeque<T>>",
|
|
"System.Collections.Concurrent.ConcurrentStack": "std::sync::Mutex<Vec<T>>",
|
|
"System.Action": "Box<dyn Fn() + Send + Sync>",
|
|
"System.Comparison": "Box<dyn Fn(&T, &T) -> std::cmp::Ordering + Send + Sync>",
|
|
"System.Converter": "Box<dyn Fn(TInput) -> TOutput + Send + Sync>",
|
|
"System.Func": "Box<dyn Fn(Args) -> Output + Send + Sync>",
|
|
"System.Predicate": "Box<dyn Fn(&T) -> bool + Send + Sync>",
|
|
"System.AsyncCallback": "Box<dyn Fn(&dyn std::any::Any) + Send + Sync>",
|
|
"System.IAsyncResult": "Box<dyn std::any::Any + Send + Sync>",
|
|
"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<T>",
|
|
"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<dyn libremetaverse_types::compat::ReadWrite + Send>",
|
|
"System.IO.MemoryStream": "std::io::Cursor<Vec<u8>>",
|
|
"System.IO.TextReader": "Box<dyn std::io::BufRead + Send>",
|
|
"System.IO.TextWriter": "Box<dyn std::io::Write + Send>",
|
|
"System.IO.BinaryReader": "libremetaverse_types::compat::BinaryReader<R>",
|
|
"System.IO.BinaryWriter": "libremetaverse_types::compat::BinaryWriter<W>",
|
|
"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<T>",
|
|
"System.Threading.Tasks.ValueTask": "libremetaverse_types::compat::Task<T>",
|
|
"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<String>",
|
|
"M:LibreMetaverse.PrimMesher.PrimMesh.GetVertexIndexer": (
|
|
"Option<libremetaverse_prim_mesher::VertexIndexer>"
|
|
),
|
|
}
|
|
MEMBER_TYPE_OVERRIDES = {
|
|
"F:LibreMetaverse.Primitive.Flexible": "Option<libremetaverse::PrimitiveFlexibleData>",
|
|
"F:LibreMetaverse.Primitive.Light": "Option<libremetaverse::PrimitiveLightData>",
|
|
"F:LibreMetaverse.Primitive.LightMap": "Option<libremetaverse::PrimitiveLightImage>",
|
|
"F:LibreMetaverse.Primitive.NameValues": "Option<Vec<libremetaverse::NameValue>>",
|
|
"F:LibreMetaverse.Primitive.TextureEntry.FaceTextures": (
|
|
"Vec<Option<libremetaverse::PrimitiveTextureEntryFace>>"
|
|
),
|
|
"F:LibreMetaverse.Rendering.Face.Weights": (
|
|
"Option<Vec<libremetaverse::rendering::VertexWeight>>"
|
|
),
|
|
"F:LibreMetaverse.PrimMesher.VertexIndexer.viewerPolygons": (
|
|
"Vec<Option<Vec<libremetaverse_prim_mesher::ViewerPolygon>>>"
|
|
),
|
|
}
|
|
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<TValue>)], 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<TValue>)], start_index: i32) -> Result<(), crate::Error>",
|
|
"shared_self,mutable_borrow,owned",
|
|
),
|
|
"P:LibreMetaverse.ExpiringCache`2.Item(`0)": (
|
|
"pub fn item(&self, key: TKey) -> Option<TValue>",
|
|
"shared_self,owned",
|
|
),
|
|
"M:LibreMetaverse.Utils.EnumTryParse``1(System.String,``0@)": (
|
|
"pub fn enum_try_parse<T: crate::EnumValueMetadata>(str_type: String, result: &mut Option<T>) -> Result<bool, crate::Error>",
|
|
"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<f32, crate::Error>",
|
|
"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<f64, crate::Error>",
|
|
"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<std::sync::Arc<libremetaverse::GridClient>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"optional_shared",
|
|
),
|
|
"M:LibreMetaverse.AgentManager.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"optional_shared",
|
|
),
|
|
"M:LibreMetaverse.AvatarManager.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"optional_shared",
|
|
),
|
|
"M:LibreMetaverse.GridManager.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"optional_shared",
|
|
),
|
|
"M:LibreMetaverse.Inventory.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new_with_grid_client(client: std::sync::Arc<libremetaverse::GridClient>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"shared",
|
|
),
|
|
"M:LibreMetaverse.Inventory.#ctor(LibreMetaverse.GridClient,LibreMetaverse.UUID)": (
|
|
"pub fn new_with_grid_client_uuid(client: std::sync::Arc<libremetaverse::GridClient>, "
|
|
"owner: libremetaverse_types::UUID) -> Result<Self, crate::Error>",
|
|
"shared,owned",
|
|
),
|
|
"M:LibreMetaverse.Inventory.Contains(LibreMetaverse.InventoryBase)": (
|
|
"pub fn contains_with_inventory_base(&self, obj: &dyn "
|
|
"libremetaverse::InventoryObjectClass) -> Result<bool, crate::Error>",
|
|
"shared_self,shared_borrow",
|
|
),
|
|
"M:LibreMetaverse.Inventory.GetContents(LibreMetaverse.InventoryFolder)": (
|
|
"pub fn get_contents_with_inventory_folder(&self, folder: &libremetaverse::InventoryFolder) "
|
|
"-> Result<Vec<Box<dyn libremetaverse::InventoryObjectClass>>, crate::Error>",
|
|
"shared_self,shared_borrow",
|
|
),
|
|
"M:LibreMetaverse.Inventory.GetContents(LibreMetaverse.UUID)": (
|
|
"pub fn get_contents_with_uuid(&self, folder: libremetaverse_types::UUID) "
|
|
"-> Result<Vec<Box<dyn libremetaverse::InventoryObjectClass>>, 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<Box<dyn libremetaverse::InventoryObjectClass>>) "
|
|
"-> Result<bool, crate::Error>",
|
|
"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<Vec<libremetaverse_types::UUID>, 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<libremetaverse_types::UUID, crate::Error>",
|
|
"owned,shared_borrow",
|
|
),
|
|
"M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetListingsRoot(LibreMetaverse.Inventory)": (
|
|
"pub fn get_listings_root(inventory: &libremetaverse::Inventory) "
|
|
"-> Result<libremetaverse_types::UUID, crate::Error>",
|
|
"shared_borrow",
|
|
),
|
|
"M:LibreMetaverse.Marketplace.MarketplaceFolderClassifier.GetRole(LibreMetaverse.UUID,LibreMetaverse.Inventory)": (
|
|
"pub fn get_role(folder_id: libremetaverse_types::UUID, inventory: &libremetaverse::Inventory) "
|
|
"-> Result<libremetaverse::marketplace::MarketplaceFolderRole, crate::Error>",
|
|
"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<i32, crate::Error>",
|
|
"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<libremetaverse_types::UUID, crate::Error>",
|
|
"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<bool, crate::Error>",
|
|
"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<libremetaverse::marketplace::MarketplaceValidationFlags, crate::Error>",
|
|
"owned,shared_borrow",
|
|
),
|
|
"M:LibreMetaverse.Marketplace.MarketplaceErrorEventArgs.#ctor(System.String,System.Exception)": (
|
|
"pub fn new(message: String, ex: Option<std::sync::Arc<libremetaverse_types::compat::ExternalError>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"owned,optional_shared",
|
|
),
|
|
"M:LibreMetaverse.Marketplace.MarketplaceListingChangedEventArgs.#ctor(System.Int32,LibreMetaverse.Marketplace.MarketplaceListing)": (
|
|
"pub fn new(listing_id: i32, listing: Option<std::sync::Arc<libremetaverse::marketplace::MarketplaceListing>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"owned,optional_shared",
|
|
),
|
|
"M:LibreMetaverse.InventoryAISClient.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"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<Vec<libremetaverse::InventoryFolder>>, items: &mut "
|
|
"Option<Vec<libremetaverse::InventoryItem>>, links: &mut "
|
|
"Option<Vec<libremetaverse::InventoryItem>>) "
|
|
"-> 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<libremetaverse_structured_data::OSD>) -> Result<Vec<libremetaverse::InventoryItem>, "
|
|
"crate::Error>",
|
|
"shared_self,optional_owned",
|
|
),
|
|
"M:LibreMetaverse.InventoryAISClient.ParseLinksFromEmbedded(LibreMetaverse.StructuredData.OSDMap)": (
|
|
"pub fn parse_links_from_embedded(&self, response: "
|
|
"Option<libremetaverse_structured_data::OSD>) -> Result<Vec<libremetaverse::InventoryItem>, "
|
|
"crate::Error>",
|
|
"shared_self,optional_owned",
|
|
),
|
|
"M:LibreMetaverse.InventoryManager.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"optional_shared",
|
|
),
|
|
"M:LibreMetaverse.ObjectManager.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) "
|
|
"-> Result<Self, crate::Error>",
|
|
"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<Option<&libremetaverse::PrimitiveTextureEntryFace>, crate::Error>",
|
|
"shared_self,owned",
|
|
),
|
|
"M:LibreMetaverse.SlurlParser.#ctor(System.String)": (
|
|
"pub fn new(location: Option<String>) -> Result<Self, crate::Error>",
|
|
"optional_owned",
|
|
),
|
|
"M:LibreMetaverse.InventoryManager.CreateInventoryItem(LibreMetaverse.InventoryType,LibreMetaverse.UUID)": (
|
|
"pub fn create_inventory_item(type_: libremetaverse_types::InventoryType, id: "
|
|
"libremetaverse_types::UUID) -> Result<Box<dyn "
|
|
"libremetaverse::InventoryItemClass>, 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<libremetaverse_types::compat::CancellationToken>, follow_links: Option<bool>) "
|
|
"-> Result<Vec<Box<dyn libremetaverse::InventoryObjectClass>>, 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<libremetaverse::Simulator>, cancellation_token: "
|
|
"Option<libremetaverse_types::compat::CancellationToken>) -> Result<Vec<Box<dyn "
|
|
"libremetaverse::InventoryObjectClass>>, crate::Error>",
|
|
"shared_self,owned,owned,optional_owned,optional_owned",
|
|
),
|
|
"M:LibreMetaverse.InventoryManager.ItemCRC(LibreMetaverse.InventoryItem)": (
|
|
"pub fn item_crc(iitem: &libremetaverse::InventoryItem) -> Result<u32, crate::Error>",
|
|
"shared_borrow",
|
|
),
|
|
"M:LibreMetaverse.InventoryNode.#ctor(LibreMetaverse.InventoryBase)": (
|
|
"pub fn new_with_inventory_base(data: &dyn libremetaverse::InventoryObjectClass) "
|
|
"-> Result<Self, crate::Error>",
|
|
"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<libremetaverse::Simulator>, avatar_id: libremetaverse_types::UUID, "
|
|
"is_trial: bool, default_texture: libremetaverse::PrimitiveTextureEntryFace, face_textures: "
|
|
"Vec<libremetaverse::PrimitiveTextureEntryFace>, visual_params: Vec<u8>, appearance_version: "
|
|
"u8, cof_version: i32, appearance_flags: libremetaverse::AppearanceFlags, child_count: i32) "
|
|
"-> Result<Self, crate::Error>",
|
|
"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<std::sync::Arc<dyn "
|
|
"libremetaverse::appearance::ICurrentOutfitPolicy>>) -> Result<&Self, crate::Error>",
|
|
"shared_self,optional_shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<std::sync::Arc<libremetaverse::GridClient>>) -> Result<Self, crate::Error>",
|
|
"optional_shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)": (
|
|
"pub fn can_attach(&self, item: Option<std::sync::Arc<libremetaverse::InventoryItem>>) "
|
|
"-> Result<bool, crate::Error>",
|
|
"shared_self,optional_shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)": (
|
|
"pub fn can_detach(&self, item: Option<std::sync::Arc<libremetaverse::InventoryItem>>) "
|
|
"-> Result<bool, crate::Error>",
|
|
"shared_self,optional_shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)": (
|
|
"pub fn remove_policy(&self, policy_to_remove: Option<std::sync::Arc<dyn "
|
|
"libremetaverse::appearance::ICurrentOutfitPolicy>>) -> 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<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>, "
|
|
"removed_items: Option<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>, cancellation_token: "
|
|
"Option<libremetaverse_types::compat::CancellationToken>) -> 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<dyn "
|
|
"libremetaverse::appearance::ICurrentOutfitPolicy>) -> Result<std::sync::Arc<dyn "
|
|
"libremetaverse::appearance::ICurrentOutfitPolicy>, crate::Error>",
|
|
"shared_self,shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)": (
|
|
"pub fn remove_policy(&self, policy_to_remove: std::sync::Arc<dyn "
|
|
"libremetaverse::appearance::ICurrentOutfitPolicy>) -> Result<(), crate::Error>",
|
|
"shared_self,shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem)": (
|
|
"pub fn resolve_inventory_link(&self, item_link: std::sync::Arc<libremetaverse::InventoryItem>) "
|
|
"-> Result<Option<std::sync::Arc<libremetaverse::InventoryItem>>, crate::Error>",
|
|
"shared_self,shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)": (
|
|
"pub fn can_attach(&self, item: Option<std::sync::Arc<libremetaverse::InventoryItem>>) "
|
|
"-> Result<bool, crate::Error>",
|
|
"shared_self,optional_shared",
|
|
),
|
|
"M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)": (
|
|
"pub fn can_detach(&self, item: Option<std::sync::Arc<libremetaverse::InventoryItem>>) "
|
|
"-> Result<bool, crate::Error>",
|
|
"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<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>, "
|
|
"removed_items: Option<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>, cancellation_token: "
|
|
"Option<libremetaverse_types::compat::CancellationToken>) -> std::pin::Pin<Box<dyn "
|
|
"std::future::Future<Output = Result<(), crate::Error>> + 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<dyn Iterator<Item = (libremetaverse::InventoryItem, String)>>, callback: Option<Box<dyn "
|
|
"Fn(bool) + Send + Sync>>, cancellation_token: Option<libremetaverse_types::compat::CancellationToken>) "
|
|
"-> 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<String, "
|
|
"libremetaverse::animesh::JointPose>) -> 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<std::collections::HashMap<String, "
|
|
"libremetaverse::animesh::JointPose>>, skeleton: Option<libremetaverse::rendering::LindenSkeleton>, "
|
|
"skin_data: Option<libremetaverse::rendering::MeshSkinData>) "
|
|
"-> Result<Vec<libremetaverse_types::Matrix4>, 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<libremetaverse_types::Matrix4>, 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<std::collections::HashMap<String, "
|
|
"std::sync::Arc<libremetaverse::rendering::Joint>>, crate::Error>",
|
|
"shared_self",
|
|
),
|
|
"M:LibreMetaverse.Rendering.LindenSkeleton.GetAllJoints": (
|
|
"pub fn get_all_joints(&self) -> Result<Box<dyn Iterator<Item = "
|
|
"std::sync::Arc<libremetaverse::rendering::Joint>>>, crate::Error>",
|
|
"shared_self",
|
|
),
|
|
"M:LibreMetaverse.Rendering.LindenSkeleton.GetBone(System.String)": (
|
|
"pub fn get_bone(&self, name_or_alias: String) -> Result<Option<std::sync::Arc<"
|
|
"libremetaverse::rendering::Joint>>, crate::Error>",
|
|
"shared_self,owned",
|
|
),
|
|
"M:LibreMetaverse.AssetManager.#ctor(LibreMetaverse.GridClient)": (
|
|
"pub fn new(client: Option<libremetaverse::GridClient>) -> Result<Self, crate::Error>",
|
|
"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<i32, crate::Error>",
|
|
"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<i32, crate::Error>",
|
|
"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<u8>, "
|
|
"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<libremetaverse_types::compat::CancellationToken>) "
|
|
"-> Result<Option<Vec<Option<libremetaverse::assets::AssetMaterial>>>, 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<Option<libremetaverse::assets::AssetMaterial>>, cancellation_token: "
|
|
"Option<libremetaverse_types::compat::CancellationToken>) -> Result<bool, crate::Error>",
|
|
"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<libremetaverse::rendering::Vertex>, "
|
|
"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<Vec<f32>>, x_begin: f32, x_end: f32, "
|
|
"y_begin: f32, y_end: f32) -> Result<libremetaverse::rendering::Face, crate::Error>",
|
|
"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<libremetaverse::rendering::Vertex>, "
|
|
"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<libremetaverse::rendering::Vertex>, "
|
|
"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<std::sync::Arc<libremetaverse_types::compat::ExternalError>>",
|
|
"shared_self",
|
|
),
|
|
"P:LibreMetaverse.Marketplace.MarketplaceListingChangedEventArgs.Listing": (
|
|
"pub fn listing(&self) -> Option<std::sync::Arc<libremetaverse::marketplace::MarketplaceListing>>",
|
|
"shared_self",
|
|
),
|
|
"P:LibreMetaverse.InventoryNode.Data": (
|
|
"pub fn data(&self) -> Option<Box<dyn libremetaverse::InventoryObjectClass>> ; "
|
|
"pub fn set_data(&mut self, value: Option<Box<dyn "
|
|
"libremetaverse::InventoryObjectClass>>)",
|
|
"shared_or_mutable_self",
|
|
),
|
|
"P:LibreMetaverse.AppearanceManager.TextureProvider": (
|
|
"pub fn texture_provider(&self) -> std::sync::Arc<dyn libremetaverse::IBakingTextureProvider> ; "
|
|
"pub fn set_texture_provider(&mut self, value: std::sync::Arc<dyn "
|
|
"libremetaverse::IBakingTextureProvider>)",
|
|
"shared_or_mutable_self",
|
|
),
|
|
"P:LibreMetaverse.Rendering.Joint.bone": (
|
|
"pub fn bone(&self) -> Option<Vec<libremetaverse::rendering::Joint>> ; "
|
|
"pub fn set_bone(&mut self, value: Option<Vec<libremetaverse::rendering::Joint>>)",
|
|
"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<dyn Fn(libremetaverse::DictionaryEventAction, "
|
|
"libremetaverse_types::compat::DictionaryEntry) + Send + Sync>"
|
|
),
|
|
}
|
|
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<dyn Fn({', '.join(arguments)}) + Send + Sync>", "language"
|
|
if base == "System.Func":
|
|
output = arguments[-1] if arguments else "()"
|
|
return "core", "callback", f"Box<dyn Fn({', '.join(arguments[:-1])}) -> {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 (
|
|
"native client-core implementation" in body
|
|
or "native network implementation" in body
|
|
):
|
|
continue
|
|
uses_standardized_or_native_path = any(
|
|
marker in body
|
|
for marker in (
|
|
"unimplemented_api!",
|
|
"not_implemented(",
|
|
"NotImplemented::new(",
|
|
"crate::agent_manager::",
|
|
"crate::agent_messages::",
|
|
"crate::agent_movement::",
|
|
"crate::animation::",
|
|
"crate::animesh_runtime::",
|
|
"crate::animesh_skinning::",
|
|
"crate::asset_cache::",
|
|
"crate::asset_manager::",
|
|
"crate::asset_material::",
|
|
"crate::asset_models::",
|
|
"crate::appearance_baker::",
|
|
"crate::appearance_manager::",
|
|
"crate::avatar_manager::",
|
|
"crate::avatar_rig::",
|
|
"crate::baking_texture_provider::",
|
|
"crate::byte_order::",
|
|
"crate::attention_catalog::",
|
|
"crate::binary::",
|
|
"crate::dispatch::",
|
|
"crate::foliage_catalog::",
|
|
"crate::genepool_catalog::",
|
|
"crate::gesture::",
|
|
"crate::json_codec::",
|
|
"crate::inventory::",
|
|
"crate::inventory_ais::",
|
|
"crate::inventory_manager::",
|
|
"crate::initial_outfit::",
|
|
"crate::current_outfit::",
|
|
"crate::message_codec::",
|
|
"crate::message_decoder::",
|
|
"crate::NameValue {",
|
|
"crate::object_material::",
|
|
"crate::object_manager::",
|
|
"crate::object_model::",
|
|
"crate::notation::",
|
|
"crate::packet_catalog::",
|
|
"crate::packet_wire::",
|
|
"crate::ProfileFlags::",
|
|
"crate::TransactionInfo",
|
|
"crate::protobuf::",
|
|
"crate::skeleton::",
|
|
"crate::visual_catalog::",
|
|
"crate::transfers::",
|
|
"crate::xml_codec::",
|
|
"stored_visual_params",
|
|
"self.native_",
|
|
"left.native_equals(",
|
|
)
|
|
)
|
|
if uses_standardized_or_native_path:
|
|
continue
|
|
if FAKE_BODY.search(body):
|
|
excerpt = " ".join(body.strip().split())[:160]
|
|
raise ValueError(
|
|
"generated shim returns a plausible fallback: "
|
|
f"{path.relative_to(ROOT)}: {excerpt}"
|
|
)
|
|
excerpt = " ".join(body.strip().split())[:160]
|
|
raise ValueError(
|
|
"generated shim function does not use the standardized failure: "
|
|
f"{path.relative_to(ROOT)}: {excerpt}"
|
|
)
|
|
|
|
|
|
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<dyn Fn({parameters}) + Send + Sync>"
|
|
elif base == "System.Func":
|
|
output = mapped_arguments[-1] if mapped_arguments else "()"
|
|
parameters = ", ".join(mapped_arguments[:-1])
|
|
target = f"Box<dyn Fn({parameters}) -> {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<dyn {target}>"
|
|
elif base in self.interfaces or base == "System.IProgress":
|
|
target = f"Box<dyn {target}>"
|
|
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<Box<{value_type[7:-1]}>>"
|
|
if value_type.startswith("Option<")
|
|
else f"Box<{value_type}>"
|
|
)
|
|
if kind == "enum_value":
|
|
signature = f"{rust_name}: {value_type}"
|
|
elif kind == "constant":
|
|
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<Box<dyn std::future::Future<Output = "
|
|
f"{return_type}> + 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<B, C<D, E>>, F") == ["A<B, C<D, E>>", "F"]
|
|
assert type_parts("A<B<C>, D>.Nested[]") == ("A.Nested", ["B<C>", "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()
|