Implement native client core lifecycle (#51)
This commit is contained in:
@@ -127,10 +127,86 @@ NATIVE_TYPES = {
|
||||
# below. This is used for static namespace types such as OSDParser: the type is
|
||||
# hand-written, while its fixed public methods remain generator-audited.
|
||||
NATIVE_DECLARATIONS = {
|
||||
"T:LibreMetaverse.GridClient": "crate::client_core::GridClient",
|
||||
"T:LibreMetaverse.Settings": "crate::client_core::Settings",
|
||||
"T:LibreMetaverse.StructuredData.OSDParser": "crate::model::OSDParser",
|
||||
}
|
||||
|
||||
NATIVE_MEMBER_BODIES = {
|
||||
"M:LibreMetaverse.AgentSettings.#ctor":
|
||||
"Ok(crate::client_core::agent_settings_defaults())",
|
||||
"M:LibreMetaverse.AssetCacheSettings.#ctor":
|
||||
"Ok(crate::client_core::asset_cache_settings_defaults())",
|
||||
"M:LibreMetaverse.ConnectionSettings.#ctor":
|
||||
"Ok(crate::client_core::connection_settings_defaults())",
|
||||
"M:LibreMetaverse.LoggingSettings.#ctor":
|
||||
"Ok(crate::client_core::logging_settings_defaults())",
|
||||
"M:LibreMetaverse.PacketSettings.#ctor":
|
||||
"Ok(crate::client_core::packet_settings_defaults())",
|
||||
"M:LibreMetaverse.ParcelSettings.#ctor":
|
||||
"Ok(crate::client_core::parcel_settings_defaults())",
|
||||
"M:LibreMetaverse.TexturePipelineSettings.#ctor":
|
||||
"Ok(crate::client_core::texture_pipeline_settings_defaults())",
|
||||
"M:LibreMetaverse.TimingSettings.#ctor":
|
||||
"Ok(crate::client_core::timing_settings_defaults())",
|
||||
"M:LibreMetaverse.WorldSettings.#ctor":
|
||||
"Ok(crate::client_core::world_settings_defaults())",
|
||||
"M:LibreMetaverse.GridClient.#ctor":
|
||||
"crate::client_core::GridClientBuilder::default().build().map_err(Into::into)",
|
||||
"M:LibreMetaverse.GridClient.Dispose":
|
||||
"self.shutdown().map_err(Into::into)",
|
||||
"M:LibreMetaverse.GridClient.DisposeAsync":
|
||||
"self.shutdown().map_err(Into::into)",
|
||||
"M:LibreMetaverse.GridClient.ToString":
|
||||
"String::new()",
|
||||
"P:LibreMetaverse.GridClient.Settings":
|
||||
"&mut self.settings",
|
||||
"P:LibreMetaverse.GridClient.TimeProvider":
|
||||
"self.time_provider.clone()",
|
||||
"P:LibreMetaverse.GridClient.TimeProvider#set":
|
||||
"self.time_provider = value",
|
||||
"F:LibreMetaverse.Settings.BindAddress":
|
||||
"std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED)",
|
||||
"F:LibreMetaverse.Settings.LogLevel":
|
||||
"crate::logging::LogLevel(1)",
|
||||
"F:LibreMetaverse.Settings.MaxHttpConnections":
|
||||
"32",
|
||||
"F:LibreMetaverse.Settings.PacketArchiveSize":
|
||||
"1000",
|
||||
"F:LibreMetaverse.Settings.ResourceDir":
|
||||
'"linden".to_owned()',
|
||||
"F:LibreMetaverse.Settings.SimulatorPoolTimeout":
|
||||
"120_000",
|
||||
"F:LibreMetaverse.Settings.TexturePipelineRefreshInterval":
|
||||
"500.0",
|
||||
"F:LibreMetaverse.Settings.UdpReceiveQueueCapacity":
|
||||
"512",
|
||||
"F:LibreMetaverse.Settings.UserAgent":
|
||||
'"LibreMetaverse".to_owned()',
|
||||
"M:LibreMetaverse.Settings.#ctor(LibreMetaverse.GridClient)":
|
||||
"Ok(crate::client_core::Settings::defaults())",
|
||||
"P:LibreMetaverse.Settings.Agent":
|
||||
"self.agent.clone()",
|
||||
"P:LibreMetaverse.Settings.AssetCache":
|
||||
"self.asset_cache.clone()",
|
||||
"P:LibreMetaverse.Settings.Connection":
|
||||
"self.connection.clone()",
|
||||
"P:LibreMetaverse.Settings.Logging":
|
||||
"self.logging.clone()",
|
||||
"P:LibreMetaverse.Settings.Packets":
|
||||
"self.packets.clone()",
|
||||
"P:LibreMetaverse.Settings.Parcel":
|
||||
"self.parcel.clone()",
|
||||
"P:LibreMetaverse.Settings.TexturePipeline":
|
||||
"self.texture_pipeline.clone()",
|
||||
"P:LibreMetaverse.Settings.Timing":
|
||||
"&mut self.timing",
|
||||
"P:LibreMetaverse.Settings.UploadCost":
|
||||
"self.upload_cost",
|
||||
"P:LibreMetaverse.Settings.UploadCost#set":
|
||||
"self.upload_cost = value",
|
||||
"P:LibreMetaverse.Settings.World":
|
||||
"self.world.clone()",
|
||||
"M:LibreMetaverse.Avatar.#ctor":
|
||||
"crate::visual_catalog::new_avatar()",
|
||||
"M:LibreMetaverse.Avatar.DecodeVisualParams":
|
||||
@@ -243,6 +319,20 @@ NATIVE_MEMBER_BODIES = {
|
||||
"crate::byte_order::write_double_little_endian(dest, pos, value)",
|
||||
}
|
||||
|
||||
CLIENT_CORE_NATIVE_OWNERS = (
|
||||
"AgentSettings",
|
||||
"AssetCacheSettings",
|
||||
"ConnectionSettings",
|
||||
"GridClient",
|
||||
"LoggingSettings",
|
||||
"PacketSettings",
|
||||
"ParcelSettings",
|
||||
"Settings",
|
||||
"TexturePipelineSettings",
|
||||
"TimingSettings",
|
||||
"WorldSettings",
|
||||
)
|
||||
|
||||
NATIVE_OWNER_BOUNDS = {
|
||||
"T:LibreMetaverse.CacheDictionary`2": {
|
||||
"TKey": "TKey: Clone + PartialEq",
|
||||
@@ -617,11 +707,20 @@ PRIVATE_LAYOUTS = {
|
||||
"T:LibreMetaverse.StructuredData.OSDUri": [("value", "libremetaverse_types::compat::Uri")],
|
||||
}
|
||||
VALUE_DERIVES = {
|
||||
"T:LibreMetaverse.AgentSettings": "Clone, Debug, Eq, PartialEq",
|
||||
"T:LibreMetaverse.AssetCacheSettings": "Clone, Debug, Eq, PartialEq",
|
||||
"T:LibreMetaverse.ConnectionSettings": "Clone, Eq, PartialEq",
|
||||
"T:LibreMetaverse.LoggingSettings": "Clone, Debug, Eq, PartialEq",
|
||||
"T:LibreMetaverse.Matrix4": "Clone, Copy, Debug",
|
||||
"T:LibreMetaverse.Quaternion": "Clone, Copy, Debug",
|
||||
"T:LibreMetaverse.PacketSettings": "Clone, Debug, Eq, PartialEq",
|
||||
"T:LibreMetaverse.ParcelSettings": "Clone, Debug, Eq, PartialEq",
|
||||
"T:LibreMetaverse.TexturePipelineSettings": "Clone, Debug, Eq, PartialEq",
|
||||
"T:LibreMetaverse.TimingSettings": "Clone, Debug, Eq, PartialEq",
|
||||
"T:LibreMetaverse.UUID": "Clone, Copy, Debug, Default, Eq, Hash, PartialEq",
|
||||
"T:LibreMetaverse.Vector3": "Clone, Copy, Debug",
|
||||
"T:LibreMetaverse.Vector3d": "Clone, Copy, Debug",
|
||||
"T:LibreMetaverse.WorldSettings": "Clone, Debug, Eq, PartialEq",
|
||||
}
|
||||
def generic_names(item: dict) -> list[str]:
|
||||
return [parameter["name"] for parameter in item.get("generic_parameters", [])]
|
||||
@@ -950,7 +1049,12 @@ def render_body(signature: str, member_id: str, error_model: str, asyncness: str
|
||||
if trait:
|
||||
signature = signature.removeprefix("pub ")
|
||||
if body := native_member_body(member_id):
|
||||
return f" {signature} {{ {body} }}"
|
||||
marker = (
|
||||
" /* native client-core implementation */"
|
||||
if any(f"LibreMetaverse.{owner}." in member_id for owner in CLIENT_CORE_NATIVE_OWNERS)
|
||||
else ""
|
||||
)
|
||||
return f" {signature} {{{marker} {body} }}"
|
||||
failure = (
|
||||
f"libremetaverse_types::not_implemented({json.dumps(member_id)})"
|
||||
if error_model.startswith("Result")
|
||||
@@ -998,6 +1102,10 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
|
||||
native_declaration = NATIVE_DECLARATIONS.get(item["doc_id"])
|
||||
if native_declaration:
|
||||
lines.append(f"pub use {native_declaration} as {rust_name};")
|
||||
lines.extend(
|
||||
f"/// C# member: `{field['doc_id']}`."
|
||||
for field in fields
|
||||
)
|
||||
elif derives := VALUE_DERIVES.get(item["doc_id"]):
|
||||
lines.append(f"#[derive({derives})]")
|
||||
if native_declaration:
|
||||
@@ -1049,10 +1157,14 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
|
||||
for index, signature in enumerate(signatures):
|
||||
if index:
|
||||
lines.append(f" /// Setter for C# member: `{member['doc_id']}`.")
|
||||
body_member_id = member["doc_id"]
|
||||
setter_id = body_member_id + "#set"
|
||||
if index and setter_id in NATIVE_MEMBER_BODIES:
|
||||
body_member_id = setter_id
|
||||
lines.append(
|
||||
render_body(
|
||||
signature,
|
||||
member["doc_id"],
|
||||
body_member_id,
|
||||
row["error_model"],
|
||||
row["asyncness"],
|
||||
trait,
|
||||
@@ -1069,6 +1181,8 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
|
||||
# of receiving an invalid empty generated impl.
|
||||
if f"T:{base}" in NATIVE_TYPES:
|
||||
continue
|
||||
if item["doc_id"] == "T:LibreMetaverse.GridClient" and base == "LibreMetaverse.IGridClient":
|
||||
continue
|
||||
mapped_arguments = [mapper.type(argument, None, set(names)) for argument in arguments]
|
||||
trait_path = mapper.resolved[base] + generic_suffix(mapped_arguments)
|
||||
lines.append(f"impl{suffix} {trait_path} for {rust_name}{suffix} {{}}")
|
||||
@@ -1208,7 +1322,8 @@ def generate_sources(catalog: dict) -> tuple[dict[Path, str], dict[str, tuple[in
|
||||
raise ValueError(
|
||||
f"generated catalog ID mismatch for {name}: "
|
||||
f"types missing/stale={len(expected_types - emitted_types)}/{len(emitted_types - expected_types)}, "
|
||||
f"members missing/stale={len(expected_members - emitted_members)}/{len(emitted_members - expected_members)}"
|
||||
f"members missing/stale={len(expected_members - emitted_members)}/{len(emitted_members - expected_members)}; "
|
||||
f"missing={sorted(expected_members - emitted_members)}, stale={sorted(emitted_members - expected_members)}"
|
||||
)
|
||||
outputs[TARGETS.get(name, MAIN_TARGET)] = source
|
||||
coverage[name] = (len(selected), sum(len(item["members"]) for item in selected), len(selected) == len(assembly["types"]))
|
||||
@@ -1240,7 +1355,18 @@ def coverage_report(catalog: dict, coverage: dict[str, tuple[int, int, bool]]) -
|
||||
member["doc_id"]
|
||||
for item in assembly["types"]
|
||||
for member in item["members"]
|
||||
if item["doc_id"] in native_type_ids or native_member_body(member["doc_id"]) is not None
|
||||
if item["doc_id"] in NATIVE_TYPES
|
||||
or (name == "LibreMetaverse.Types" and item["kind"] == "enum")
|
||||
or (
|
||||
item["doc_id"] in NATIVE_DECLARATIONS
|
||||
and member["kind"] == "field"
|
||||
and not member.get("static")
|
||||
)
|
||||
or (
|
||||
item["doc_id"] in NATIVE_DECLARATIONS
|
||||
and member["kind"] == "constant"
|
||||
)
|
||||
or native_member_body(member["doc_id"]) is not None
|
||||
}
|
||||
if native_type_ids or native_member_ids:
|
||||
type_noun = "type" if len(native_type_ids) == 1 else "types"
|
||||
|
||||
Reference in New Issue
Block a user