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
target. Types, StructuredData, Imaging, PrimMesher, both rendering adapters, and
the main assembly's packet/message/asset/primitive wire-data, core
runtime/networking, and avatar-facing manager slices now have complete callable,
failure-only signatures; the remaining world manager and extension slices plus
semantic Rust test translations are still required before the public API shim
is complete.
runtime/networking, avatar-facing manager, and world/social/service manager
slices now have complete callable, failure-only signatures; the remaining
extension slices plus semantic Rust test translations are still required before
the public API shim is complete.
The source snapshot, compatibility rules, dependency research, and ordered
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
resolved to cross-platform Rust or project-owned boundary types;
- the completed Types, StructuredData, Imaging, PrimMesher, rendering, main
wire/data, core runtime/networking, and avatar-facing manager slices expose
2,597 final Rust-facing types and all 27,191 callable members with real
fields/constants/enum values and standardized failing bodies;
wire/data, core runtime/networking, avatar-facing manager, and
world/social/service manager slices expose 2,795 final Rust-facing types and
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
entries that intentionally panic with their source identity and body hash;
- all nine sample/tool projects have compiling Rust binary targets;
- the 128 TestClient command source files are retained as a command inventory.
The remaining 469 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,
The remaining 271 generated type shims preserve names and module placement, but
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
multiline signatures. Likewise, a generated `pending` test is a parity
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
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
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 |
|---|---:|---:|---|
| `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.Skia` | 1 | 3 | callable failure-only shim |
| `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.WebRTC` | 12 | 210 | pending milestone issue |
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
remain pending shells for issue #10.
The `LibreMetaverse` assembly is complete across issue #7 wire/data, issue #8 core runtime/networking,
issue #9 avatar-facing manager, and issue #10 world/social/service manager slices.
| Main assembly slice | Types | Members |
|---|---:|---:|
| Issue #7 wire/data | 2,209 | 22,805 |
| Issue #8 core runtime/networking | 111 | 965 |
| 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 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)
if item["type"] == "System.Boolean":
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
@@ -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:
tree: dict = {"items": [], "children": {}}
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 = (
assembly["types"]
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}
rendered_items = [
@@ -602,7 +614,7 @@ def coverage_report(catalog: dict, coverage: dict[str, tuple[int, int, bool]]) -
else:
status = "pending milestone issue"
lines.append(f"| `{name}` | {types:,} | {members:,} | {status} |")
if "LibreMetaverse" in coverage and not coverage["LibreMetaverse"][2]:
if "LibreMetaverse" in coverage:
main_types = next(
assembly["types"]
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
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 += [
"",
"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",
"remain pending shells for issue #10.",
(
"The `LibreMetaverse` assembly is complete across issue #7 wire/data, issue #8 core runtime/networking,"
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 |",
"|---|---:|---:|",
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 #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**.", ""]
return "\n".join(lines)