Generate Types and StructuredData API shims
This commit is contained in:
@@ -54,7 +54,7 @@ PRIMITIVES = {
|
||||
}
|
||||
|
||||
STDLIB = {
|
||||
"System.Array": "Vec<T>",
|
||||
"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>",
|
||||
@@ -96,8 +96,8 @@ STDLIB = {
|
||||
"System.DateTime": "std::time::SystemTime",
|
||||
"System.DateTimeOffset": "std::time::SystemTime",
|
||||
"System.TimeSpan": "std::time::Duration",
|
||||
"System.Guid": "uuid::Uuid",
|
||||
"System.Uri": "url::Url",
|
||||
"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",
|
||||
@@ -114,7 +114,7 @@ STDLIB = {
|
||||
"System.ValueType": "libremetaverse_types::compat::Value",
|
||||
"System.Text.Encoding": "libremetaverse_types::compat::TextEncoding",
|
||||
"System.Text.StringBuilder": "String",
|
||||
"System.Text.RegularExpressions.Regex": "regex::Regex",
|
||||
"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>",
|
||||
@@ -142,8 +142,8 @@ STDLIB = {
|
||||
"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<R>",
|
||||
"System.Xml.XmlWriter": "libremetaverse_structured_data::xml::Writer<W>",
|
||||
"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",
|
||||
}
|
||||
|
||||
@@ -459,8 +459,9 @@ def build_type_rows(catalog: dict) -> tuple[list[dict[str, str]], dict[str, str]
|
||||
|
||||
|
||||
class Mapper:
|
||||
def __init__(self, resolved: dict[str, str]):
|
||||
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)
|
||||
@@ -498,8 +499,10 @@ class Mapper:
|
||||
"System.ValueTuple", "System.Threading.Tasks.Task", "System.Threading.Tasks.ValueTask",
|
||||
}:
|
||||
target = substitute(target, mapped_arguments)
|
||||
if "<" not in target and target not in {"Ord", "Eq", "Clone", "std::fmt::Display"}:
|
||||
if "<" not in target and not target.startswith("(") and target not in {"Ord", "Eq", "Clone", "std::fmt::Display"}:
|
||||
target += "<" + ", ".join(mapped_arguments) + ">"
|
||||
if base in self.interfaces:
|
||||
target = f"Box<dyn {target}>"
|
||||
for _ in re.findall(r"\[[,]*\]", suffix):
|
||||
target = f"Vec<{target}>"
|
||||
if "*" in suffix:
|
||||
@@ -551,7 +554,7 @@ def map_parameter(mapper: Mapper, parameter: dict, generics: set[str]) -> tuple[
|
||||
def member_base_name(item: dict) -> str:
|
||||
if item["kind"] == "enum_value":
|
||||
return pascal(item["name"])
|
||||
if item["kind"] == "constant" or (item["kind"] == "field" and item.get("static")):
|
||||
if item["kind"] == "constant":
|
||||
return snake(item["name"]).upper()
|
||||
if item["kind"] == "constructor":
|
||||
return "new"
|
||||
@@ -566,6 +569,14 @@ def member_base_name(item: dict) -> str:
|
||||
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]:
|
||||
generics = generic_names(item, owner)
|
||||
mapped_parameters = [map_parameter(mapper, parameter, generics) for parameter in item.get("parameters", [])]
|
||||
@@ -581,7 +592,8 @@ def member_signature(mapper: Mapper, owner: dict, item: dict, rust_name: str) ->
|
||||
elif kind == "constant":
|
||||
signature = f"pub const {rust_name}: {value_type}"
|
||||
elif item.get("static"):
|
||||
signature = f"pub static {rust_name}: {value_type}"
|
||||
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"
|
||||
@@ -653,16 +665,22 @@ def build_member_rows(catalog: dict, mapper: Mapper) -> list[dict[str, str]]:
|
||||
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"])
|
||||
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:
|
||||
suffixes = []
|
||||
for parameter in item.get("parameters", []):
|
||||
base, _, _ = type_parts(parameter["type"])
|
||||
suffixes.append(snake(base.rsplit(".", 1)[-1]))
|
||||
suffixes.append(parameter_slug(parameter["type"]))
|
||||
suffix = "_".join(suffixes) or item["kind"]
|
||||
rust_name = f"{base_name}_with_{suffix}"
|
||||
decision = "descriptive_overload_name"
|
||||
@@ -808,7 +826,8 @@ def coverage_report(catalog: dict, type_rows: list[dict[str, str]], member_rows:
|
||||
def generate() -> dict[Path, str]:
|
||||
catalog = json.loads(CATALOG.read_text())
|
||||
type_rows, resolved = build_type_rows(catalog)
|
||||
mapper = Mapper(resolved)
|
||||
interfaces = {type_parts(item["signature"])[0] for assembly in catalog["assemblies"] for item in assembly["types"] if item["kind"] == "interface"}
|
||||
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"]
|
||||
|
||||
Reference in New Issue
Block a user