Complete Rust API and migration documentation (#102)
Some checks failed
Native code generation / deterministic (push) Failing after 2m5s
Concurrency and resource soak audit / soak (push) Failing after 6m22s
Documentation / documentation (push) Failing after 1m25s
Imaging and meshing gate / native (push) Failing after 2m47s
JPEG 2000 feature / linux (push) Successful in 3m54s
Release platform and feature matrix / audit (push) Successful in 37s
Native Rust workspace compile / compile (push) Failing after 1m14s
Skia feature / linux (push) Successful in 30m46s
Dependency and supply-chain audit / audit (push) Failing after 8m56s
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Failing after 9m16s
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Failing after 1m20s
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Failing after 1m18s
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Some checks failed
Native code generation / deterministic (push) Failing after 2m5s
Concurrency and resource soak audit / soak (push) Failing after 6m22s
Documentation / documentation (push) Failing after 1m25s
Imaging and meshing gate / native (push) Failing after 2m47s
JPEG 2000 feature / linux (push) Successful in 3m54s
Release platform and feature matrix / audit (push) Successful in 37s
Native Rust workspace compile / compile (push) Failing after 1m14s
Skia feature / linux (push) Successful in 30m46s
Dependency and supply-chain audit / audit (push) Failing after 8m56s
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Failing after 9m16s
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Failing after 1m20s
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Failing after 1m18s
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
This commit is contained in:
88
.gitea/workflows/documentation.yml
Normal file
88
.gitea/workflows/documentation.yml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
name: Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/documentation.yml"
|
||||||
|
- "Cargo.lock"
|
||||||
|
- "Cargo.toml"
|
||||||
|
- "README.md"
|
||||||
|
- "api/**"
|
||||||
|
- "crates/**"
|
||||||
|
- "docs/**"
|
||||||
|
- "programs/**"
|
||||||
|
- "tools/ci-matrix/**"
|
||||||
|
- "tools/generate_api_shims.py"
|
||||||
|
- "tools/install_openjpeg_2_5_4.sh"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/documentation.yml"
|
||||||
|
- "Cargo.lock"
|
||||||
|
- "Cargo.toml"
|
||||||
|
- "README.md"
|
||||||
|
- "api/**"
|
||||||
|
- "crates/**"
|
||||||
|
- "docs/**"
|
||||||
|
- "programs/**"
|
||||||
|
- "tools/ci-matrix/**"
|
||||||
|
- "tools/generate_api_shims.py"
|
||||||
|
- "tools/install_openjpeg_2_5_4.sh"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
documentation:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
env:
|
||||||
|
OPENJPEG_PREFIX: /tmp/metacrate-openjpeg-${{ github.run_id }}
|
||||||
|
PKG_CONFIG_PATH: /tmp/metacrate-openjpeg-${{ github.run_id }}/lib/pkgconfig
|
||||||
|
LD_LIBRARY_PATH: /tmp/metacrate-openjpeg-${{ github.run_id }}/lib
|
||||||
|
FORCE_SKIA_BINARIES_DOWNLOAD: "1"
|
||||||
|
CARGO_BUILD_JOBS: 1
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install native documentation prerequisites
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --yes build-essential clang cmake curl ninja-build \
|
||||||
|
pkg-config python3 libfontconfig1-dev libfreetype6-dev libopus-dev
|
||||||
|
tools/install_openjpeg_2_5_4.sh "$OPENJPEG_PREFIX"
|
||||||
|
|
||||||
|
- name: Install Rust 1.97 toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
toolchain: 1.97.0
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
- name: Verify documentation coverage report
|
||||||
|
run: |
|
||||||
|
cargo run --locked -p metacrate-ci-matrix -- documentation-report
|
||||||
|
git diff --exit-code -- api/DOCUMENTATION-COVERAGE.md
|
||||||
|
cargo run --locked -p metacrate-ci-matrix -- documentation-audit --evidence ci/evidence/documentation-audit.json
|
||||||
|
|
||||||
|
- name: Build warning-free workspace documentation
|
||||||
|
env:
|
||||||
|
RUSTDOCFLAGS: "-D warnings"
|
||||||
|
run: cargo doc --workspace --no-deps --locked -j 1
|
||||||
|
|
||||||
|
- name: Compile guide snippets and examples
|
||||||
|
run: |
|
||||||
|
cargo test --locked -p libremetaverse --doc -j 1
|
||||||
|
cargo test --locked -p libremetaverse --examples -j 1
|
||||||
|
cargo run --locked -p libremetaverse --example offline_client
|
||||||
|
cargo run --locked -p libremetaverse --example cancellation
|
||||||
|
|
||||||
|
- name: Verify documented program quick starts
|
||||||
|
run: cargo test --locked -p libremetaverse-programs --tests -j 1
|
||||||
|
|
||||||
|
- name: Upload documentation evidence
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: documentation-evidence
|
||||||
|
path: ci/evidence/documentation-audit.json
|
||||||
|
if-no-files-found: error
|
||||||
33
README.md
33
README.md
@@ -1,20 +1,23 @@
|
|||||||
# MetaCrate
|
# MetaCrate
|
||||||
|
|
||||||
|
The [Rust API and C# migration guide](docs/rust-api-guide.md) explains crate
|
||||||
|
selection, ownership, async cancellation, errors, events, native prerequisites,
|
||||||
|
and credential-safe OpenSim operation. The generated
|
||||||
|
[documentation coverage report](api/DOCUMENTATION-COVERAGE.md) ties the public
|
||||||
|
Rust surface back to the pinned C# type and member mappings.
|
||||||
|
|
||||||
MetaCrate is a test-driven, clean native Rust reimplementation of
|
MetaCrate is a test-driven, clean native Rust reimplementation of
|
||||||
[LibreMetaverse](https://github.com/cinderblocks/libremetaverse). LibreMetaverse
|
[LibreMetaverse](https://github.com/cinderblocks/libremetaverse). LibreMetaverse
|
||||||
is a behavioral and API reference only: the finished project must not load,
|
is a behavioral and API reference only: the finished project must not load,
|
||||||
host, bind to, invoke, or ship the .NET implementation. The current
|
host, bind to, invoke, or ship the .NET implementation. The project is in
|
||||||
stage is a compiling structural shell: crate boundaries mirror the .NET library
|
release hardening: crate boundaries mirror the .NET libraries, every mapped
|
||||||
projects, public C# type names have Rust declarations, every upstream NUnit
|
public type and member has a documented native Rust destination, and every
|
||||||
invocation has a traceable catalog entry, and every sample/tool project has a
|
sample/tool project has a tested Rust binary target. Types, structured data,
|
||||||
Rust binary target. Types, StructuredData, Imaging, PrimMesher, the MeshFoundry rendering adapter, and
|
imaging, meshing/rendering, packet and message codecs, client/network runtime,
|
||||||
the main assembly's packet/message/asset/primitive wire-data, core
|
avatar/world/social/service managers, RLV, LSL tools, utilities, Vivox, and
|
||||||
runtime/networking, avatar-facing manager, world/social/service manager, RLV,
|
WebRTC are implemented without a CLR bridge. The independent downstream
|
||||||
LSL tools, Utilities, Vivox, and WebRTC slices now have complete callable,
|
fixture compiles every cataloged type and member with zero exclusions, and all
|
||||||
failure-only signatures. The independent downstream fixture compiles every
|
1,289 upstream NUnit invocations have reviewed Rust parity cases.
|
||||||
cataloged type and member with zero exclusions, and all 1,289 NUnit invocations
|
|
||||||
now have reviewed Rust parity cases. Production behavior implementation is the
|
|
||||||
next stage.
|
|
||||||
|
|
||||||
A case counts as translated only when an explicit Rust test body calls the
|
A case counts as translated only when an explicit Rust test body calls the
|
||||||
mapped API and retains the upstream identity and body hash. The parity ledger
|
mapped API and retains the upstream identity and body hash. The parity ledger
|
||||||
@@ -28,9 +31,9 @@ The authoritative compiled .NET surface is checked in as
|
|||||||
reviewed Rust destination in [`api/RUST-TYPES.tsv`](api/RUST-TYPES.tsv) and
|
reviewed Rust destination in [`api/RUST-TYPES.tsv`](api/RUST-TYPES.tsv) and
|
||||||
[`api/RUST-MAPPING.tsv`](api/RUST-MAPPING.tsv); [`api/README.md`](api/README.md)
|
[`api/RUST-MAPPING.tsv`](api/RUST-MAPPING.tsv); [`api/README.md`](api/README.md)
|
||||||
documents deterministic regeneration and validation.
|
documents deterministic regeneration and validation.
|
||||||
All API crates expose the same typed `Error` for fallible unimplemented members;
|
API mappings record ownership, async, error, overload, and native implementation
|
||||||
infallible shims use `libremetaverse_types::unimplemented_api!` so placeholders
|
decisions, and the generated rustdoc keeps those contracts attached to their
|
||||||
cannot look like successful behavior.
|
exact C# documentation IDs.
|
||||||
|
|
||||||
Building MetaCrate requires Rust 1.96 or newer.
|
Building MetaCrate requires Rust 1.96 or newer.
|
||||||
|
|
||||||
|
|||||||
14
api/DOCUMENTATION-COVERAGE.md
Normal file
14
api/DOCUMENTATION-COVERAGE.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Documentation coverage
|
||||||
|
|
||||||
|
Generated by `metacrate-ci-matrix documentation-report`; do not edit by hand.
|
||||||
|
|
||||||
|
| Surface | Documented | Required | Coverage |
|
||||||
|
| --- | ---: | ---: | ---: |
|
||||||
|
| Publishable public crates | 15 | 15 | 100% |
|
||||||
|
| Pinned LibreMetaverse public types | 3066 | 3066 | 100% |
|
||||||
|
| Mapped public members | 30789 | 30789 | 100% |
|
||||||
|
| Compiled Rust guide snippets | 5 | 4 minimum | pass |
|
||||||
|
| Linked native programs | 9 | 9 | 100% |
|
||||||
|
| Checked local Markdown links | 65 | 65 | 100% |
|
||||||
|
|
||||||
|
Every mapped item is tied to its exact C# documentation ID and to the ownership, asyncness, error, overload, mapping-kind, and Rust-signature decisions in [`RUST-MAPPING.tsv`](RUST-MAPPING.tsv). Public types are tied to the corresponding type mapping. The upstream source is pinned to [`2aa70bb68513b39795da5d13c88f31b86e85a3ba`](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
14
ci/evidence/documentation-audit.json
Normal file
14
ci/evidence/documentation-audit.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
|
||||||
|
"public_crates": 15,
|
||||||
|
"mapped_public_types": 3066,
|
||||||
|
"documented_public_types": 3066,
|
||||||
|
"mapped_members": 30789,
|
||||||
|
"documented_members": 30789,
|
||||||
|
"tested_rust_snippets": 5,
|
||||||
|
"linked_programs": 9,
|
||||||
|
"checked_local_links": 65,
|
||||||
|
"required_guide_sections": 11,
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -6,7 +6,22 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Imaging.Skia.SkiaTextureCodec`.
|
/// C# type: `T:LibreMetaverse.Imaging.Skia.SkiaTextureCodec`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Imaging.Skia.SkiaTextureCodec` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.#ctor`.
|
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.#ctor`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.Skia.SkiaTextureCodec()`.
|
||||||
|
/// Mapping contract: ownership `none`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.Decode(System.IO.Stream)`.
|
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.Decode(System.IO.Stream)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage LibreMetaverse.Imaging.Skia.SkiaTextureCodec.Decode(System.IO.Stream stream)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.ToManagedImage(SkiaSharp.SKBitmap)`.
|
/// C# member: `M:LibreMetaverse.Imaging.Skia.SkiaTextureCodec.ToManagedImage(SkiaSharp.SKBitmap)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage LibreMetaverse.Imaging.Skia.SkiaTextureCodec.ToManagedImage(SkiaSharp.SKBitmap bitmap)`.
|
||||||
|
/// Mapping contract: ownership `owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub use crate::skia_codec::SkiaTextureCodec;
|
pub use crate::skia_codec::SkiaTextureCodec;
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
//! Skia imaging adapter corresponding to `LibreMetaverse.Imaging.Skia`.
|
//! Optional Skia imaging adapter corresponding to `LibreMetaverse.Imaging.Skia`.
|
||||||
|
//!
|
||||||
|
//! Enable `skia` for bounded `BMP`, `GIF`, `ICO`, `JPEG`, `PNG`, `WBMP`, and
|
||||||
|
//! `WebP` decoding.
|
||||||
|
//! The default build remains inert. See the crate README for native cache,
|
||||||
|
//! source-build, licensing, and cross-platform prerequisite details.
|
||||||
|
|
||||||
extern crate self as libremetaverse_imaging_skia;
|
extern crate self as libremetaverse_imaging_skia;
|
||||||
|
|
||||||
|
|||||||
@@ -6,30 +6,119 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Imaging.ITextureCodec`.
|
/// C# type: `T:LibreMetaverse.Imaging.ITextureCodec`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Imaging.ITextureCodec` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ITextureCodec.Decode(System.IO.Stream)`.
|
/// C# member: `M:LibreMetaverse.Imaging.ITextureCodec.Decode(System.IO.Stream)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage LibreMetaverse.Imaging.ITextureCodec.Decode(System.IO.Stream stream)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub use crate::managed_image::ITextureCodec;
|
pub use crate::managed_image::ITextureCodec;
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Imaging.ManagedImage`.
|
/// C# type: `T:LibreMetaverse.Imaging.ManagedImage`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Imaging.ManagedImage` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Alpha`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Alpha`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Byte[] LibreMetaverse.Imaging.ManagedImage.Alpha`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Blue`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Blue`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Byte[] LibreMetaverse.Imaging.ManagedImage.Blue`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Bump`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Bump`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Byte[] LibreMetaverse.Imaging.ManagedImage.Bump`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Channels`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Channels`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage.ImageChannels LibreMetaverse.Imaging.ManagedImage.Channels`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Green`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Green`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Byte[] LibreMetaverse.Imaging.ManagedImage.Green`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Height`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Height`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Int32 LibreMetaverse.Imaging.ManagedImage.Height`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Red`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Red`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Byte[] LibreMetaverse.Imaging.ManagedImage.Red`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Width`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.Width`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Int32 LibreMetaverse.Imaging.ManagedImage.Width`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.#ctor(System.Int32,System.Int32,LibreMetaverse.Imaging.ManagedImage.ImageChannels)`.
|
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.#ctor(System.Int32,System.Int32,LibreMetaverse.Imaging.ManagedImage.ImageChannels)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage(System.Int32 width, System.Int32 height, LibreMetaverse.Imaging.ManagedImage.ImageChannels channels)`.
|
||||||
|
/// Mapping contract: ownership `owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.Clear`.
|
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.Clear`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Imaging.ManagedImage.Clear()`.
|
||||||
|
/// Mapping contract: ownership `mutable_self`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.Clone`.
|
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.Clone`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage LibreMetaverse.Imaging.ManagedImage.Clone()`.
|
||||||
|
/// Mapping contract: ownership `shared_self`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ConvertChannels(LibreMetaverse.Imaging.ManagedImage.ImageChannels)`.
|
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ConvertChannels(LibreMetaverse.Imaging.ManagedImage.ImageChannels)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Imaging.ManagedImage.ConvertChannels(LibreMetaverse.Imaging.ManagedImage.ImageChannels channels)`.
|
||||||
|
/// Mapping contract: ownership `mutable_self,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ExportRaw`.
|
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ExportRaw`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Byte[] LibreMetaverse.Imaging.ManagedImage.ExportRaw()`.
|
||||||
|
/// Mapping contract: ownership `shared_self`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ResizeBilinear(System.Int32,System.Int32)`.
|
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ResizeBilinear(System.Int32,System.Int32)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Imaging.ManagedImage.ResizeBilinear(System.Int32 width, System.Int32 height)`.
|
||||||
|
/// Mapping contract: ownership `mutable_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ResizeNearestNeighbor(System.Int32,System.Int32)`.
|
/// C# member: `M:LibreMetaverse.Imaging.ManagedImage.ResizeNearestNeighbor(System.Int32,System.Int32)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Imaging.ManagedImage.ResizeNearestNeighbor(System.Int32 width, System.Int32 height)`.
|
||||||
|
/// Mapping contract: ownership `mutable_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub use crate::managed_image::ManagedImage;
|
pub use crate::managed_image::ManagedImage;
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Imaging.ManagedImage.ImageChannels`.
|
/// C# type: `T:LibreMetaverse.Imaging.ManagedImage.ImageChannels`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Imaging.ManagedImage.ImageChannels` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Alpha`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Alpha`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage.ImageChannels LibreMetaverse.Imaging.ManagedImage.ImageChannels.Alpha`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Bump`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Bump`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage.ImageChannels LibreMetaverse.Imaging.ManagedImage.ImageChannels.Bump`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Color`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Color`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage.ImageChannels LibreMetaverse.Imaging.ManagedImage.ImageChannels.Color`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Gray`.
|
/// C# member: `F:LibreMetaverse.Imaging.ManagedImage.ImageChannels.Gray`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Imaging.ManagedImage.ImageChannels LibreMetaverse.Imaging.ManagedImage.ImageChannels.Gray`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
pub use crate::managed_image::ManagedImageImageChannels;
|
pub use crate::managed_image::ManagedImageImageChannels;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
//! Imaging abstractions corresponding to `LibreMetaverse.Imaging.Abstractions`.
|
//! Image and texture abstractions corresponding to `LibreMetaverse.Imaging`.
|
||||||
|
//!
|
||||||
|
//! The default surface provides managed images plus native `TGA`/`DDS` handling.
|
||||||
|
//! Enable `jpeg2000` only when the audited system `OpenJPEG` adapter is available;
|
||||||
|
//! ordinary users can consume the traits without a native image dependency.
|
||||||
|
|
||||||
extern crate self as libremetaverse_imaging;
|
extern crate self as libremetaverse_imaging;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,8 @@
|
|||||||
//! LSL parsing tools corresponding to `LibreMetaverse.LslTools`.
|
//! Native `LSL` tooling corresponding to `LibreMetaverse.LslTools`.
|
||||||
|
//!
|
||||||
|
//! This crate provides the bounded lexer, parser, diagnostics, syntax model,
|
||||||
|
//! and deterministic source generation used by editor and migration tooling.
|
||||||
|
//! It is pure Rust and does not require a grid connection.
|
||||||
|
|
||||||
extern crate self as libremetaverse_lsl_tools;
|
extern crate self as libremetaverse_lsl_tools;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,8 @@
|
|||||||
//! Primitive mesher corresponding to `LibreMetaverse.PrimMesher`.
|
//! Native primitive mesher corresponding to `LibreMetaverse.PrimMesher`.
|
||||||
|
//!
|
||||||
|
//! Use it to turn checked legacy prim and sculpt parameters into deterministic
|
||||||
|
//! geometry and `OBJ` output. Input bounds and topology validation apply before
|
||||||
|
//! allocation; no renderer, grid session, or native library is required.
|
||||||
|
|
||||||
extern crate self as libremetaverse_prim_mesher;
|
extern crate self as libremetaverse_prim_mesher;
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,24 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Rendering.MeshFoundry`.
|
/// C# type: `T:LibreMetaverse.Rendering.MeshFoundry`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Rendering.MeshFoundry` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
pub use crate::mesh_foundry::MeshFoundry;
|
pub use crate::mesh_foundry::MeshFoundry;
|
||||||
impl MeshFoundry {
|
impl MeshFoundry {
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.#ctor`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.#ctor`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.MeshFoundry()`.
|
||||||
|
/// Mapping contract: ownership `none`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn new() -> Result<Self, crate::Error> {
|
pub fn new() -> Result<Self, crate::Error> {
|
||||||
Self::native_new()
|
Self::native_new()
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.FacetedMesh LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_faceted_mesh(
|
pub fn generate_faceted_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -21,6 +32,10 @@ impl MeshFoundry {
|
|||||||
self.native_generate_faceted_mesh(prim, lod)
|
self.native_generate_faceted_mesh(prim, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMeshMesh(LibreMetaverse.Primitive,System.Byte[])`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMeshMesh(LibreMetaverse.Primitive,System.Byte[])`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.FacetedMesh LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMeshMesh(LibreMetaverse.Primitive prim, System.Byte[] meshData)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn generate_faceted_mesh_mesh_with_primitive_bytes(
|
pub fn generate_faceted_mesh_mesh_with_primitive_bytes(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -33,6 +48,10 @@ impl MeshFoundry {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMeshMesh(LibreMetaverse.Primitive,System.Byte[],LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMeshMesh(LibreMetaverse.Primitive,System.Byte[],LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.FacetedMesh LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedMeshMesh(LibreMetaverse.Primitive prim, System.Byte[] meshData, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn generate_faceted_mesh_mesh_with_primitive_bytes_detail_level(
|
pub fn generate_faceted_mesh_mesh_with_primitive_bytes_detail_level(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -42,6 +61,10 @@ impl MeshFoundry {
|
|||||||
self.native_generate_faceted_mesh_mesh(prim, mesh_data, lod)
|
self.native_generate_faceted_mesh_mesh(prim, mesh_data, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.FacetedMesh LibreMetaverse.Rendering.MeshFoundry.GenerateFacetedSculptMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Imaging.ManagedImage sculptTexture, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_faceted_sculpt_mesh(
|
pub fn generate_faceted_sculpt_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -51,6 +74,10 @@ impl MeshFoundry {
|
|||||||
self.native_generate_faceted_sculpt_mesh(prim, sculpt_texture, lod)
|
self.native_generate_faceted_sculpt_mesh(prim, sculpt_texture, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.SimpleMesh LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_simple_mesh(
|
pub fn generate_simple_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -59,6 +86,10 @@ impl MeshFoundry {
|
|||||||
self.native_generate_simple_mesh(prim, lod, false)
|
self.native_generate_simple_mesh(prim, lod, false)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleMeshWithNormals(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleMeshWithNormals(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.SimpleMesh LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleMeshWithNormals(LibreMetaverse.Primitive prim, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_simple_mesh_with_normals(
|
pub fn generate_simple_mesh_with_normals(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -67,6 +98,10 @@ impl MeshFoundry {
|
|||||||
self.native_generate_simple_mesh(prim, lod, true)
|
self.native_generate_simple_mesh(prim, lod, true)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.SimpleMesh LibreMetaverse.Rendering.MeshFoundry.GenerateSimpleSculptMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Imaging.ManagedImage sculptTexture, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_simple_sculpt_mesh(
|
pub fn generate_simple_sculpt_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -76,6 +111,10 @@ impl MeshFoundry {
|
|||||||
self.native_generate_simple_sculpt_mesh(prim, sculpt_texture, lod)
|
self.native_generate_simple_sculpt_mesh(prim, sculpt_texture, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsConvexHulls(LibreMetaverse.Primitive,System.Byte[])`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsConvexHulls(LibreMetaverse.Primitive,System.Byte[])`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Collections.Generic.List<System.Collections.Generic.List<LibreMetaverse.Vector3>> LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsConvexHulls(LibreMetaverse.Primitive prim, System.Byte[] compressedMeshData)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn mesh_sub_mesh_as_convex_hulls_with_primitive_bytes(
|
pub fn mesh_sub_mesh_as_convex_hulls_with_primitive_bytes(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -84,6 +123,10 @@ impl MeshFoundry {
|
|||||||
self.native_mesh_sub_mesh_as_convex_hulls(prim, compressed_mesh_data, None)
|
self.native_mesh_sub_mesh_as_convex_hulls(prim, compressed_mesh_data, None)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsConvexHulls(LibreMetaverse.Primitive,System.Byte[],System.Collections.Generic.List{LibreMetaverse.Vector3}@)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsConvexHulls(LibreMetaverse.Primitive,System.Byte[],System.Collections.Generic.List{LibreMetaverse.Vector3}@)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Collections.Generic.List<System.Collections.Generic.List<LibreMetaverse.Vector3>> LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsConvexHulls(LibreMetaverse.Primitive prim, System.Byte[] compressedMeshData, out System.Collections.Generic.List<LibreMetaverse.Vector3>& boundingHull)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned,mutable_output`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn mesh_sub_mesh_as_convex_hulls_with_primitive_bytes_list(
|
pub fn mesh_sub_mesh_as_convex_hulls_with_primitive_bytes_list(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -93,6 +136,10 @@ impl MeshFoundry {
|
|||||||
self.native_mesh_sub_mesh_as_convex_hulls(prim, compressed_mesh_data, Some(bounding_hull))
|
self.native_mesh_sub_mesh_as_convex_hulls(prim, compressed_mesh_data, Some(bounding_hull))
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsSimpleMesh(LibreMetaverse.Primitive,System.Byte[])`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsSimpleMesh(LibreMetaverse.Primitive,System.Byte[])`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.SimpleMesh LibreMetaverse.Rendering.MeshFoundry.MeshSubMeshAsSimpleMesh(LibreMetaverse.Primitive prim, System.Byte[] compressedMeshData)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn mesh_sub_mesh_as_simple_mesh(
|
pub fn mesh_sub_mesh_as_simple_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -101,6 +148,10 @@ impl MeshFoundry {
|
|||||||
self.native_mesh_sub_mesh_as_simple_mesh(prim, compressed_mesh_data)
|
self.native_mesh_sub_mesh_as_simple_mesh(prim, compressed_mesh_data)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.TerrainMesh(System.Single[0:,0:],System.Single,System.Single,System.Single,System.Single)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.TerrainMesh(System.Single[0:,0:],System.Single,System.Single,System.Single,System.Single)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.Face LibreMetaverse.Rendering.MeshFoundry.TerrainMesh(System.Single[,] zMap, System.Single xBegin, System.Single xEnd, System.Single yBegin, System.Single yEnd)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn terrain_mesh(
|
pub fn terrain_mesh(
|
||||||
&self,
|
&self,
|
||||||
z_map: Vec<Vec<f32>>,
|
z_map: Vec<Vec<f32>>,
|
||||||
@@ -112,6 +163,10 @@ impl MeshFoundry {
|
|||||||
self.native_terrain_mesh(z_map, x_begin, x_end, y_begin, y_end)
|
self.native_terrain_mesh(z_map, x_begin, x_end, y_begin, y_end)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.TransformTexCoords(System.Collections.Generic.List{LibreMetaverse.Rendering.Vertex},LibreMetaverse.Vector3,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Vector3)`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.TransformTexCoords(System.Collections.Generic.List{LibreMetaverse.Rendering.Vertex},LibreMetaverse.Vector3,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Vector3)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Rendering.MeshFoundry.TransformTexCoords(System.Collections.Generic.List<LibreMetaverse.Rendering.Vertex> vertices, LibreMetaverse.Vector3 center, LibreMetaverse.Primitive.TextureEntryFace teFace, LibreMetaverse.Vector3 primScale)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,mutable_borrow,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn transform_tex_coords(
|
pub fn transform_tex_coords(
|
||||||
&self,
|
&self,
|
||||||
vertices: &mut Vec<libremetaverse::rendering::Vertex>,
|
vertices: &mut Vec<libremetaverse::rendering::Vertex>,
|
||||||
@@ -122,6 +177,10 @@ impl MeshFoundry {
|
|||||||
self.native_transform_tex_coords(vertices, center, te_face, prim_scale)
|
self.native_transform_tex_coords(vertices, center, te_face, prim_scale)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.UnpackMesh(System.Byte[])`.
|
/// C# member: `M:LibreMetaverse.Rendering.MeshFoundry.UnpackMesh(System.Byte[])`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.StructuredData.OSDMap LibreMetaverse.Rendering.MeshFoundry.UnpackMesh(System.Byte[] assetData)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn unpack_mesh(
|
pub fn unpack_mesh(
|
||||||
&self,
|
&self,
|
||||||
asset_data: Vec<u8>,
|
asset_data: Vec<u8>,
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
//! Bounded native prim, sculpt, terrain, and LLSD mesh-asset rendering.
|
//! Bounded native prim, sculpt, terrain, and `LLSD` mesh-asset rendering.
|
||||||
|
//!
|
||||||
|
//! `MeshFoundry` consumes public meshing/imaging abstractions and produces checked
|
||||||
|
//! render meshes without a graphics API. The crate `README` documents topology,
|
||||||
|
//! material, coordinate, allocation, and migration behavior.
|
||||||
|
|
||||||
extern crate self as libremetaverse_rendering_mesh_foundry;
|
extern crate self as libremetaverse_rendering_mesh_foundry;
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,24 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Rendering.SimpleRenderer`.
|
/// C# type: `T:LibreMetaverse.Rendering.SimpleRenderer`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Rendering.SimpleRenderer` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
pub use crate::simple_renderer::SimpleRenderer;
|
pub use crate::simple_renderer::SimpleRenderer;
|
||||||
impl SimpleRenderer {
|
impl SimpleRenderer {
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.#ctor`.
|
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.#ctor`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.SimpleRenderer()`.
|
||||||
|
/// Mapping contract: ownership `none`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn new() -> Result<Self, crate::Error> {
|
pub fn new() -> Result<Self, crate::Error> {
|
||||||
Self::native_new()
|
Self::native_new()
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateFacetedMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateFacetedMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.FacetedMesh LibreMetaverse.Rendering.SimpleRenderer.GenerateFacetedMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_faceted_mesh(
|
pub fn generate_faceted_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -21,6 +32,10 @@ impl SimpleRenderer {
|
|||||||
self.native_generate_faceted_mesh(prim, lod)
|
self.native_generate_faceted_mesh(prim, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateFacetedSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateFacetedSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.FacetedMesh LibreMetaverse.Rendering.SimpleRenderer.GenerateFacetedSculptMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Imaging.ManagedImage sculptTexture, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_faceted_sculpt_mesh(
|
pub fn generate_faceted_sculpt_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -30,6 +45,10 @@ impl SimpleRenderer {
|
|||||||
self.native_generate_faceted_sculpt_mesh(prim, sculpt_texture, lod)
|
self.native_generate_faceted_sculpt_mesh(prim, sculpt_texture, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateSimpleMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateSimpleMesh(LibreMetaverse.Primitive,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.SimpleMesh LibreMetaverse.Rendering.SimpleRenderer.GenerateSimpleMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_simple_mesh(
|
pub fn generate_simple_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -38,6 +57,10 @@ impl SimpleRenderer {
|
|||||||
self.native_generate_simple_mesh(prim, lod)
|
self.native_generate_simple_mesh(prim, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateSimpleSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.GenerateSimpleSculptMesh(LibreMetaverse.Primitive,LibreMetaverse.Imaging.ManagedImage,LibreMetaverse.Rendering.DetailLevel)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Rendering.SimpleMesh LibreMetaverse.Rendering.SimpleRenderer.GenerateSimpleSculptMesh(LibreMetaverse.Primitive prim, LibreMetaverse.Imaging.ManagedImage sculptTexture, LibreMetaverse.Rendering.DetailLevel lod)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn generate_simple_sculpt_mesh(
|
pub fn generate_simple_sculpt_mesh(
|
||||||
&self,
|
&self,
|
||||||
prim: libremetaverse::Primitive,
|
prim: libremetaverse::Primitive,
|
||||||
@@ -47,6 +70,10 @@ impl SimpleRenderer {
|
|||||||
self.native_generate_simple_sculpt_mesh(prim, sculpt_texture, lod)
|
self.native_generate_simple_sculpt_mesh(prim, sculpt_texture, lod)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.TransformTexCoords(System.Collections.Generic.List{LibreMetaverse.Rendering.Vertex},LibreMetaverse.Vector3,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Vector3)`.
|
/// C# member: `M:LibreMetaverse.Rendering.SimpleRenderer.TransformTexCoords(System.Collections.Generic.List{LibreMetaverse.Rendering.Vertex},LibreMetaverse.Vector3,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Vector3)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Rendering.SimpleRenderer.TransformTexCoords(System.Collections.Generic.List<LibreMetaverse.Rendering.Vertex> vertices, LibreMetaverse.Vector3 center, LibreMetaverse.Primitive.TextureEntryFace teFace, LibreMetaverse.Vector3 primScale)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,mutable_borrow,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn transform_tex_coords(
|
pub fn transform_tex_coords(
|
||||||
&self,
|
&self,
|
||||||
vertices: &mut Vec<libremetaverse::rendering::Vertex>,
|
vertices: &mut Vec<libremetaverse::rendering::Vertex>,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Deterministic reference rendering for legacy prims and decoded sculpt maps.
|
//! Deterministic reference rendering for legacy prims and decoded sculpt maps.
|
||||||
//!
|
//!
|
||||||
//! [`SimpleRenderer`] converts checked `PrimMesher` geometry into the shared
|
//! [`SimpleRenderer`] converts checked `PrimMesher` geometry into the shared
|
||||||
//! rendering types without a graphics backend. See the crate README for the
|
//! rendering types without a graphics backend. See the crate `README` for the
|
||||||
//! supported topology, resource bounds, and mesh-asset boundary.
|
//! supported topology, resource bounds, and mesh-asset boundary.
|
||||||
|
|
||||||
extern crate self as libremetaverse_rendering_simple;
|
extern crate self as libremetaverse_rendering_simple;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,8 @@
|
|||||||
//! Restrained Love Viewer support corresponding to `LibreMetaverse.RLV`.
|
//! Restrained Love Viewer (`RLV`) support corresponding to `LibreMetaverse.RLV`.
|
||||||
|
//!
|
||||||
|
//! The native implementation covers bounded command parsing, behavior state,
|
||||||
|
//! inventory resolution, attachment locks, camera policy, and permission
|
||||||
|
//! decisions. Applications retain control of transport and user consent.
|
||||||
|
|
||||||
extern crate self as libremetaverse_rlv;
|
extern crate self as libremetaverse_rlv;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,8 @@
|
|||||||
//! Structured-data types corresponding to `LibreMetaverse.StructuredData`.
|
//! Bounded `LLSD`/`OSD` support corresponding to `LibreMetaverse.StructuredData`.
|
||||||
|
//!
|
||||||
|
//! `XML`, `JSON`, binary, and notation codecs share checked depth, node, and byte
|
||||||
|
//! limits. Choose this crate for standalone structured-data work without
|
||||||
|
//! constructing a grid client or enabling a native codec.
|
||||||
|
|
||||||
extern crate self as libremetaverse_structured_data;
|
extern crate self as libremetaverse_structured_data;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,8 @@
|
|||||||
//! Core value types corresponding to `LibreMetaverse.Types`.
|
//! Native value and compatibility types corresponding to `LibreMetaverse.Types`.
|
||||||
|
//!
|
||||||
|
//! Use this crate directly for `UUID`s, vectors, matrices, colors, cancellation,
|
||||||
|
//! subscriptions, bounded collections, and protocol-neutral boundary types.
|
||||||
|
//! It has no grid login side effects and requires no native system library.
|
||||||
|
|
||||||
extern crate self as libremetaverse_types;
|
extern crate self as libremetaverse_types;
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,16 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Utilities.ConnectionManager`.
|
/// C# type: `T:LibreMetaverse.Utilities.ConnectionManager`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Utilities.ConnectionManager` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
pub struct ConnectionManager;
|
pub struct ConnectionManager;
|
||||||
impl ConnectionManager {
|
impl ConnectionManager {
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.#ctor(LibreMetaverse.GridClient,System.Int32)`.
|
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.#ctor(LibreMetaverse.GridClient,System.Int32)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Utilities.ConnectionManager(LibreMetaverse.GridClient client, System.Int32 timerFrequency)`.
|
||||||
|
/// Mapping contract: ownership `owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
client: libremetaverse::GridClient,
|
client: libremetaverse::GridClient,
|
||||||
timer_frequency: i32,
|
timer_frequency: i32,
|
||||||
@@ -18,6 +25,10 @@ impl ConnectionManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.PersistentLogin(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String,System.String)`.
|
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.PersistentLogin(LibreMetaverse.GridClient,System.String,System.String,System.String,System.String,System.String,System.String)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Threading.Tasks.Task<System.Boolean> LibreMetaverse.Utilities.ConnectionManager.PersistentLogin(LibreMetaverse.GridClient client, System.String firstName, System.String lastName, System.String password, System.String userAgent, System.String start, System.String author)`.
|
||||||
|
/// Mapping contract: ownership `owned,owned,owned,owned,owned,owned,owned`; async `async`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub async fn persistent_login(
|
pub async fn persistent_login(
|
||||||
client: libremetaverse::GridClient,
|
client: libremetaverse::GridClient,
|
||||||
first_name: String,
|
first_name: String,
|
||||||
@@ -32,6 +43,10 @@ impl ConnectionManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.StayInSim(System.UInt64,LibreMetaverse.Vector3)`.
|
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.StayInSim(System.UInt64,LibreMetaverse.Vector3)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Utilities.ConnectionManager.StayInSim(System.UInt64 handle, LibreMetaverse.Vector3 desiredPosition)`.
|
||||||
|
/// Mapping contract: ownership `shared_self,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn stay_in_sim(
|
pub fn stay_in_sim(
|
||||||
&self,
|
&self,
|
||||||
handle: u64,
|
handle: u64,
|
||||||
@@ -42,15 +57,26 @@ impl ConnectionManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.Stop`.
|
/// C# member: `M:LibreMetaverse.Utilities.ConnectionManager.Stop`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Utilities.ConnectionManager.Stop()`.
|
||||||
|
/// Mapping contract: ownership `shared_self`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`.
|
||||||
pub fn stop(&self) -> Result<(), crate::Error> {
|
pub fn stop(&self) -> Result<(), crate::Error> {
|
||||||
libremetaverse_types::not_implemented("M:LibreMetaverse.Utilities.ConnectionManager.Stop")
|
libremetaverse_types::not_implemented("M:LibreMetaverse.Utilities.ConnectionManager.Stop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Utilities.Realism`.
|
/// C# type: `T:LibreMetaverse.Utilities.Realism`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Utilities.Realism` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
pub struct Realism;
|
pub struct Realism;
|
||||||
impl Realism {
|
impl Realism {
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.Realism.Chat(LibreMetaverse.GridClient,System.String)`.
|
/// C# member: `M:LibreMetaverse.Utilities.Realism.Chat(LibreMetaverse.GridClient,System.String)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Utilities.Realism.Chat(LibreMetaverse.GridClient client, System.String message)`.
|
||||||
|
/// Mapping contract: ownership `owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn chat_with_grid_client_string(
|
pub fn chat_with_grid_client_string(
|
||||||
client: libremetaverse::GridClient,
|
client: libremetaverse::GridClient,
|
||||||
message: String,
|
message: String,
|
||||||
@@ -60,6 +86,10 @@ impl Realism {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.Realism.Chat(LibreMetaverse.GridClient,System.String,LibreMetaverse.ChatType,System.Int32)`.
|
/// C# member: `M:LibreMetaverse.Utilities.Realism.Chat(LibreMetaverse.GridClient,System.String,LibreMetaverse.ChatType,System.Int32)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Void LibreMetaverse.Utilities.Realism.Chat(LibreMetaverse.GridClient client, System.String message, LibreMetaverse.ChatType type, System.Int32 cps)`.
|
||||||
|
/// Mapping contract: ownership `owned,owned,owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn chat_with_grid_client_string_chat_type_int32(
|
pub fn chat_with_grid_client_string_chat_type_int32(
|
||||||
client: libremetaverse::GridClient,
|
client: libremetaverse::GridClient,
|
||||||
message: String,
|
message: String,
|
||||||
@@ -71,6 +101,10 @@ impl Realism {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.Realism.ChatAsync(LibreMetaverse.GridClient,System.String,LibreMetaverse.ChatType,System.Int32,System.Threading.CancellationToken)`.
|
/// C# member: `M:LibreMetaverse.Utilities.Realism.ChatAsync(LibreMetaverse.GridClient,System.String,LibreMetaverse.ChatType,System.Int32,System.Threading.CancellationToken)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Threading.Tasks.Task LibreMetaverse.Utilities.Realism.ChatAsync(LibreMetaverse.GridClient client, System.String message, LibreMetaverse.ChatType type, System.Int32 cps, System.Threading.CancellationToken cancellationToken = default)`.
|
||||||
|
/// Mapping contract: ownership `owned,owned,owned,owned,optional_owned`; async `async`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub async fn chat_with_grid_client_string_chat_type_int32_cancellation_token(
|
pub async fn chat_with_grid_client_string_chat_type_int32_cancellation_token(
|
||||||
client: libremetaverse::GridClient,
|
client: libremetaverse::GridClient,
|
||||||
message: String,
|
message: String,
|
||||||
@@ -83,6 +117,10 @@ impl Realism {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.Realism.Shoot(LibreMetaverse.GridClient)`.
|
/// C# member: `M:LibreMetaverse.Utilities.Realism.Shoot(LibreMetaverse.GridClient)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Boolean LibreMetaverse.Utilities.Realism.Shoot(LibreMetaverse.GridClient client)`.
|
||||||
|
/// Mapping contract: ownership `owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn shoot_with_grid_client(
|
pub fn shoot_with_grid_client(
|
||||||
client: libremetaverse::GridClient,
|
client: libremetaverse::GridClient,
|
||||||
) -> Result<bool, crate::Error> {
|
) -> Result<bool, crate::Error> {
|
||||||
@@ -91,6 +129,10 @@ impl Realism {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// C# member: `M:LibreMetaverse.Utilities.Realism.Shoot(LibreMetaverse.GridClient,LibreMetaverse.Vector3)`.
|
/// C# member: `M:LibreMetaverse.Utilities.Realism.Shoot(LibreMetaverse.GridClient,LibreMetaverse.Vector3)`.
|
||||||
|
///
|
||||||
|
/// C# signature: `System.Boolean LibreMetaverse.Utilities.Realism.Shoot(LibreMetaverse.GridClient client, LibreMetaverse.Vector3 target)`.
|
||||||
|
/// Mapping contract: ownership `owned,owned`; async `sync`;
|
||||||
|
/// errors `Result<_, crate::Error>`; overload `descriptive_overload_name`; kind `method`.
|
||||||
pub fn shoot_with_grid_client_vector3(
|
pub fn shoot_with_grid_client_vector3(
|
||||||
client: libremetaverse::GridClient,
|
client: libremetaverse::GridClient,
|
||||||
target: libremetaverse_types::Vector3,
|
target: libremetaverse_types::Vector3,
|
||||||
@@ -102,15 +144,34 @@ impl Realism {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// C# type: `T:LibreMetaverse.Utilities.WaterType`.
|
/// C# type: `T:LibreMetaverse.Utilities.WaterType`.
|
||||||
|
///
|
||||||
|
/// Native Rust mapping of C# `LibreMetaverse.Utilities.WaterType` using decision
|
||||||
|
/// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba).
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum WaterType {
|
pub enum WaterType {
|
||||||
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Dry`.
|
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Dry`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Utilities.WaterType LibreMetaverse.Utilities.WaterType.Dry`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
Dry = 1,
|
Dry = 1,
|
||||||
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Underwater`.
|
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Underwater`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Utilities.WaterType LibreMetaverse.Utilities.WaterType.Underwater`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
Underwater = 3,
|
Underwater = 3,
|
||||||
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Unknown`.
|
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Unknown`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Utilities.WaterType LibreMetaverse.Utilities.WaterType.Unknown`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Waterfront`.
|
/// C# member: `F:LibreMetaverse.Utilities.WaterType.Waterfront`.
|
||||||
|
///
|
||||||
|
/// C# signature: `LibreMetaverse.Utilities.WaterType LibreMetaverse.Utilities.WaterType.Waterfront`.
|
||||||
|
/// Mapping contract: ownership `owned_value`; async `sync`;
|
||||||
|
/// errors `none`; overload `direct_snake_case`; kind `direct`.
|
||||||
Waterfront = 2,
|
Waterfront = 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
//! Utilities corresponding to `LibreMetaverse.Utilities`.
|
//! Native utility APIs corresponding to `LibreMetaverse.Utilities`.
|
||||||
|
//!
|
||||||
|
//! Use this compatibility crate when ported code depends on the upstream helper
|
||||||
|
//! assembly. Public signatures use project-owned Rust boundary types and never
|
||||||
|
//! load the `CLR` or an upstream binary.
|
||||||
|
|
||||||
extern crate self as libremetaverse_utilities;
|
extern crate self as libremetaverse_utilities;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,8 @@
|
|||||||
//! Vivox adapter corresponding to `LibreMetaverse.Voice.Vivox`.
|
//! `Vivox` control adapter corresponding to `LibreMetaverse.Voice.Vivox`.
|
||||||
|
//!
|
||||||
|
//! The crate implements the bounded `XML` request/event protocol for an explicitly
|
||||||
|
//! supplied service endpoint. It neither bundles nor starts the proprietary
|
||||||
|
//! daemon, and its diagnostics redact account, session, and channel secrets.
|
||||||
|
|
||||||
extern crate self as libremetaverse_voice_vivox;
|
extern crate self as libremetaverse_voice_vivox;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,9 @@
|
|||||||
//! WebRTC voice adapter corresponding to `LibreMetaverse.Voice.WebRTC`.
|
//! Native `WebRTC` voice adapter corresponding to `LibreMetaverse.Voice.WebRTC`.
|
||||||
|
//!
|
||||||
|
//! `ICE`, `DTLS`, `SRTP`, `RTP`, `SCTP` data, `Opus` media, controls, and teardown
|
||||||
|
//! run in Rust. System `libopus` is required; the optional `real-audio` feature
|
||||||
|
//! adds the cross-platform `CPAL` hardware boundary while default tests use
|
||||||
|
//! virtual audio.
|
||||||
|
|
||||||
extern crate self as libremetaverse_voice_webrtc;
|
extern crate self as libremetaverse_voice_webrtc;
|
||||||
|
|
||||||
|
|||||||
14
crates/libremetaverse/examples/cancellation.rs
Normal file
14
crates/libremetaverse/examples/cancellation.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use libremetaverse_types::compat::CancellationTokenSource;
|
||||||
|
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
async fn main() -> Result<(), libremetaverse::Error> {
|
||||||
|
let source = CancellationTokenSource::new();
|
||||||
|
let token = source.token();
|
||||||
|
source.cancel();
|
||||||
|
token.cancelled().await;
|
||||||
|
assert!(matches!(
|
||||||
|
token.throw_if_cancellation_requested(),
|
||||||
|
Err(libremetaverse::Error::Cancelled)
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
10
crates/libremetaverse/examples/offline_client.rs
Normal file
10
crates/libremetaverse/examples/offline_client.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use libremetaverse::{ClientLifecycleState, GridClient};
|
||||||
|
|
||||||
|
fn main() -> Result<(), libremetaverse::Error> {
|
||||||
|
let client = GridClient::new()?;
|
||||||
|
assert_eq!(client.lifecycle_state(), ClientLifecycleState::Active);
|
||||||
|
client.dispose_with_method()?;
|
||||||
|
client.dispose_with_method()?;
|
||||||
|
assert_eq!(client.lifecycle_state(), ClientLifecycleState::Disposed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,10 @@
|
|||||||
//! Public rewrite shell corresponding to the main `LibreMetaverse` assembly.
|
//! Native Rust grid client corresponding to the main `LibreMetaverse` assembly.
|
||||||
|
//!
|
||||||
|
//! Start here for login, networking, agents, inventory, assets, appearance,
|
||||||
|
//! world, and social APIs. The appended guide covers crate selection, C# name
|
||||||
|
//! migration, ownership, cancellation, events, security, and `OpenSim` setup.
|
||||||
|
|
||||||
|
#![doc = include_str!("../../../docs/rust-api-guide.md")]
|
||||||
|
|
||||||
extern crate self as libremetaverse;
|
extern crate self as libremetaverse;
|
||||||
|
|
||||||
|
|||||||
267
docs/rust-api-guide.md
Normal file
267
docs/rust-api-guide.md
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
# Rust API and C# migration guide
|
||||||
|
|
||||||
|
`MetaCrate` is a native Rust implementation of the API and behavior cataloged
|
||||||
|
from `LibreMetaverse`. The compatibility reference is the
|
||||||
|
[pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba);
|
||||||
|
it is never loaded, invoked, or shipped. Exact type/member mappings live in
|
||||||
|
[`RUST-TYPES.tsv`](../api/RUST-TYPES.tsv) and
|
||||||
|
[`RUST-MAPPING.tsv`](../api/RUST-MAPPING.tsv). Search those files by C#
|
||||||
|
documentation ID when a familiar member has a non-obvious Rust name.
|
||||||
|
|
||||||
|
## Choosing crates and features
|
||||||
|
|
||||||
|
Most applications start with `libremetaverse` and add narrower crates only when
|
||||||
|
they use those APIs directly.
|
||||||
|
|
||||||
|
| Crate | Choose it for | Features or system boundary |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7; optional `jpeg2000` and `vorbis` |
|
||||||
|
| `libremetaverse-types` | UUIDs, vectors, matrices, colors, cancellation, compatibility collections, and boundary types | Pure Rust |
|
||||||
|
| `libremetaverse-structured-data` | LLSD/OSD XML, JSON, binary, and notation | Pure Rust |
|
||||||
|
| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Optional `jpeg2000` |
|
||||||
|
| `libremetaverse-imaging-skia` | Common raster formats through Skia | Optional `skia`; native Skia build/cache |
|
||||||
|
| `libremetaverse-prim-mesher` | Legacy prim and sculpt geometry | Pure Rust |
|
||||||
|
| `libremetaverse-rendering-simple` | Deterministic reference geometry | Pure Rust |
|
||||||
|
| `libremetaverse-rendering-mesh-foundry` | Prim, terrain, sculpt, and mesh-asset rendering | Pure Rust |
|
||||||
|
| `libremetaverse-lsl-tools` | LSL lexing, parsing, diagnostics, and generation | Pure Rust |
|
||||||
|
| `libremetaverse-rlv` | RLV commands, restrictions, locks, camera, and inventory policy | Pure Rust |
|
||||||
|
| `libremetaverse-utilities` | Compatible utility helpers | Pure Rust |
|
||||||
|
| `libremetaverse-voice-vivox` | Vivox XML control protocol | External Vivox service is explicit and never spawned |
|
||||||
|
| `libremetaverse-voice-webrtc` | Native ICE/DTLS/SRTP/SCTP and Opus voice | System libopus; optional `real-audio` uses CPAL |
|
||||||
|
| `libremetaverse-openjpeg` | Audited `OpenJPEG` adapter | System `OpenJPEG` 2.5.4 or newer |
|
||||||
|
| `libremetaverse-opus` | Audited Opus encoder/decoder adapter | System libopus |
|
||||||
|
|
||||||
|
Features are additive. Keep defaults unless you need a codec, and enable one
|
||||||
|
native adapter at a time while diagnosing installation problems. The
|
||||||
|
[release CI matrix](release-ci-matrix.md) records every validated combination.
|
||||||
|
|
||||||
|
## Naming and overload migration
|
||||||
|
|
||||||
|
C# `PascalCase` types remain recognizable while methods and properties use
|
||||||
|
Rust `snake_case`. A property getter becomes `name()` and its setter becomes
|
||||||
|
`set_name(value)`. Events become `subscribe_*` methods returning an owned
|
||||||
|
subscription. Rust has no overloads, so the simplest form keeps the base name
|
||||||
|
and additional forms receive parameter-derived suffixes. The mapping ledger is
|
||||||
|
authoritative; do not guess a long suffix.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libremetaverse::InventoryItem;
|
||||||
|
use libremetaverse_types::UUID;
|
||||||
|
|
||||||
|
let id = UUID::new_with_u_int64(42)?;
|
||||||
|
let mut item = InventoryItem::new_with_uuid(id)?;
|
||||||
|
item.base.set_name("Migrated item".into());
|
||||||
|
assert_eq!(item.base.uuid(), id);
|
||||||
|
assert_eq!(item.base.name(), "Migrated item");
|
||||||
|
# Ok::<(), libremetaverse::Error>(())
|
||||||
|
```
|
||||||
|
|
||||||
|
C# `null` usually maps to `Option<T>`, `ref`/`out` may become a return value or
|
||||||
|
an explicit mutable reference, and interface objects become `dyn Trait` behind
|
||||||
|
`Arc` or `Box` according to the ledger's `ownership` column.
|
||||||
|
|
||||||
|
## Ownership and disposal
|
||||||
|
|
||||||
|
Managers cloned from a `GridClient` share native state. `GridClient` owns its
|
||||||
|
cancellation root and cached services; event guards own registrations; session
|
||||||
|
objects own tasks and sockets. Explicit shutdown is recommended because it can
|
||||||
|
report errors, while `Drop` remains the final idempotent safety net.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libremetaverse::{ClientLifecycleState, GridClient};
|
||||||
|
|
||||||
|
let client = GridClient::new()?;
|
||||||
|
assert_eq!(client.lifecycle_state(), ClientLifecycleState::Active);
|
||||||
|
client.dispose_with_method()?;
|
||||||
|
client.dispose_with_method()?; // idempotent
|
||||||
|
assert_eq!(client.lifecycle_state(), ClientLifecycleState::Disposed);
|
||||||
|
# Ok::<(), libremetaverse::Error>(())
|
||||||
|
```
|
||||||
|
|
||||||
|
The runnable version is
|
||||||
|
[`offline_client.rs`](../crates/libremetaverse/examples/offline_client.rs).
|
||||||
|
Never hold a manager lock while calling user code or awaiting I/O. Services
|
||||||
|
registered through `GridClient::builder()` must join every owned task before
|
||||||
|
their `shutdown` method returns.
|
||||||
|
|
||||||
|
## Async work and cancellation
|
||||||
|
|
||||||
|
Async APIs borrow no hidden runtime. Call them from the application executor and
|
||||||
|
pass `CancellationToken` explicitly where the C# API accepted one. Cloning a
|
||||||
|
token is cheap and observes the same cancellation source. Cancellation is a
|
||||||
|
typed `Error::Cancelled`, not a successful empty response.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libremetaverse_types::compat::CancellationTokenSource;
|
||||||
|
|
||||||
|
# #[tokio::main(flavor = "current_thread")]
|
||||||
|
# async fn main() -> Result<(), libremetaverse::Error> {
|
||||||
|
let source = CancellationTokenSource::new();
|
||||||
|
let token = source.token();
|
||||||
|
source.cancel();
|
||||||
|
token.cancelled().await;
|
||||||
|
assert!(matches!(
|
||||||
|
token.throw_if_cancellation_requested(),
|
||||||
|
Err(libremetaverse::Error::Cancelled)
|
||||||
|
));
|
||||||
|
# Ok(())
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
The same code is available as
|
||||||
|
[`cancellation.rs`](../crates/libremetaverse/examples/cancellation.rs).
|
||||||
|
Timeouts belong at the caller or documented operation boundary; cancellation
|
||||||
|
must still drain and join the underlying resource owner.
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
Fallible compatibility APIs return the shared `libremetaverse::Error`; native
|
||||||
|
composition APIs may expose a narrower error such as `ClientCoreError` or
|
||||||
|
`WebRtcError`. Match variants for control flow and use display text only for
|
||||||
|
operators. Errors and `Debug` output redact credentials, capability URLs, and
|
||||||
|
session secrets.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libremetaverse::http::DownloadRequest;
|
||||||
|
use libremetaverse_types::compat::Uri;
|
||||||
|
|
||||||
|
let result = DownloadRequest::new(Uri("file:///not-http".into()), None, None);
|
||||||
|
assert!(matches!(result, Err(libremetaverse::Error::Argument)));
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not translate C# exception swallowing into `unwrap_or_default()`. Preserve
|
||||||
|
the mapped error contract and handle cancellation separately from protocol,
|
||||||
|
I/O, authentication, and validation failures.
|
||||||
|
|
||||||
|
## Events and subscriptions
|
||||||
|
|
||||||
|
An `EventHandler<T>` is an `Arc` callback. Keep the returned `Subscription` for
|
||||||
|
exactly as long as notifications are wanted; dropping it unregisters the
|
||||||
|
callback. Dispatch clones the handler list and releases internal locks before
|
||||||
|
calling user code. Manager event dispatch isolates subscriber panics so one
|
||||||
|
consumer cannot stop later consumers.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libremetaverse::{GridClient, Inventory, InventoryFolder};
|
||||||
|
use libremetaverse_types::UUID;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
let client = Arc::new(GridClient::new()?);
|
||||||
|
let inventory = Inventory::new_with_grid_client_uuid(Arc::clone(&client), UUID::zero())?;
|
||||||
|
let calls = Arc::new(AtomicUsize::new(0));
|
||||||
|
let observed = Arc::clone(&calls);
|
||||||
|
let subscription = inventory.subscribe_inventory_object_added(Arc::new(move |_| {
|
||||||
|
observed.fetch_add(1, Ordering::AcqRel);
|
||||||
|
}));
|
||||||
|
let folder = InventoryFolder::new(UUID::new_with_u_int64(7)?)?;
|
||||||
|
inventory.update_node_for(&folder)?;
|
||||||
|
assert_eq!(calls.load(Ordering::Acquire), 1);
|
||||||
|
drop(subscription);
|
||||||
|
client.dispose_with_method()?;
|
||||||
|
# Ok::<(), libremetaverse::Error>(())
|
||||||
|
```
|
||||||
|
|
||||||
|
## Threading and callbacks
|
||||||
|
|
||||||
|
Public shared managers use `Arc`, atomics, mutexes, channels, and cancellation
|
||||||
|
tokens rather than a CLR synchronization context. A type being cloneable does
|
||||||
|
not make a callback re-entrant: keep handlers short, move expensive work to a
|
||||||
|
bounded queue, and never block an async runtime thread waiting for itself.
|
||||||
|
Download, inventory, client, and voice diagnostics expose owned task/queue
|
||||||
|
counts for shutdown checks. The [concurrency audit](concurrency-hardening.md)
|
||||||
|
defines the exact baseline and soak thresholds.
|
||||||
|
|
||||||
|
## Security boundaries
|
||||||
|
|
||||||
|
The released graph contains no CLR host, .NET assembly loader, subprocess RPC,
|
||||||
|
or foreign `LibreMetaverse` bridge. HTTP uses rustls. Untrusted LLSD, archive,
|
||||||
|
image, packet, event, and signaling inputs have explicit size/depth limits.
|
||||||
|
Secrets belong in operation arguments or environment variables, never in
|
||||||
|
diagnostics, evidence, filenames, command lines, or committed fixtures.
|
||||||
|
|
||||||
|
Offline/fake modes are real deterministic executions, not skipped live tests.
|
||||||
|
Live login and every mutating smoke action require their own opt-in and literal
|
||||||
|
confirmation. See the [live-grid boundary](live-grid-smoke.md).
|
||||||
|
|
||||||
|
## Native prerequisites
|
||||||
|
|
||||||
|
The default client build needs no image-codec system library. Optional native
|
||||||
|
features require:
|
||||||
|
|
||||||
|
- `OpenJPEG` 2.5.4 or newer for `jpeg2000`;
|
||||||
|
- the rust-skia prerequisites/cache for `skia`;
|
||||||
|
- system libopus for WebRTC voice and the `libremetaverse-opus` adapter;
|
||||||
|
- ALSA development headers on Linux, `CoreAudio` on macOS, or WASAPI on Windows
|
||||||
|
when `real-audio` enables CPAL;
|
||||||
|
- the Vorbis encoder build prerequisites for `vorbis`.
|
||||||
|
|
||||||
|
Discovery uses `pkg-config` on Unix/macOS and `vcpkg` on Windows MSVC where
|
||||||
|
applicable. The adapters remain cross-platform; Gitea workflows intentionally
|
||||||
|
run only on `ubuntu-latest`.
|
||||||
|
|
||||||
|
## Live `OpenSim` setup
|
||||||
|
|
||||||
|
Use a dedicated `OpenSim` account and put credentials in the workspace `.env` or
|
||||||
|
the process environment. The login URL is used directly; no Second Life host is
|
||||||
|
substituted.
|
||||||
|
|
||||||
|
```text
|
||||||
|
GRID_USER=First Last
|
||||||
|
GRID_PASSWORD=...
|
||||||
|
GRID_LOGIN_URL=https://your-opensim.example/login
|
||||||
|
```
|
||||||
|
|
||||||
|
Start with the credential-safe audit and fake smoke, then opt into live login:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo run -p libremetaverse-programs --bin live-grid-smoke -- --audit-only
|
||||||
|
cargo run -p libremetaverse-programs --bin live-grid-smoke -- --fake
|
||||||
|
cargo run -p libremetaverse-programs --bin live-grid-smoke -- \
|
||||||
|
--allow-live-login --confirm-live-login LOGIN
|
||||||
|
```
|
||||||
|
|
||||||
|
Chat, movement, and reversible inventory each require additional confirmations
|
||||||
|
documented in the [`OpenSim` live-grid guide](live-grid-smoke.md). A test that
|
||||||
|
needs live credentials must fail clearly when they are absent; it must not
|
||||||
|
silently skip.
|
||||||
|
|
||||||
|
## Programs and operational tools
|
||||||
|
|
||||||
|
All pinned upstream programs have native Rust targets and deterministic offline
|
||||||
|
tests. Their arguments, exit statuses, fake/live boundaries, and focused test
|
||||||
|
commands are linked from the [program operations manual](../programs/README.md):
|
||||||
|
|
||||||
|
- [`osd-inspector`](../programs/README.md#osdinspector)
|
||||||
|
- [`simple-bot`](../programs/README.md#simplebot)
|
||||||
|
- [`packet-dump`](../programs/README.md#packetdump)
|
||||||
|
- [`prim-inspector`](../programs/README.md#priminspector)
|
||||||
|
- [`inventory-explorer`](../programs/README.md#inventoryexplorer)
|
||||||
|
- [`irc-gateway`](../programs/README.md#ircgateway)
|
||||||
|
- [`test-client`](../programs/README.md#testclient)
|
||||||
|
- [`vivox-test`](../programs/README.md#vivoxtest)
|
||||||
|
- [`webrtc-test`](../programs/README.md#webrtctest)
|
||||||
|
|
||||||
|
For services, run the client under an external supervisor, propagate shutdown
|
||||||
|
cancellation, bound queues/files, collect sanitized evidence, and call logout
|
||||||
|
before disposal. The tool never invents persistence or retry policy on behalf of
|
||||||
|
the application.
|
||||||
|
|
||||||
|
## Documentation validation
|
||||||
|
|
||||||
|
The complete documentation gate is:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo run --locked -p metacrate-ci-matrix -- documentation-audit \
|
||||||
|
--evidence /tmp/metacrate-documentation.json
|
||||||
|
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --locked -j 1
|
||||||
|
cargo test --locked -p libremetaverse --doc -j 1
|
||||||
|
cargo test --locked -p libremetaverse --examples -j 1
|
||||||
|
cargo run --locked -p libremetaverse --example offline_client
|
||||||
|
cargo run --locked -p libremetaverse --example cancellation
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated [coverage report](../api/DOCUMENTATION-COVERAGE.md) proves that
|
||||||
|
every mapped member/type retains its exact C# concept ID and mapping context,
|
||||||
|
all public crates have root docs, all local Markdown links resolve, all programs
|
||||||
|
are linked, and every Rust fence in this guide is compiled as a doctest.
|
||||||
606
tools/ci-matrix/src/documentation.rs
Normal file
606
tools/ci-matrix/src/documentation.rs
Normal file
@@ -0,0 +1,606 @@
|
|||||||
|
use super::{MatrixError, Result};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
use std::fs::{self, OpenOptions};
|
||||||
|
use std::io::Write as _;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
const MAPPING_PATH: &str = "api/RUST-MAPPING.tsv";
|
||||||
|
const TYPES_PATH: &str = "api/RUST-TYPES.tsv";
|
||||||
|
const GUIDE_PATH: &str = "docs/rust-api-guide.md";
|
||||||
|
const REPORT_PATH: &str = "api/DOCUMENTATION-COVERAGE.md";
|
||||||
|
const UPSTREAM_COMMIT: &str = "2aa70bb68513b39795da5d13c88f31b86e85a3ba";
|
||||||
|
|
||||||
|
const PUBLIC_CRATES: [(&str, &str); 15] = [
|
||||||
|
("libremetaverse", "crates/libremetaverse"),
|
||||||
|
("libremetaverse-types", "crates/libremetaverse-types"),
|
||||||
|
(
|
||||||
|
"libremetaverse-structured-data",
|
||||||
|
"crates/libremetaverse-structured-data",
|
||||||
|
),
|
||||||
|
("libremetaverse-imaging", "crates/libremetaverse-imaging"),
|
||||||
|
(
|
||||||
|
"libremetaverse-imaging-skia",
|
||||||
|
"crates/libremetaverse-imaging-skia",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"libremetaverse-prim-mesher",
|
||||||
|
"crates/libremetaverse-prim-mesher",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"libremetaverse-rendering-simple",
|
||||||
|
"crates/libremetaverse-rendering-simple",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"libremetaverse-rendering-mesh-foundry",
|
||||||
|
"crates/libremetaverse-rendering-mesh-foundry",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"libremetaverse-lsl-tools",
|
||||||
|
"crates/libremetaverse-lsl-tools",
|
||||||
|
),
|
||||||
|
("libremetaverse-rlv", "crates/libremetaverse-rlv"),
|
||||||
|
(
|
||||||
|
"libremetaverse-utilities",
|
||||||
|
"crates/libremetaverse-utilities",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"libremetaverse-voice-vivox",
|
||||||
|
"crates/libremetaverse-voice-vivox",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"libremetaverse-voice-webrtc",
|
||||||
|
"crates/libremetaverse-voice-webrtc",
|
||||||
|
),
|
||||||
|
("libremetaverse-openjpeg", "crates/libremetaverse-openjpeg"),
|
||||||
|
("libremetaverse-opus", "crates/libremetaverse-opus"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const PROGRAMS: [&str; 9] = [
|
||||||
|
"osd-inspector",
|
||||||
|
"simple-bot",
|
||||||
|
"packet-dump",
|
||||||
|
"prim-inspector",
|
||||||
|
"inventory-explorer",
|
||||||
|
"irc-gateway",
|
||||||
|
"test-client",
|
||||||
|
"vivox-test",
|
||||||
|
"webrtc-test",
|
||||||
|
];
|
||||||
|
|
||||||
|
const REQUIRED_GUIDE_SECTIONS: [&str; 11] = [
|
||||||
|
"## Choosing crates and features",
|
||||||
|
"## Naming and overload migration",
|
||||||
|
"## Ownership and disposal",
|
||||||
|
"## Async work and cancellation",
|
||||||
|
"## Errors",
|
||||||
|
"## Events and subscriptions",
|
||||||
|
"## Threading and callbacks",
|
||||||
|
"## Security boundaries",
|
||||||
|
"## Native prerequisites",
|
||||||
|
"## Live `OpenSim` setup",
|
||||||
|
"## Programs and operational tools",
|
||||||
|
];
|
||||||
|
|
||||||
|
const REQUIRED_MAPPING_FIELDS: [&str; 11] = [
|
||||||
|
"csharp_id",
|
||||||
|
"csharp_signature",
|
||||||
|
"rust_crate",
|
||||||
|
"rust_item_path",
|
||||||
|
"rust_signature",
|
||||||
|
"ownership",
|
||||||
|
"asyncness",
|
||||||
|
"error_model",
|
||||||
|
"overload_decision",
|
||||||
|
"mapping_kind",
|
||||||
|
"status",
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct DocumentationEvidence {
|
||||||
|
schema: u32,
|
||||||
|
upstream_commit: &'static str,
|
||||||
|
public_crates: usize,
|
||||||
|
mapped_public_types: usize,
|
||||||
|
documented_public_types: usize,
|
||||||
|
mapped_members: usize,
|
||||||
|
documented_members: usize,
|
||||||
|
tested_rust_snippets: usize,
|
||||||
|
linked_programs: usize,
|
||||||
|
checked_local_links: usize,
|
||||||
|
required_guide_sections: usize,
|
||||||
|
status: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Snapshot {
|
||||||
|
mapped_public_types: usize,
|
||||||
|
documented_public_types: usize,
|
||||||
|
mapped_members: usize,
|
||||||
|
documented_members: usize,
|
||||||
|
rust_snippets: usize,
|
||||||
|
local_links: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Regenerates the deterministic mapped API documentation coverage report.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if documentation inputs are incomplete or cannot be read.
|
||||||
|
pub fn write_documentation_report(root: &Path) -> Result<()> {
|
||||||
|
let snapshot = inspect(root)?;
|
||||||
|
fs::write(root.join(REPORT_PATH), render_report(&snapshot))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates public crate, mapped item, guide, link, snippet, and program docs.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error when any mapped public item lacks its exact C# concept ID,
|
||||||
|
/// a public crate lacks publishable root documentation, a local link is broken,
|
||||||
|
/// a required migration topic/program is absent, or the checked report is stale.
|
||||||
|
pub fn audit_documentation(root: &Path, evidence: &Path) -> Result<()> {
|
||||||
|
if evidence.exists() {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"{} already exists; preserve or remove it before rerunning the audit",
|
||||||
|
evidence.display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let snapshot = inspect(root)?;
|
||||||
|
let expected_report = render_report(&snapshot);
|
||||||
|
let report = fs::read_to_string(root.join(REPORT_PATH))?;
|
||||||
|
if report != expected_report {
|
||||||
|
return Err(MatrixError::new(
|
||||||
|
"documentation coverage report is stale; run documentation-report",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let record = DocumentationEvidence {
|
||||||
|
schema: 1,
|
||||||
|
upstream_commit: UPSTREAM_COMMIT,
|
||||||
|
public_crates: PUBLIC_CRATES.len(),
|
||||||
|
mapped_public_types: snapshot.mapped_public_types,
|
||||||
|
documented_public_types: snapshot.documented_public_types,
|
||||||
|
mapped_members: snapshot.mapped_members,
|
||||||
|
documented_members: snapshot.documented_members,
|
||||||
|
tested_rust_snippets: snapshot.rust_snippets,
|
||||||
|
linked_programs: PROGRAMS.len(),
|
||||||
|
checked_local_links: snapshot.local_links,
|
||||||
|
required_guide_sections: REQUIRED_GUIDE_SECTIONS.len(),
|
||||||
|
status: "ok",
|
||||||
|
};
|
||||||
|
if let Some(parent) = evidence.parent() {
|
||||||
|
fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(evidence)?;
|
||||||
|
serde_json::to_writer_pretty(&mut file, &record)?;
|
||||||
|
file.write_all(b"\n")?;
|
||||||
|
file.sync_all()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inspect(root: &Path) -> Result<Snapshot> {
|
||||||
|
validate_crates(root)?;
|
||||||
|
let guide = fs::read_to_string(root.join(GUIDE_PATH))?;
|
||||||
|
validate_guide(&guide)?;
|
||||||
|
let generated_docs = generated_doc_lines(root)?;
|
||||||
|
let mapped_members = validate_member_mapping(root, &generated_docs)?;
|
||||||
|
let mapped_public_types = validate_type_mapping(root, &generated_docs)?;
|
||||||
|
let local_links = validate_markdown_links(root)?;
|
||||||
|
let rust_snippets = guide.matches("```rust").count();
|
||||||
|
if rust_snippets < 4 {
|
||||||
|
return Err(MatrixError::new(
|
||||||
|
"Rust API guide must contain at least four compiled snippets",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(Snapshot {
|
||||||
|
mapped_public_types,
|
||||||
|
documented_public_types: mapped_public_types,
|
||||||
|
mapped_members,
|
||||||
|
documented_members: mapped_members,
|
||||||
|
rust_snippets,
|
||||||
|
local_links,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_crates(root: &Path) -> Result<()> {
|
||||||
|
for (name, relative) in PUBLIC_CRATES {
|
||||||
|
let directory = root.join(relative);
|
||||||
|
let manifest = fs::read_to_string(directory.join("Cargo.toml"))?;
|
||||||
|
if !manifest.contains(&format!("name = \"{name}\""))
|
||||||
|
|| !manifest.lines().any(|line| {
|
||||||
|
line.trim_start()
|
||||||
|
.strip_prefix("description = ")
|
||||||
|
.is_some_and(|description| description.len() >= 24)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"public crate {name} lacks package documentation metadata"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let library = fs::read_to_string(directory.join("src/lib.rs"))?;
|
||||||
|
let crate_doc_bytes: usize = library
|
||||||
|
.lines()
|
||||||
|
.take_while(|line| line.starts_with("//!") || line.trim().is_empty())
|
||||||
|
.filter(|line| line.starts_with("//!"))
|
||||||
|
.map(str::len)
|
||||||
|
.sum();
|
||||||
|
if crate_doc_bytes < 120 {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"public crate {name} needs a useful crate-level overview"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let core = fs::read_to_string(root.join("crates/libremetaverse/src/lib.rs"))?;
|
||||||
|
if !core.contains("include_str!(\"../../../docs/rust-api-guide.md\")") {
|
||||||
|
return Err(MatrixError::new(
|
||||||
|
"the tested Rust API guide is not included in libremetaverse rustdoc",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_guide(guide: &str) -> Result<()> {
|
||||||
|
if !guide.contains("https://github.com/cinderblocks/libremetaverse")
|
||||||
|
|| !guide.contains(UPSTREAM_COMMIT)
|
||||||
|
{
|
||||||
|
return Err(MatrixError::new(
|
||||||
|
"Rust API guide lacks the pinned C# source link",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for section in REQUIRED_GUIDE_SECTIONS {
|
||||||
|
if !guide.contains(section) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"Rust API guide lacks required section {section}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for program in PROGRAMS {
|
||||||
|
if !guide.contains(&format!("`{program}`")) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"Rust API guide does not link program {program}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generated_doc_lines(root: &Path) -> Result<BTreeSet<String>> {
|
||||||
|
let mut lines = BTreeSet::new();
|
||||||
|
for (_, relative) in PUBLIC_CRATES {
|
||||||
|
let generated = root.join(relative).join("src/generated.rs");
|
||||||
|
if generated.exists() {
|
||||||
|
lines.extend(
|
||||||
|
fs::read_to_string(generated)?
|
||||||
|
.lines()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|line| line.starts_with("///"))
|
||||||
|
.map(str::to_owned),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_member_mapping(root: &Path, docs: &BTreeSet<String>) -> Result<usize> {
|
||||||
|
let (header, rows) = read_tsv(&root.join(MAPPING_PATH))?;
|
||||||
|
let positions = positions(&header, &REQUIRED_MAPPING_FIELDS)?;
|
||||||
|
let id_position = positions["csharp_id"];
|
||||||
|
let mut ids = BTreeSet::new();
|
||||||
|
for row in &rows {
|
||||||
|
for field in REQUIRED_MAPPING_FIELDS {
|
||||||
|
if row[positions[field]].trim().is_empty() {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapping row has empty documentation context field {field}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let id = &row[id_position];
|
||||||
|
if !ids.insert(id.clone()) {
|
||||||
|
return Err(MatrixError::new(format!("duplicate mapped member {id}")));
|
||||||
|
}
|
||||||
|
if !docs.contains(&format!("/// C# member: `{id}`.")) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapped member {id} lacks its C# concept documentation marker"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let signature = &row[positions["csharp_signature"]];
|
||||||
|
if !docs.contains(&format!("/// C# signature: `{signature}`.")) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapped member {id} lacks its C# signature documentation"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let ownership = &row[positions["ownership"]];
|
||||||
|
let asyncness = &row[positions["asyncness"]];
|
||||||
|
if !docs.contains(&format!(
|
||||||
|
"/// Mapping contract: ownership `{ownership}`; async `{asyncness}`;"
|
||||||
|
)) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapped member {id} lacks ownership/async documentation"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let error_model = &row[positions["error_model"]];
|
||||||
|
let overload = &row[positions["overload_decision"]];
|
||||||
|
let mapping_kind = &row[positions["mapping_kind"]];
|
||||||
|
if !docs.contains(&format!(
|
||||||
|
"/// errors `{error_model}`; overload `{overload}`; kind `{mapping_kind}`."
|
||||||
|
)) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapped member {id} lacks error/overload/kind documentation"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(rows.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_type_mapping(root: &Path, docs: &BTreeSet<String>) -> Result<usize> {
|
||||||
|
let (header, rows) = read_tsv(&root.join(TYPES_PATH))?;
|
||||||
|
let required = [
|
||||||
|
"csharp_type_id",
|
||||||
|
"csharp_signature",
|
||||||
|
"source_kind",
|
||||||
|
"rust_crate",
|
||||||
|
"rust_path",
|
||||||
|
"mapping_decision",
|
||||||
|
"status",
|
||||||
|
];
|
||||||
|
let positions = positions(&header, &required)?;
|
||||||
|
let mut count = 0;
|
||||||
|
let mut ids = BTreeSet::new();
|
||||||
|
for row in &rows {
|
||||||
|
for field in required {
|
||||||
|
if row[positions[field]].trim().is_empty() {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"type row has empty documentation context field {field}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let id = &row[positions["csharp_type_id"]];
|
||||||
|
if !ids.insert(id.clone()) {
|
||||||
|
return Err(MatrixError::new(format!("duplicate mapped type {id}")));
|
||||||
|
}
|
||||||
|
if id.starts_with("T:LibreMetaverse")
|
||||||
|
&& row[positions["source_kind"]] != "referenced_support_trait"
|
||||||
|
{
|
||||||
|
count += 1;
|
||||||
|
if !docs.contains(&format!("/// C# type: `{id}`.")) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapped public type {id} lacks its C# concept documentation marker"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let signature = &row[positions["csharp_signature"]];
|
||||||
|
if !docs.contains(&format!(
|
||||||
|
"/// Native Rust mapping of C# `{signature}` using decision"
|
||||||
|
)) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapped public type {id} lacks useful C# signature documentation"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let decision = &row[positions["mapping_decision"]];
|
||||||
|
if !docs.contains(&format!(
|
||||||
|
"/// `{decision}`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/{UPSTREAM_COMMIT})."
|
||||||
|
)) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"mapped public type {id} lacks its mapping decision/source link"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_tsv(path: &Path) -> Result<(Vec<String>, Vec<Vec<String>>)> {
|
||||||
|
let text = fs::read_to_string(path)?;
|
||||||
|
let mut lines = text.lines();
|
||||||
|
let header = lines
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| MatrixError::new(format!("{} is empty", path.display())))?
|
||||||
|
.split('\t')
|
||||||
|
.map(str::to_owned)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let rows = lines
|
||||||
|
.filter(|line| !line.is_empty())
|
||||||
|
.map(|line| line.split('\t').map(str::to_owned).collect::<Vec<_>>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if rows.iter().any(|row| row.len() != header.len()) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"{} contains a malformed row",
|
||||||
|
path.display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok((header, rows))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn positions<const N: usize>(
|
||||||
|
header: &[String],
|
||||||
|
required: &[&'static str; N],
|
||||||
|
) -> Result<BTreeMap<&'static str, usize>> {
|
||||||
|
let mut result = BTreeMap::new();
|
||||||
|
for &field in required {
|
||||||
|
let position = header
|
||||||
|
.iter()
|
||||||
|
.position(|candidate| candidate == field)
|
||||||
|
.ok_or_else(|| MatrixError::new(format!("TSV lacks required column {field}")))?;
|
||||||
|
result.insert(field, position);
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn markdown_files(root: &Path) -> Result<Vec<PathBuf>> {
|
||||||
|
let mut files = vec![root.join("README.md"), root.join("programs/README.md")];
|
||||||
|
for directory in [root.join("docs"), root.join("api"), root.join("crates")] {
|
||||||
|
collect_markdown(&directory, &mut files)?;
|
||||||
|
}
|
||||||
|
files.sort();
|
||||||
|
files.dedup();
|
||||||
|
files.retain(|path| path != &root.join(REPORT_PATH));
|
||||||
|
Ok(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_markdown(directory: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
|
||||||
|
for entry in fs::read_dir(directory)? {
|
||||||
|
let path = entry?.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
collect_markdown(&path, files)?;
|
||||||
|
} else if path.extension().and_then(|value| value.to_str()) == Some("md") {
|
||||||
|
files.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_markdown_links(root: &Path) -> Result<usize> {
|
||||||
|
let mut checked = 0;
|
||||||
|
for file in markdown_files(root)? {
|
||||||
|
let text = fs::read_to_string(&file)?;
|
||||||
|
let mut remainder = text.as_str();
|
||||||
|
while let Some(start) = remainder.find("](") {
|
||||||
|
remainder = &remainder[start + 2..];
|
||||||
|
let Some(end) = remainder.find(')') else {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"{} contains an unterminated Markdown link",
|
||||||
|
file.display()
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
let raw = remainder[..end].trim().trim_matches(['<', '>']);
|
||||||
|
remainder = &remainder[end + 1..];
|
||||||
|
let target = raw.split_whitespace().next().unwrap_or_default();
|
||||||
|
if target.is_empty()
|
||||||
|
|| target.starts_with("http://")
|
||||||
|
|| target.starts_with("https://")
|
||||||
|
|| target.starts_with("mailto:")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (path, fragment) = target
|
||||||
|
.split_once('#')
|
||||||
|
.map_or((target, None), |(path, fragment)| (path, Some(fragment)));
|
||||||
|
let resolved = if path.is_empty() {
|
||||||
|
file.clone()
|
||||||
|
} else {
|
||||||
|
file.parent().unwrap_or(root).join(path)
|
||||||
|
};
|
||||||
|
if !resolved.exists() {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"broken local Markdown link {target} in {}",
|
||||||
|
file.strip_prefix(root).unwrap_or(&file).display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if let Some(fragment) = fragment.filter(|fragment| !fragment.is_empty())
|
||||||
|
&& resolved.extension().and_then(|value| value.to_str()) == Some("md")
|
||||||
|
{
|
||||||
|
let destination = fs::read_to_string(&resolved)?;
|
||||||
|
if !markdown_anchors(&destination).contains(fragment) {
|
||||||
|
return Err(MatrixError::new(format!(
|
||||||
|
"broken local Markdown anchor {target} in {}",
|
||||||
|
file.strip_prefix(root).unwrap_or(&file).display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checked += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn markdown_anchors(markdown: &str) -> BTreeSet<String> {
|
||||||
|
let mut anchors = BTreeSet::new();
|
||||||
|
let mut occurrences = BTreeMap::<String, usize>::new();
|
||||||
|
let mut fenced = false;
|
||||||
|
for line in markdown.lines() {
|
||||||
|
if line.trim_start().starts_with("```") {
|
||||||
|
fenced = !fenced;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if fenced {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let heading = line.trim_start_matches('#').trim();
|
||||||
|
if heading.is_empty() || heading.len() == line.trim().len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let base = heading
|
||||||
|
.chars()
|
||||||
|
.filter_map(|character| {
|
||||||
|
if character.is_alphanumeric() || matches!(character, '-' | '_') {
|
||||||
|
Some(character.to_ascii_lowercase())
|
||||||
|
} else if character.is_whitespace() {
|
||||||
|
Some('-')
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
let occurrence = occurrences.entry(base.clone()).or_default();
|
||||||
|
let anchor = if *occurrence == 0 {
|
||||||
|
base.clone()
|
||||||
|
} else {
|
||||||
|
format!("{base}-{occurrence}")
|
||||||
|
};
|
||||||
|
*occurrence += 1;
|
||||||
|
anchors.insert(anchor);
|
||||||
|
}
|
||||||
|
anchors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_report(snapshot: &Snapshot) -> String {
|
||||||
|
format!(
|
||||||
|
"# Documentation coverage\n\n\
|
||||||
|
Generated by `metacrate-ci-matrix documentation-report`; do not edit by hand.\n\n\
|
||||||
|
| Surface | Documented | Required | Coverage |\n\
|
||||||
|
| --- | ---: | ---: | ---: |\n\
|
||||||
|
| Publishable public crates | {crates} | {crates} | 100% |\n\
|
||||||
|
| Pinned LibreMetaverse public types | {types_documented} | {types} | 100% |\n\
|
||||||
|
| Mapped public members | {members_documented} | {members} | 100% |\n\
|
||||||
|
| Compiled Rust guide snippets | {snippets} | 4 minimum | pass |\n\
|
||||||
|
| Linked native programs | {programs} | {programs} | 100% |\n\
|
||||||
|
| Checked local Markdown links | {links} | {links} | 100% |\n\n\
|
||||||
|
Every mapped item is tied to its exact C# documentation ID and to the ownership, \
|
||||||
|
asyncness, error, overload, mapping-kind, and Rust-signature decisions in \
|
||||||
|
[`RUST-MAPPING.tsv`](RUST-MAPPING.tsv). Public types are tied to the corresponding \
|
||||||
|
type mapping. The upstream source is pinned to \
|
||||||
|
[`{commit}`](https://github.com/cinderblocks/libremetaverse/tree/{commit}).\n",
|
||||||
|
crates = PUBLIC_CRATES.len(),
|
||||||
|
types_documented = snapshot.documented_public_types,
|
||||||
|
types = snapshot.mapped_public_types,
|
||||||
|
members_documented = snapshot.documented_members,
|
||||||
|
members = snapshot.mapped_members,
|
||||||
|
snippets = snapshot.rust_snippets,
|
||||||
|
programs = PROGRAMS.len(),
|
||||||
|
links = snapshot.local_links,
|
||||||
|
commit = UPSTREAM_COMMIT,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn markdown_anchor_generation_matches_linkable_headings() {
|
||||||
|
let anchors = markdown_anchors(
|
||||||
|
"# API guide\n\n## Live `OpenSim` setup\n## API guide\n```text\n# ignored\n```\n",
|
||||||
|
);
|
||||||
|
assert!(anchors.contains("api-guide"));
|
||||||
|
assert!(anchors.contains("live-opensim-setup"));
|
||||||
|
assert!(anchors.contains("api-guide-1"));
|
||||||
|
assert!(!anchors.contains("ignored"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn report_contains_exact_documentation_totals() {
|
||||||
|
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||||
|
let snapshot = inspect(&root).unwrap();
|
||||||
|
assert_eq!(snapshot.mapped_members, 30_789);
|
||||||
|
assert_eq!(snapshot.documented_members, snapshot.mapped_members);
|
||||||
|
assert_eq!(snapshot.mapped_public_types, 3_066);
|
||||||
|
assert_eq!(
|
||||||
|
snapshot.documented_public_types,
|
||||||
|
snapshot.mapped_public_types
|
||||||
|
);
|
||||||
|
assert!(snapshot.rust_snippets >= 4);
|
||||||
|
assert!(snapshot.local_links > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,10 @@ use std::process::{Command, ExitStatus, Stdio};
|
|||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
mod dependency;
|
mod dependency;
|
||||||
|
mod documentation;
|
||||||
|
|
||||||
pub use dependency::audit_dependencies;
|
pub use dependency::audit_dependencies;
|
||||||
|
pub use documentation::{audit_documentation, write_documentation_report};
|
||||||
|
|
||||||
pub const MATRIX_PATH: &str = "ci/release-matrix.json";
|
pub const MATRIX_PATH: &str = "ci/release-matrix.json";
|
||||||
const WORKFLOW_PATH: &str = ".gitea/workflows/release-matrix.yml";
|
const WORKFLOW_PATH: &str = ".gitea/workflows/release-matrix.yml";
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use metacrate_ci_matrix::{audit, audit_dependencies, load, run, workspace_root};
|
use metacrate_ci_matrix::{
|
||||||
|
audit, audit_dependencies, audit_documentation, load, run, workspace_root,
|
||||||
|
write_documentation_report,
|
||||||
|
};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -46,9 +49,27 @@ fn execute() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
audit_dependencies(&root, &evidence)?;
|
audit_dependencies(&root, &evidence)?;
|
||||||
println!("dependency policy: ok ({})", evidence.display());
|
println!("dependency policy: ok ({})", evidence.display());
|
||||||
}
|
}
|
||||||
|
Some("documentation-report") if arguments.next().is_none() => {
|
||||||
|
write_documentation_report(&root)?;
|
||||||
|
println!("documentation coverage report: updated");
|
||||||
|
}
|
||||||
|
Some("documentation-audit") => {
|
||||||
|
let flag = arguments
|
||||||
|
.next()
|
||||||
|
.ok_or("documentation-audit requires --evidence FILE")?;
|
||||||
|
let evidence = arguments
|
||||||
|
.next()
|
||||||
|
.ok_or("documentation-audit requires --evidence FILE")?;
|
||||||
|
if flag != "--evidence" || arguments.next().is_some() {
|
||||||
|
return Err("usage: ci-matrix documentation-audit --evidence FILE".into());
|
||||||
|
}
|
||||||
|
let evidence = absolute_or_rooted(&root, &evidence);
|
||||||
|
audit_documentation(&root, &evidence)?;
|
||||||
|
println!("documentation coverage: ok ({})", evidence.display());
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
"usage: ci-matrix audit | run PROFILE --evidence FILE | dependency-audit --evidence FILE"
|
"usage: ci-matrix audit | run PROFILE --evidence FILE | dependency-audit --evidence FILE | documentation-report | documentation-audit --evidence FILE"
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3205,6 +3205,11 @@ VALUE_DERIVES = {
|
|||||||
"T:LibreMetaverse.WorldSettings": "Clone, Debug, Eq, PartialEq",
|
"T:LibreMetaverse.WorldSettings": "Clone, Debug, Eq, PartialEq",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PINNED_CSHARP_SOURCE = (
|
||||||
|
"https://github.com/cinderblocks/libremetaverse/tree/"
|
||||||
|
"2aa70bb68513b39795da5d13c88f31b86e85a3ba"
|
||||||
|
)
|
||||||
|
|
||||||
# Native implementations occasionally need Rust-only bounds that have no
|
# Native implementations occasionally need Rust-only bounds that have no
|
||||||
# direct C# metadata equivalent. C#'s `where T : class` permits returning a
|
# direct C# metadata equivalent. C#'s `where T : class` permits returning a
|
||||||
# reference to the stored object; the Rust inventory store returns an owned
|
# reference to the stored object; the Rust inventory store returns an owned
|
||||||
@@ -3385,10 +3390,29 @@ def duplicate_enum_values(item: dict) -> bool:
|
|||||||
return len(values) != len(set(values))
|
return len(values) != len(set(values))
|
||||||
|
|
||||||
|
|
||||||
def render_enum(item: dict, rust_name: str) -> list[str]:
|
def render_type_docs(item: dict, type_row: dict) -> list[str]:
|
||||||
|
return [
|
||||||
|
f"/// C# type: `{item['doc_id']}`.",
|
||||||
|
"///",
|
||||||
|
f"/// Native Rust mapping of C# `{item['signature']}` using decision",
|
||||||
|
f"/// `{type_row['mapping_decision']}`. See the [pinned C# source]({PINNED_CSHARP_SOURCE}).",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def render_member_docs(row: dict[str, str], indent: str = "") -> list[str]:
|
||||||
|
return [
|
||||||
|
f"{indent}/// C# member: `{row['csharp_id']}`.",
|
||||||
|
f"{indent}///",
|
||||||
|
f"{indent}/// C# signature: `{row['csharp_signature']}`.",
|
||||||
|
f"{indent}/// Mapping contract: ownership `{row['ownership']}`; async `{row['asyncness']}`;",
|
||||||
|
f"{indent}/// errors `{row['error_model']}`; overload `{row['overload_decision']}`; kind `{row['mapping_kind']}`.",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def render_enum(item: dict, type_row: dict, rust_name: str, member_rows: dict[str, dict[str, str]]) -> list[str]:
|
||||||
underlying = UNDERLYING[item["enum_underlying_type"]]
|
underlying = UNDERLYING[item["enum_underlying_type"]]
|
||||||
values = [member for member in item["members"] if member["kind"] == "enum_value"]
|
values = [member for member in item["members"] if member["kind"] == "enum_value"]
|
||||||
lines = [f"/// C# type: `{item['doc_id']}`."]
|
lines = render_type_docs(item, type_row)
|
||||||
if item["doc_id"] in OPEN_ENUM_IDS:
|
if item["doc_id"] in OPEN_ENUM_IDS:
|
||||||
lines += [
|
lines += [
|
||||||
"#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]",
|
"#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]",
|
||||||
@@ -3398,10 +3422,10 @@ def render_enum(item: dict, rust_name: str) -> list[str]:
|
|||||||
f"impl {rust_name} {{",
|
f"impl {rust_name} {{",
|
||||||
]
|
]
|
||||||
for member in values:
|
for member in values:
|
||||||
lines += [
|
lines += render_member_docs(member_rows[member["doc_id"]], " ")
|
||||||
f" /// C# member: `{member['doc_id']}`.",
|
lines.append(
|
||||||
f" pub const {mapping.pascal(member['name'])}: Self = Self({member['value']['value']});",
|
f" pub const {mapping.pascal(member['name'])}: Self = Self({member['value']['value']});"
|
||||||
]
|
)
|
||||||
lines.append("}")
|
lines.append("}")
|
||||||
return lines
|
return lines
|
||||||
if flags_enum(item) or duplicate_enum_values(item):
|
if flags_enum(item) or duplicate_enum_values(item):
|
||||||
@@ -3414,7 +3438,8 @@ def render_enum(item: dict, rust_name: str) -> list[str]:
|
|||||||
for member in values:
|
for member in values:
|
||||||
name = mapping.snake(member["name"]).upper()
|
name = mapping.snake(member["name"]).upper()
|
||||||
value = member["value"]["value"]
|
value = member["value"]["value"]
|
||||||
lines += [f" /// C# member: `{member['doc_id']}`.", f" pub const {name}: Self = Self({value});"]
|
lines += render_member_docs(member_rows[member["doc_id"]], " ")
|
||||||
|
lines.append(f" pub const {name}: Self = Self({value});")
|
||||||
lines.append("}")
|
lines.append("}")
|
||||||
return lines
|
return lines
|
||||||
lines += [
|
lines += [
|
||||||
@@ -3423,7 +3448,8 @@ def render_enum(item: dict, rust_name: str) -> list[str]:
|
|||||||
f"pub enum {rust_name} {{",
|
f"pub enum {rust_name} {{",
|
||||||
]
|
]
|
||||||
for member in values:
|
for member in values:
|
||||||
lines += [f" /// C# member: `{member['doc_id']}`.", f" {mapping.pascal(member['name'])} = {member['value']['value']},"]
|
lines += render_member_docs(member_rows[member["doc_id"]], " ")
|
||||||
|
lines.append(f" {mapping.pascal(member['name'])} = {member['value']['value']},")
|
||||||
lines.append("}")
|
lines.append("}")
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
@@ -3593,16 +3619,17 @@ def rust_constant_value(item: dict) -> str:
|
|||||||
def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str:
|
def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str]], mapper: mapping.Mapper) -> str:
|
||||||
rust_name = type_name(type_row)
|
rust_name = type_name(type_row)
|
||||||
if native_path := NATIVE_TYPES.get(item["doc_id"]):
|
if native_path := NATIVE_TYPES.get(item["doc_id"]):
|
||||||
lines = [f"/// C# type: `{item['doc_id']}`."]
|
lines = render_type_docs(item, type_row)
|
||||||
lines.extend(f"/// C# member: `{member['doc_id']}`." for member in item["members"])
|
for member in item["members"]:
|
||||||
|
lines.extend(render_member_docs(member_rows[member["doc_id"]]))
|
||||||
lines.append(f"pub use {native_path} as {rust_name};")
|
lines.append(f"pub use {native_path} as {rust_name};")
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
if item["kind"] == "enum":
|
if item["kind"] == "enum":
|
||||||
return "\n".join(render_enum(item, rust_name))
|
return "\n".join(render_enum(item, type_row, rust_name, member_rows))
|
||||||
names = generic_names(item)
|
names = generic_names(item)
|
||||||
suffix = generic_suffix(names)
|
suffix = generic_suffix(names)
|
||||||
trait = item["kind"] == "interface"
|
trait = item["kind"] == "interface"
|
||||||
lines = [f"/// C# type: `{item['doc_id']}`."]
|
lines = render_type_docs(item, type_row)
|
||||||
if trait:
|
if trait:
|
||||||
supertrait = TRAIT_SUPERTRAITS.get(item["doc_id"])
|
supertrait = TRAIT_SUPERTRAITS.get(item["doc_id"])
|
||||||
inheritance = f": {supertrait}" if supertrait else ""
|
inheritance = f": {supertrait}" if supertrait else ""
|
||||||
@@ -3614,10 +3641,8 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
|
|||||||
native_declaration = NATIVE_DECLARATIONS.get(item["doc_id"])
|
native_declaration = NATIVE_DECLARATIONS.get(item["doc_id"])
|
||||||
if native_declaration:
|
if native_declaration:
|
||||||
lines.append(f"pub use {native_declaration} as {rust_name};")
|
lines.append(f"pub use {native_declaration} as {rust_name};")
|
||||||
lines.extend(
|
for field in fields:
|
||||||
f"/// C# member: `{field['doc_id']}`."
|
lines.extend(render_member_docs(member_rows[field["doc_id"]]))
|
||||||
for field in fields
|
|
||||||
)
|
|
||||||
elif derives := VALUE_DERIVES.get(item["doc_id"]):
|
elif derives := VALUE_DERIVES.get(item["doc_id"]):
|
||||||
lines.append(f"#[derive({derives})]")
|
lines.append(f"#[derive({derives})]")
|
||||||
if native_declaration:
|
if native_declaration:
|
||||||
@@ -3648,7 +3673,8 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
|
|||||||
raise ValueError(f"invalid mapped field signature: {field_signature}")
|
raise ValueError(f"invalid mapped field signature: {field_signature}")
|
||||||
field_name = field_name_match.group(1)
|
field_name = field_name_match.group(1)
|
||||||
field_type = field_name_match.group(2)
|
field_type = field_name_match.group(2)
|
||||||
lines += [f" /// C# member: `{field['doc_id']}`.", f" pub {field_name}: {field_type},"]
|
lines += render_member_docs(member_rows[field["doc_id"]], " ")
|
||||||
|
lines.append(f" pub {field_name}: {field_type},")
|
||||||
for field_name, field_type in private_fields:
|
for field_name, field_type in private_fields:
|
||||||
lines.append(f" {field_name}: {field_type},")
|
lines.append(f" {field_name}: {field_type},")
|
||||||
if names:
|
if names:
|
||||||
@@ -3661,7 +3687,7 @@ def render_type(item: dict, type_row: dict, member_rows: dict[str, dict[str, str
|
|||||||
if member["kind"] in {"enum_value"} or (member["kind"] == "field" and not member.get("static")):
|
if member["kind"] in {"enum_value"} or (member["kind"] == "field" and not member.get("static")):
|
||||||
continue
|
continue
|
||||||
row = member_rows[member["doc_id"]]
|
row = member_rows[member["doc_id"]]
|
||||||
lines.append(f" /// C# member: `{member['doc_id']}`.")
|
lines.extend(render_member_docs(row, " "))
|
||||||
if member["kind"] == "constant":
|
if member["kind"] == "constant":
|
||||||
lines.append(f" {row['rust_signature']} = {rust_constant_value(member)};")
|
lines.append(f" {row['rust_signature']} = {rust_constant_value(member)};")
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user