Generate world and social API shims

This commit is contained in:
2026-08-08 12:44:47 +02:00
parent 376e63ac6d
commit 1291234e6f
7 changed files with 9099 additions and 314 deletions

View File

@@ -9,10 +9,10 @@ projects, public C# type names have Rust declarations, every upstream NUnit case
has a traceable placeholder, and every sample/tool project has a Rust binary has a traceable placeholder, and every sample/tool project has a Rust binary
target. Types, StructuredData, Imaging, PrimMesher, both rendering adapters, and target. Types, StructuredData, Imaging, PrimMesher, both rendering adapters, and
the main assembly's packet/message/asset/primitive wire-data, core the main assembly's packet/message/asset/primitive wire-data, core
runtime/networking, and avatar-facing manager slices now have complete callable, runtime/networking, avatar-facing manager, and world/social/service manager
failure-only signatures; the remaining world manager and extension slices plus slices now have complete callable, failure-only signatures; the remaining
semantic Rust test translations are still required before the public API shim extension slices plus semantic Rust test translations are still required before
is complete. the public API shim is complete.
The source snapshot, compatibility rules, dependency research, and ordered The source snapshot, compatibility rules, dependency research, and ordered
implementation plan are in [RUSTREWRITE.md](RUSTREWRITE.md). implementation plan are in [RUSTREWRITE.md](RUSTREWRITE.md).

View File

@@ -52,16 +52,17 @@ The current workspace is a structural baseline, not a working client:
matching independent ECMA-335 counts, with all 142 external signature types matching independent ECMA-335 counts, with all 142 external signature types
resolved to cross-platform Rust or project-owned boundary types; resolved to cross-platform Rust or project-owned boundary types;
- the completed Types, StructuredData, Imaging, PrimMesher, rendering, main - the completed Types, StructuredData, Imaging, PrimMesher, rendering, main
wire/data, core runtime/networking, and avatar-facing manager slices expose wire/data, core runtime/networking, avatar-facing manager, and
2,597 final Rust-facing types and all 27,191 callable members with real world/social/service manager slices expose 2,795 final Rust-facing types and
fields/constants/enum values and standardized failing bodies; all 28,768 callable members with real fields/constants/enum values and
standardized failing bodies;
- all 1,295 NUnit `[Test]`/`[TestCase]` invocations have compiling Rust test - all 1,295 NUnit `[Test]`/`[TestCase]` invocations have compiling Rust test
entries that intentionally panic with their source identity and body hash; entries that intentionally panic with their source identity and body hash;
- all nine sample/tool projects have compiling Rust binary targets; - all nine sample/tool projects have compiling Rust binary targets;
- the 128 TestClient command source files are retained as a command inventory. - the 128 TestClient command source files are retained as a command inventory.
The remaining 469 generated type shims preserve names and module placement, but The remaining 271 generated type shims preserve names and module placement, but
do not yet expose their 3,598 callable members. `api/SURFACE.tsv` is an inventory aid, do not yet expose their 2,021 callable members. `api/SURFACE.tsv` is an inventory aid,
not proof of API coverage: it records declaration lines but does not fully parse not proof of API coverage: it records declaration lines but does not fully parse
multiline signatures. Likewise, a generated `pending` test is a parity multiline signatures. Likewise, a generated `pending` test is a parity
placeholder, not a semantic translation. **This repository is therefore at placeholder, not a semantic translation. **This repository is therefore at
@@ -74,6 +75,13 @@ Long-running mapped calls retain an explicit cross-platform cancellation token
and return the shared `NotImplemented { csharp_member }` error; events return a and return the shared `NotImplemented { csharp_member }` error; events return a
typed subscription guard so callback lifetime is visible to Rust callers. typed subscription guard so callback lifetime is visible to Rust callers.
The world-facing slice preserves the C# object, parcel, terrain, grid,
environment, estate, directory, friend, group, sound, interest-list,
marketplace, and reporting concepts. Server-facing async operations retain
explicit cancellation and shared typed failures; request/reply events expose
typed payloads and subscription guards, while catalog constants and flag values
remain real.
No functional porting starts until both missing gates are complete across the No functional porting starts until both missing gates are complete across the
entire workspace, rather than one crate at a time: entire workspace, rather than one crate at a time:

View File

@@ -4,7 +4,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
| Assembly | Types | Members | Status | | Assembly | Types | Members | Status |
|---|---:|---:|---| |---|---:|---:|---|
| `LibreMetaverse` | 2,711 | 27,281 | partial callable shim (2,513 types / 25,704 members) | | `LibreMetaverse` | 2,711 | 27,281 | callable failure-only shim |
| `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | callable failure-only shim | | `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | callable failure-only shim |
| `LibreMetaverse.Imaging.Skia` | 1 | 3 | callable failure-only shim | | `LibreMetaverse.Imaging.Skia` | 1 | 3 | callable failure-only shim |
| `LibreMetaverse.LslTools` | 164 | 768 | pending milestone issue | | `LibreMetaverse.LslTools` | 164 | 768 | pending milestone issue |
@@ -18,14 +18,14 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
| `LibreMetaverse.Voice.Vivox` | 64 | 531 | pending milestone issue | | `LibreMetaverse.Voice.Vivox` | 64 | 531 | pending milestone issue |
| `LibreMetaverse.Voice.WebRTC` | 12 | 210 | pending milestone issue | | `LibreMetaverse.Voice.WebRTC` | 12 | 210 | pending milestone issue |
The partial `LibreMetaverse` row contains the completed issue #7 wire/data, issue #8 core The `LibreMetaverse` assembly is complete across issue #7 wire/data, issue #8 core runtime/networking,
runtime/networking, and issue #9 avatar-facing manager slices. World/social/service manager types issue #9 avatar-facing manager, and issue #10 world/social/service manager slices.
remain pending shells for issue #10.
| Main assembly slice | Types | Members | | Main assembly slice | Types | Members |
|---|---:|---:| |---|---:|---:|
| Issue #7 wire/data | 2,209 | 22,805 | | Issue #7 wire/data | 2,209 | 22,805 |
| Issue #8 core runtime/networking | 111 | 965 | | Issue #8 core runtime/networking | 111 | 965 |
| Issue #9 avatar-facing managers | 193 | 1,934 | | Issue #9 avatar-facing managers | 193 | 1,934 |
| Issue #10 world/social/service managers | 198 | 1,577 |
Current callable coverage: **2,597 types / 27,191 members**. Current callable coverage: **2,795 types / 28,768 members**.

View File

@@ -134,3 +134,5 @@ pub struct TypeId(pub &'static str);
pub struct Span<T>(pub PhantomData<fn(T)>); pub struct Span<T>(pub PhantomData<fn(T)>);
pub struct RandomSource; pub struct RandomSource;
pub struct IAsyncEnumerable<T>(pub PhantomData<fn() -> T>);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,86 @@
use libremetaverse::marketplace::{MarketplaceListing, MarketplaceManager};
use libremetaverse::{
DirectoryManager, DirectoryManagerAgentSearchData, EstateTask, EstateTools, FriendsManager,
GroupManager, GroupProfileEventArgs, ObjectManager, ParcelManager, ParcelSettings, Simulator,
TerrainManager, TerrainPatch,
};
use libremetaverse_types::UUID;
use libremetaverse_types::compat::{
CancellationToken, EventHandler, IAsyncEnumerable, Subscription,
};
fn member_id<T>(result: Result<T, libremetaverse::Error>) -> &'static str {
result
.err()
.expect("failure-only shim unexpectedly returned a value")
.csharp_member()
}
fn compile_search_call(directory: &DirectoryManager, token: CancellationToken) {
let _: Result<IAsyncEnumerable<DirectoryManagerAgentSearchData>, libremetaverse::Error> =
directory.search_people(String::new(), Some(token));
}
fn compile_group_and_friend_calls(
groups: &GroupManager,
friends: &FriendsManager,
group_id: UUID,
friend_id: UUID,
handler: EventHandler<GroupProfileEventArgs>,
) {
let _: Subscription = groups.subscribe_group_profile(handler);
let _ = groups.request_group_profile(group_id);
let _ = friends.offer_friendship_with_uuid(friend_id);
}
fn compile_parcel_call(parcels: &ParcelManager, parcel_id: UUID) {
let _ = parcels.request_parcel_info(parcel_id);
}
fn compile_object_call(objects: &ObjectManager, simulator: Simulator) {
let _ = objects.request_object(simulator, 1);
}
fn compile_estate_call(estate: &EstateTools) {
let _ = estate.request_info();
let _ = estate.request_top_scripts();
}
async fn compile_terrain_call(terrain: &TerrainManager, token: CancellationToken) {
let _ = terrain.get_terrain_material_overrides(Some(token)).await;
}
async fn compile_marketplace_call(marketplace: &MarketplaceManager, token: CancellationToken) {
let _ = marketplace.fetch_listings(Some(token)).await;
}
#[test]
fn world_and_social_flows_have_typed_callable_signatures() {
let _ = compile_search_call;
let _ = compile_group_and_friend_calls;
let _ = compile_parcel_call;
let _ = compile_object_call;
let _ = compile_estate_call;
let _ = compile_terrain_call;
let _ = compile_marketplace_call;
}
#[test]
fn world_facing_constructors_fail_with_catalog_ids() {
assert_eq!(
member_id(EstateTask::new()),
"M:LibreMetaverse.EstateTask.#ctor"
);
assert_eq!(
member_id(MarketplaceListing::new()),
"M:LibreMetaverse.Marketplace.MarketplaceListing.#ctor"
);
assert_eq!(
member_id(ParcelSettings::new()),
"M:LibreMetaverse.ParcelSettings.#ctor"
);
assert_eq!(
member_id(TerrainPatch::new()),
"M:LibreMetaverse.TerrainPatch.#ctor"
);
}

View File

@@ -384,6 +384,10 @@ def rust_constant_value(item: dict) -> str:
return json.dumps(value) return json.dumps(value)
if item["type"] == "System.Boolean": if item["type"] == "System.Boolean":
return value.lower() return value.lower()
if item["type"] in {"System.Single", "System.Double"} and not any(
marker in value.lower() for marker in (".", "e")
):
return value + ".0"
return value return value
@@ -500,6 +504,10 @@ def avatar_type(item: dict) -> bool:
) )
def world_type(item: dict) -> bool:
return not wire_type(item) and not core_type(item) and not avatar_type(item)
def render_modules(items: list[tuple[dict, dict, bool]], member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str: def render_modules(items: list[tuple[dict, dict, bool]], member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str:
tree: dict = {"items": [], "children": {}} tree: dict = {"items": [], "children": {}}
for item, type_row, complete in items: for item, type_row, complete in items:
@@ -558,7 +566,11 @@ def generate_sources(catalog: dict) -> tuple[dict[Path, str], dict[str, tuple[in
selected = ( selected = (
assembly["types"] assembly["types"]
if name in TARGETS if name in TARGETS
else [item for item in assembly["types"] if wire_type(item) or core_type(item) or avatar_type(item)] else [
item
for item in assembly["types"]
if wire_type(item) or core_type(item) or avatar_type(item) or world_type(item)
]
) )
selected_ids = {item["doc_id"] for item in selected} selected_ids = {item["doc_id"] for item in selected}
rendered_items = [ rendered_items = [
@@ -602,7 +614,7 @@ def coverage_report(catalog: dict, coverage: dict[str, tuple[int, int, bool]]) -
else: else:
status = "pending milestone issue" status = "pending milestone issue"
lines.append(f"| `{name}` | {types:,} | {members:,} | {status} |") lines.append(f"| `{name}` | {types:,} | {members:,} | {status} |")
if "LibreMetaverse" in coverage and not coverage["LibreMetaverse"][2]: if "LibreMetaverse" in coverage:
main_types = next( main_types = next(
assembly["types"] assembly["types"]
for assembly in catalog["assemblies"] for assembly in catalog["assemblies"]
@@ -615,17 +627,27 @@ def coverage_report(catalog: dict, coverage: dict[str, tuple[int, int, bool]]) -
for item in main_types for item in main_types
if avatar_type(item) and not wire_type(item) and not core_type(item) if avatar_type(item) and not wire_type(item) and not core_type(item)
] ]
world_types = [item for item in main_types if world_type(item)]
complete = coverage["LibreMetaverse"][2]
lines += [ lines += [
"", "",
"The partial `LibreMetaverse` row contains the completed issue #7 wire/data, issue #8 core", (
"runtime/networking, and issue #9 avatar-facing manager slices. World/social/service manager types", "The `LibreMetaverse` assembly is complete across issue #7 wire/data, issue #8 core runtime/networking,"
"remain pending shells for issue #10.", if complete
else "The partial `LibreMetaverse` row contains the completed issue #7 wire/data, issue #8 core runtime/networking,"
),
(
"issue #9 avatar-facing manager, and issue #10 world/social/service manager slices."
if complete
else "and issue #9 avatar-facing manager slices. World/social/service manager types remain pending for issue #10."
),
"", "",
"| Main assembly slice | Types | Members |", "| Main assembly slice | Types | Members |",
"|---|---:|---:|", "|---|---:|---:|",
f"| Issue #7 wire/data | {len(wire_types):,} | {sum(len(item['members']) for item in wire_types):,} |", f"| Issue #7 wire/data | {len(wire_types):,} | {sum(len(item['members']) for item in wire_types):,} |",
f"| Issue #8 core runtime/networking | {len(core_types):,} | {sum(len(item['members']) for item in core_types):,} |", f"| Issue #8 core runtime/networking | {len(core_types):,} | {sum(len(item['members']) for item in core_types):,} |",
f"| Issue #9 avatar-facing managers | {len(avatar_types):,} | {sum(len(item['members']) for item in avatar_types):,} |", f"| Issue #9 avatar-facing managers | {len(avatar_types):,} | {sum(len(item['members']) for item in avatar_types):,} |",
f"| Issue #10 world/social/service managers | {len(world_types):,} | {sum(len(item['members']) for item in world_types):,} |",
] ]
lines += ["", f"Current callable coverage: **{total_types:,} types / {total_members:,} members**.", ""] lines += ["", f"Current callable coverage: **{total_types:,} types / {total_members:,} members**.", ""]
return "\n".join(lines) return "\n".join(lines)