diff --git a/.gitea/workflows/api-surface.yml b/.gitea/workflows/api-surface.yml new file mode 100644 index 0000000..69e5df2 --- /dev/null +++ b/.gitea/workflows/api-surface.yml @@ -0,0 +1,81 @@ +name: API and SemVer surface + +on: + push: + paths: + - ".gitea/workflows/api-surface.yml" + - "Cargo.lock" + - "Cargo.toml" + - "api/**" + - "ci/evidence/api-audit.json" + - "crates/**" + - "tests/api-compile/**" + - "tests/semver-port/**" + - "tools/check_api_coverage.py" + - "tools/generate_api_shims.py" + - "tools/generate_rust_mapping.py" + - "tools/ci-matrix/**" + pull_request: + paths: + - ".gitea/workflows/api-surface.yml" + - "Cargo.lock" + - "Cargo.toml" + - "api/**" + - "ci/evidence/api-audit.json" + - "crates/**" + - "tests/api-compile/**" + - "tests/semver-port/**" + - "tools/check_api_coverage.py" + - "tools/generate_api_shims.py" + - "tools/generate_rust_mapping.py" + - "tools/ci-matrix/**" + +jobs: + api-surface: + runs-on: ubuntu-latest + env: + CARGO_BUILD_JOBS: "1" + CARGO_INCREMENTAL: "0" + CARGO_PROFILE_DEV_DEBUG: "0" + steps: + - uses: actions/checkout@v4 + + - name: Install native prerequisites + run: sudo apt-get update && sudo apt-get install -y libopus-dev pkg-config + + - name: Install Rust 1.97 + uses: dtolnay/rust-toolchain@stable + with: + toolchain: "1.97.0" + components: rustfmt, clippy + + - name: Verify deterministic mapping and export coverage + run: | + python3 tools/generate_rust_mapping.py --check + python3 tools/generate_api_shims.py --check + python3 tools/check_api_coverage.py + + - name: Verify reviewed API baseline + run: | + cargo run --locked -p metacrate-ci-matrix -- api-baseline-write + git diff --exit-code -- api/SEMVER-BASELINE.json api/SEMVER-AUDIT.md + cargo run --locked -p metacrate-ci-matrix -- api-audit --evidence /tmp/metacrate-api-audit.json + cmp /tmp/metacrate-api-audit.json ci/evidence/api-audit.json + + - name: Compile downstream API consumers + run: | + cargo check --locked --manifest-path tests/api-compile/Cargo.toml + cargo run --locked --manifest-path tests/semver-port/Cargo.toml + + - name: Check formatting and lints + run: | + cargo fmt --all -- --check + cargo fmt --manifest-path tests/semver-port/Cargo.toml -- --check + cargo clippy --locked -p metacrate-ci-matrix --all-targets -- -D warnings + cargo clippy --locked --manifest-path tests/semver-port/Cargo.toml --all-targets -- -D warnings + + - name: Upload audit evidence + uses: actions/upload-artifact@v4 + with: + name: api-semver-audit + path: ci/evidence/api-audit.json diff --git a/.gitea/workflows/documentation.yml b/.gitea/workflows/documentation.yml index c4193f6..895dd5e 100644 --- a/.gitea/workflows/documentation.yml +++ b/.gitea/workflows/documentation.yml @@ -63,7 +63,8 @@ jobs: 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 + cargo run --locked -p metacrate-ci-matrix -- documentation-audit --evidence /tmp/metacrate-documentation-audit.json + cmp /tmp/metacrate-documentation-audit.json ci/evidence/documentation-audit.json - name: Build warning-free workspace documentation env: diff --git a/.gitignore b/.gitignore index 999936e..769b0c1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /crates/libremetaverse-openjpeg/target/ /crates/libremetaverse-openjpeg/Cargo.lock /tests/api-compile/target/ +/tests/semver-port/target/ .env .env.* !.env.example diff --git a/Cargo.lock b/Cargo.lock index a0c6d13..d1b6068 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1775,6 +1775,7 @@ version = "0.0.1" dependencies = [ "serde", "serde_json", + "sha2 0.11.0", ] [[package]] diff --git a/README.md b/README.md index 9cf352d..c7a6186 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ 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. +The reviewed [first-release API and SemVer audit](api/SEMVER-AUDIT.md) fixes the +`0.0.1` public surface, features, ownership and async contracts, and intentional +migration differences in a deterministic baseline. MetaCrate is a test-driven, clean native Rust reimplementation of [LibreMetaverse](https://github.com/cinderblocks/libremetaverse). LibreMetaverse diff --git a/api/DOCUMENTATION-COVERAGE.md b/api/DOCUMENTATION-COVERAGE.md index 0d2ea16..c3690cd 100644 --- a/api/DOCUMENTATION-COVERAGE.md +++ b/api/DOCUMENTATION-COVERAGE.md @@ -9,6 +9,6 @@ Generated by `metacrate-ci-matrix documentation-report`; do not edit by hand. | 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% | +| Checked local Markdown links | 69 | 69 | 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). diff --git a/api/MAPPING-COVERAGE.md b/api/MAPPING-COVERAGE.md index a42d844..f106df0 100644 --- a/api/MAPPING-COVERAGE.md +++ b/api/MAPPING-COVERAGE.md @@ -47,7 +47,7 @@ External member APIs are not copied. Each signature type resolves to Rust core/s - Properties/indexers/events: **5,090 / 11 / 268** - Delegates and flags enums: **40 / 46** - Optional/ref/out/in parameters: **470 / 6,144 / 555 / 0** -- Async Rust signatures: **328** +- Async Rust signatures: **334** - Descriptively renamed overloads: **7,151** - Explicit CLR delegate APM differences: **80** diff --git a/api/README.md b/api/README.md index cc9f78b..25c538a 100644 --- a/api/README.md +++ b/api/README.md @@ -61,6 +61,21 @@ rejects stale, missing, duplicate, or erased entries; and verifies `API-COVERAGE.md`. Regenerate that report with `python3 tools/check_api_coverage.py --write`. +The reviewed first-release public surface is stored in +[`SEMVER-BASELINE.json`](SEMVER-BASELINE.json), with the decisions and migration +recipes rendered in [`SEMVER-AUDIT.md`](SEMVER-AUDIT.md). The audit compares +mapping/export coverage, normalized Rust declarations, feature sets, ownership, +async and error contracts, thread-safety boundaries, and public dependency +types. Record a deliberate baseline only after reviewing its SemVer impact, +then verify it and write reproducible evidence: + +```sh +cargo run --locked -p metacrate-ci-matrix -- api-baseline-write +cargo run --locked -p metacrate-ci-matrix -- api-audit \ + --evidence /tmp/metacrate-api-audit.json +cmp /tmp/metacrate-api-audit.json ci/evidence/api-audit.json +``` + Native implementations replace whole generated types where practical. The generated module retains the catalog markers and re-exports the hand-written type; member-level implementations use an explicit generator registry. This diff --git a/api/RUST-MAPPING.tsv b/api/RUST-MAPPING.tsv index d252cd5..122cc3e 100644 --- a/api/RUST-MAPPING.tsv +++ b/api/RUST-MAPPING.tsv @@ -10371,7 +10371,7 @@ M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.AddPolicy(LibreMetavers M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem) method System.Boolean LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem item) libremetaverse appearance libremetaverse::appearance::CompositeCurrentOutfitPolicy::can_attach pub fn can_attach(&self, item: Option>) -> Result shared_self,optional_shared sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem) method System.Boolean LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem item) libremetaverse appearance libremetaverse::appearance::CompositeCurrentOutfitPolicy::can_detach pub fn can_detach(&self, item: Option>) -> Result shared_self,optional_shared sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy) method System.Void LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy policyToRemove) libremetaverse appearance libremetaverse::appearance::CompositeCurrentOutfitPolicy::remove_policy pub fn remove_policy(&self, policy_to_remove: Option>) -> Result<(), crate::Error> shared_self,optional_shared sync Result<_, crate::Error> direct_snake_case method mapped -M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List addedItems, System.Collections.Generic.List removedItems, System.Threading.CancellationToken cancellationToken = default) libremetaverse appearance libremetaverse::appearance::CompositeCurrentOutfitPolicy::report_item_change pub async fn report_item_change(&self, added_items: Option>>, removed_items: Option>>, cancellation_token: Option) -> Result<(), crate::Error> shared_self,optional_shared,optional_shared,optional_owned sync Result<_, crate::Error> direct_snake_case method mapped +M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List addedItems, System.Collections.Generic.List removedItems, System.Threading.CancellationToken cancellationToken = default) libremetaverse appearance libremetaverse::appearance::CompositeCurrentOutfitPolicy::report_item_change pub async fn report_item_change(&self, added_items: Option>>, removed_items: Option>>, cancellation_token: Option) -> Result<(), crate::Error> shared_self,optional_shared,optional_shared,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.CurrentOutfitFolder.#ctor(LibreMetaverse.GridClient) constructor LibreMetaverse.Appearance.CurrentOutfitFolder(LibreMetaverse.GridClient client) libremetaverse appearance libremetaverse::appearance::CurrentOutfitFolder::new pub fn new(client: Option>) -> Result optional_shared sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy) method LibreMetaverse.Appearance.ICurrentOutfitPolicy LibreMetaverse.Appearance.CurrentOutfitFolder.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy policyToAdd) libremetaverse appearance libremetaverse::appearance::CurrentOutfitFolder::add_policy pub fn add_policy(&self, policy_to_add: std::sync::Arc) -> Result, crate::Error> shared_self,shared sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddToOutfitAsync(LibreMetaverse.InventoryItem,System.Boolean,System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.Appearance.CurrentOutfitFolder.AddToOutfitAsync(LibreMetaverse.InventoryItem item, System.Boolean replace, System.Threading.CancellationToken cancellationToken = default) libremetaverse appearance libremetaverse::appearance::CurrentOutfitFolder::add_to_outfit_with_inventory_item_boolean_cancellation_token pub async fn add_to_outfit_with_inventory_item_boolean_cancellation_token(&self, item: libremetaverse::InventoryItem, replace: bool, cancellation_token: Option) -> Result<(), crate::Error> shared_self,owned,owned,optional_owned async Result<_, crate::Error> descriptive_overload_name method mapped @@ -10393,7 +10393,7 @@ M:LibreMetaverse.Appearance.CurrentOutfitFolder.ReplaceOutfitAsync(LibreMetavers M:LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem) method LibreMetaverse.InventoryItem LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem itemLink) libremetaverse appearance libremetaverse::appearance::CurrentOutfitFolder::resolve_inventory_link pub fn resolve_inventory_link(&self, item_link: std::sync::Arc) -> Result>, crate::Error> shared_self,shared sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem) method System.Boolean LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem item) libremetaverse appearance libremetaverse::appearance::ICurrentOutfitPolicy::can_attach pub fn can_attach(&self, item: Option>) -> Result shared_self,optional_shared sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem) method System.Boolean LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem item) libremetaverse appearance libremetaverse::appearance::ICurrentOutfitPolicy::can_detach pub fn can_detach(&self, item: Option>) -> Result shared_self,optional_shared sync Result<_, crate::Error> direct_snake_case method mapped -M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List addedItems, System.Collections.Generic.List removedItems, System.Threading.CancellationToken cancellationToken = default) libremetaverse appearance libremetaverse::appearance::ICurrentOutfitPolicy::report_item_change pub fn report_item_change(&self, added_items: Option>>, removed_items: Option>>, cancellation_token: Option) -> std::pin::Pin> + Send + '_>> shared_self,optional_shared,optional_shared,optional_owned sync Result<_, crate::Error> direct_snake_case method mapped +M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List addedItems, System.Collections.Generic.List removedItems, System.Threading.CancellationToken cancellationToken = default) libremetaverse appearance libremetaverse::appearance::ICurrentOutfitPolicy::report_item_change pub fn report_item_change(&self, added_items: Option>>, removed_items: Option>>, cancellation_token: Option) -> std::pin::Pin> + Send + '_>> shared_self,optional_shared,optional_shared,optional_owned boxed_future Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.InitialOutfit.#ctor(LibreMetaverse.GridClient,LibreMetaverse.Appearance.CurrentOutfitFolder) constructor LibreMetaverse.Appearance.InitialOutfit(LibreMetaverse.GridClient client, LibreMetaverse.Appearance.CurrentOutfitFolder cof) libremetaverse appearance libremetaverse::appearance::InitialOutfit::new pub fn new(client: libremetaverse::GridClient, cof: libremetaverse::appearance::CurrentOutfitFolder) -> Result owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.InitialOutfit.CheckSystemFoldersAsync(System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.Appearance.InitialOutfit.CheckSystemFoldersAsync(System.Threading.CancellationToken cancellationToken = default) libremetaverse appearance libremetaverse::appearance::InitialOutfit::check_system_folders pub async fn check_system_folders(&self, cancellation_token: Option) -> Result<(), crate::Error> shared_self,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.Appearance.InitialOutfit.CopyFolderAsync(LibreMetaverse.InventoryFolder,LibreMetaverse.UUID,System.Threading.CancellationToken,System.IProgress{LibreMetaverse.Appearance.InitialOutfit.InitialOutfitProgress}) method System.Threading.Tasks.Task LibreMetaverse.Appearance.InitialOutfit.CopyFolderAsync(LibreMetaverse.InventoryFolder folder, LibreMetaverse.UUID destination, System.Threading.CancellationToken cancellationToken = default, System.IProgress progress = default) libremetaverse appearance libremetaverse::appearance::InitialOutfit::copy_folder pub async fn copy_folder(&self, folder: libremetaverse::InventoryFolder, destination: libremetaverse_types::UUID, cancellation_token: Option, progress: Option>>) -> Result shared_self,owned,owned,optional_owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped @@ -11887,7 +11887,7 @@ M:LibreMetaverse.InventoryManager.CreateItemFromAssetAsync(System.Byte[],System. M:LibreMetaverse.InventoryManager.CreateItemFromAssetResult.#ctor constructor LibreMetaverse.InventoryManager.CreateItemFromAssetResult() libremetaverse libremetaverse::InventoryManagerCreateItemFromAssetResult::new pub fn new() -> Result none sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.CreateItemFromAssetVariablePriceAsync(System.Byte[],System.String,System.String,LibreMetaverse.AssetType,LibreMetaverse.InventoryType,LibreMetaverse.UUID,LibreMetaverse.Permissions,System.Func{System.Int32,System.Boolean},System.Threading.CancellationToken,System.IProgress{LibreMetaverse.HttpCapsClient.ProgressReport}) method System.Threading.Tasks.Task LibreMetaverse.InventoryManager.CreateItemFromAssetVariablePriceAsync(System.Byte[] data, System.String name, System.String description, LibreMetaverse.AssetType assetType, LibreMetaverse.InventoryType invType, LibreMetaverse.UUID folderID, LibreMetaverse.Permissions permissions, System.Func confirmCost = default, System.Threading.CancellationToken cancellationToken = default, System.IProgress progress = default) libremetaverse libremetaverse::InventoryManager::create_item_from_asset_variable_price pub async fn create_item_from_asset_variable_price(&self, data: Vec, name: String, description: String, asset_type: libremetaverse_types::AssetType, inv_type: libremetaverse_types::InventoryType, folder_id: libremetaverse_types::UUID, permissions: libremetaverse::Permissions, confirm_cost: Option bool + Send + Sync>>, cancellation_token: Option, progress: Option>>) -> Result shared_self,owned,owned,owned,owned,owned,owned,owned,optional_owned,optional_owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.CreateLinkAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.String,System.String,LibreMetaverse.InventoryType,LibreMetaverse.UUID,System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.InventoryManager.CreateLinkAsync(LibreMetaverse.UUID folderID, LibreMetaverse.UUID itemID, System.String name, System.String description, LibreMetaverse.InventoryType invType, LibreMetaverse.UUID transactionID, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::create_link pub async fn create_link(&self, folder_id: libremetaverse_types::UUID, item_id: libremetaverse_types::UUID, name: String, description: String, inv_type: libremetaverse_types::InventoryType, transaction_id: libremetaverse_types::UUID, cancellation_token: Option) -> Result, crate::Error> shared_self,owned,owned,owned,owned,owned,owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped -M:LibreMetaverse.InventoryManager.CreateLinksAsync(LibreMetaverse.UUID,System.Collections.Generic.IEnumerable{System.ValueTuple{LibreMetaverse.InventoryBase,System.String}},System.Action{System.Boolean},System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.InventoryManager.CreateLinksAsync(LibreMetaverse.UUID folderID, System.Collections.Generic.IEnumerable> linksToCreate, System.Action callback, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::create_links pub async fn create_links(&self, folder_id: libremetaverse_types::UUID, links_to_create: Box>, callback: Option>, cancellation_token: Option) -> Result<(), crate::Error> shared_self,owned,owned,optional_owned,optional_owned sync Result<_, crate::Error> direct_snake_case method mapped +M:LibreMetaverse.InventoryManager.CreateLinksAsync(LibreMetaverse.UUID,System.Collections.Generic.IEnumerable{System.ValueTuple{LibreMetaverse.InventoryBase,System.String}},System.Action{System.Boolean},System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.InventoryManager.CreateLinksAsync(LibreMetaverse.UUID folderID, System.Collections.Generic.IEnumerable> linksToCreate, System.Action callback, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::create_links pub async fn create_links(&self, folder_id: libremetaverse_types::UUID, links_to_create: Box>, callback: Option>, cancellation_token: Option) -> Result<(), crate::Error> shared_self,owned,owned,optional_owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.CreateOrRetrieveInventoryItem(LibreMetaverse.InventoryType,LibreMetaverse.UUID) method LibreMetaverse.InventoryItem LibreMetaverse.InventoryManager.CreateOrRetrieveInventoryItem(LibreMetaverse.InventoryType InvType, LibreMetaverse.UUID ItemID) libremetaverse libremetaverse::InventoryManager::create_or_retrieve_inventory_item pub fn create_or_retrieve_inventory_item(&self, inv_type: libremetaverse_types::InventoryType, item_id: libremetaverse_types::UUID) -> Result shared_self,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.DecryptShadowID(LibreMetaverse.UUID) method LibreMetaverse.UUID LibreMetaverse.InventoryManager.DecryptShadowID(LibreMetaverse.UUID shadowID) libremetaverse libremetaverse::InventoryManager::decrypt_shadow_id pub fn decrypt_shadow_id(shadow_id: libremetaverse_types::UUID) -> Result owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.Dispose method System.Void LibreMetaverse.InventoryManager.Dispose() libremetaverse libremetaverse::InventoryManager::dispose pub fn dispose(&self) -> Result<(), crate::Error> shared_self sync Result<_, crate::Error> direct_snake_case method mapped @@ -11900,10 +11900,10 @@ M:LibreMetaverse.InventoryManager.FetchItemHttpAsync(LibreMetaverse.UUID,LibreMe M:LibreMetaverse.InventoryManager.FindFolderForType(LibreMetaverse.AssetType) method LibreMetaverse.UUID LibreMetaverse.InventoryManager.FindFolderForType(LibreMetaverse.AssetType type) libremetaverse libremetaverse::InventoryManager::find_folder_for_type_with_asset_type pub fn find_folder_for_type_with_asset_type(&self, type_: libremetaverse_types::AssetType) -> Result shared_self,owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.InventoryManager.FindFolderForType(LibreMetaverse.FolderType) method LibreMetaverse.UUID LibreMetaverse.InventoryManager.FindFolderForType(LibreMetaverse.FolderType type) libremetaverse libremetaverse::InventoryManager::find_folder_for_type_with_folder_type pub fn find_folder_for_type_with_folder_type(&self, type_: libremetaverse_types::FolderType) -> Result shared_self,owned sync Result<_, crate::Error> descriptive_overload_name method mapped M:LibreMetaverse.InventoryManager.FindObjectByPathAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.String,System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.InventoryManager.FindObjectByPathAsync(LibreMetaverse.UUID baseFolder, LibreMetaverse.UUID inventoryOwner, System.String path, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::find_object_by_path pub async fn find_object_by_path(&self, base_folder: libremetaverse_types::UUID, inventory_owner: libremetaverse_types::UUID, path: String, cancellation_token: Option) -> Result shared_self,owned,owned,owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped -M:LibreMetaverse.InventoryManager.FolderContentsAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.Boolean,System.Boolean,LibreMetaverse.InventorySortOrder,System.Threading.CancellationToken,System.Boolean) method System.Threading.Tasks.Task> LibreMetaverse.InventoryManager.FolderContentsAsync(LibreMetaverse.UUID folder, LibreMetaverse.UUID owner, System.Boolean fetchFolders, System.Boolean fetchItems, LibreMetaverse.InventorySortOrder order, System.Threading.CancellationToken cancellationToken = default, System.Boolean followLinks = default) libremetaverse libremetaverse::InventoryManager::folder_contents pub async fn folder_contents(&self, folder: libremetaverse_types::UUID, owner: libremetaverse_types::UUID, fetch_folders: bool, fetch_items: bool, order: libremetaverse::InventorySortOrder, cancellation_token: Option, follow_links: Option) -> Result>, crate::Error> shared_self,owned,owned,owned,owned,owned,optional_owned,optional_owned sync Result<_, crate::Error> direct_snake_case method mapped +M:LibreMetaverse.InventoryManager.FolderContentsAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.Boolean,System.Boolean,LibreMetaverse.InventorySortOrder,System.Threading.CancellationToken,System.Boolean) method System.Threading.Tasks.Task> LibreMetaverse.InventoryManager.FolderContentsAsync(LibreMetaverse.UUID folder, LibreMetaverse.UUID owner, System.Boolean fetchFolders, System.Boolean fetchItems, LibreMetaverse.InventorySortOrder order, System.Threading.CancellationToken cancellationToken = default, System.Boolean followLinks = default) libremetaverse libremetaverse::InventoryManager::folder_contents pub async fn folder_contents(&self, folder: libremetaverse_types::UUID, owner: libremetaverse_types::UUID, fetch_folders: bool, fetch_items: bool, order: libremetaverse::InventorySortOrder, cancellation_token: Option, follow_links: Option) -> Result>, crate::Error> shared_self,owned,owned,owned,owned,owned,optional_owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.FolderUpdateResult.#ctor constructor LibreMetaverse.InventoryManager.FolderUpdateResult() libremetaverse libremetaverse::InventoryManagerFolderUpdateResult::new pub fn new() -> Result none sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.GetInventoryRecursiveAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.Collections.Generic.List{LibreMetaverse.InventoryFolder},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.InventoryManager.GetInventoryRecursiveAsync(LibreMetaverse.UUID folderID, LibreMetaverse.UUID owner, System.Collections.Generic.List cats, System.Collections.Generic.List items, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::get_inventory_recursive pub async fn get_inventory_recursive(&self, folder_id: libremetaverse_types::UUID, owner: libremetaverse_types::UUID, cats: Vec, items: Vec, cancellation_token: Option) -> Result<(), crate::Error> shared_self,owned,owned,owned,owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped -M:LibreMetaverse.InventoryManager.GetTaskInventoryAsync(LibreMetaverse.UUID,System.UInt32,LibreMetaverse.Simulator,System.Threading.CancellationToken) method System.Threading.Tasks.Task> LibreMetaverse.InventoryManager.GetTaskInventoryAsync(LibreMetaverse.UUID objectID, System.UInt32 objectLocalID, LibreMetaverse.Simulator simulator = default, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::get_task_inventory pub async fn get_task_inventory(&self, object_id: libremetaverse_types::UUID, object_local_id: u32, simulator: Option, cancellation_token: Option) -> Result>, crate::Error> shared_self,owned,owned,optional_owned,optional_owned sync Result<_, crate::Error> direct_snake_case method mapped +M:LibreMetaverse.InventoryManager.GetTaskInventoryAsync(LibreMetaverse.UUID,System.UInt32,LibreMetaverse.Simulator,System.Threading.CancellationToken) method System.Threading.Tasks.Task> LibreMetaverse.InventoryManager.GetTaskInventoryAsync(LibreMetaverse.UUID objectID, System.UInt32 objectLocalID, LibreMetaverse.Simulator simulator = default, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::get_task_inventory pub async fn get_task_inventory(&self, object_id: libremetaverse_types::UUID, object_local_id: u32, simulator: Option, cancellation_token: Option) -> Result>, crate::Error> shared_self,owned,owned,optional_owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.GiveFolderAsync(LibreMetaverse.UUID,System.String,LibreMetaverse.UUID,System.Boolean,System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.InventoryManager.GiveFolderAsync(LibreMetaverse.UUID folderID, System.String folderName, LibreMetaverse.UUID recipient, System.Boolean doEffect, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::InventoryManager::give_folder pub async fn give_folder(&self, folder_id: libremetaverse_types::UUID, folder_name: String, recipient: libremetaverse_types::UUID, do_effect: bool, cancellation_token: Option) -> Result<(), crate::Error> shared_self,owned,owned,owned,owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.GiveItem(LibreMetaverse.UUID,System.String,LibreMetaverse.AssetType,LibreMetaverse.UUID,System.Boolean) method System.Void LibreMetaverse.InventoryManager.GiveItem(LibreMetaverse.UUID itemID, System.String itemName, LibreMetaverse.AssetType assetType, LibreMetaverse.UUID recipient, System.Boolean doEffect) libremetaverse libremetaverse::InventoryManager::give_item pub fn give_item(&self, item_id: libremetaverse_types::UUID, item_name: String, asset_type: libremetaverse_types::AssetType, recipient: libremetaverse_types::UUID, do_effect: bool) -> Result<(), crate::Error> shared_self,owned,owned,owned,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.InventoryManager.ItemCRC(LibreMetaverse.InventoryItem) method System.UInt32 LibreMetaverse.InventoryManager.ItemCRC(LibreMetaverse.InventoryItem iitem) libremetaverse libremetaverse::InventoryManager::item_crc pub fn item_crc(iitem: &libremetaverse::InventoryItem) -> Result shared_borrow sync Result<_, crate::Error> direct_snake_case method mapped @@ -24948,8 +24948,8 @@ M:LibreMetaverse.TerrainCompressor.DecodePatchHeader(LibreMetaverse.BitPack,Syst M:LibreMetaverse.TerrainCompressor.DecompressPatch(System.Int32[],LibreMetaverse.TerrainPatch.Header,LibreMetaverse.TerrainPatch.GroupHeader) method System.Single[] LibreMetaverse.TerrainCompressor.DecompressPatch(System.Int32[] patches, LibreMetaverse.TerrainPatch.Header header, LibreMetaverse.TerrainPatch.GroupHeader group) libremetaverse libremetaverse::TerrainCompressor::decompress_patch pub fn decompress_patch(patches: Vec, header: libremetaverse::TerrainPatchHeader, group: libremetaverse::TerrainPatchGroupHeader) -> Result, crate::Error> owned,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.TerrainManager.#ctor(LibreMetaverse.GridClient) constructor LibreMetaverse.TerrainManager(LibreMetaverse.GridClient client) libremetaverse libremetaverse::TerrainManager::new pub fn new(client: libremetaverse::GridClient) -> Result owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.TerrainManager.Dispose method System.Void LibreMetaverse.TerrainManager.Dispose() libremetaverse libremetaverse::TerrainManager::dispose pub fn dispose(&self) -> Result<(), crate::Error> shared_self sync Result<_, crate::Error> direct_snake_case method mapped -M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::TerrainManager::get_terrain_material_overrides pub async fn get_terrain_material_overrides(&self, cancellation_token: Option) -> Result>>, crate::Error> shared_self,optional_owned sync Result<_, crate::Error> direct_snake_case method mapped -M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[] overrides, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::TerrainManager::set_terrain_material_overrides pub async fn set_terrain_material_overrides(&self, overrides: Vec>, cancellation_token: Option) -> Result shared_self,owned,optional_owned sync Result<_, crate::Error> direct_snake_case method mapped +M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::TerrainManager::get_terrain_material_overrides pub async fn get_terrain_material_overrides(&self, cancellation_token: Option) -> Result>>, crate::Error> shared_self,optional_owned async Result<_, crate::Error> direct_snake_case method mapped +M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken) method System.Threading.Tasks.Task LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[] overrides, System.Threading.CancellationToken cancellationToken = default) libremetaverse libremetaverse::TerrainManager::set_terrain_material_overrides pub async fn set_terrain_material_overrides(&self, overrides: Vec>, cancellation_token: Option) -> Result shared_self,owned,optional_owned async Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.TerrainPatch.#ctor constructor LibreMetaverse.TerrainPatch() libremetaverse libremetaverse::TerrainPatch::new pub fn new() -> Result none sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.TerrainPatch.Header.setPatchIDs(System.Int32,System.Int32) method System.Int32 LibreMetaverse.TerrainPatch.Header.setPatchIDs(System.Int32 xx, System.Int32 yy) libremetaverse libremetaverse::TerrainPatchHeader::set_patch_i_ds pub fn set_patch_i_ds(&mut self, xx: i32, yy: i32) -> Result mutable_self,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped M:LibreMetaverse.TerseObjectUpdateEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.Primitive,LibreMetaverse.ObjectMovementUpdate,System.UInt16) constructor LibreMetaverse.TerseObjectUpdateEventArgs(LibreMetaverse.Simulator simulator, LibreMetaverse.Primitive prim, LibreMetaverse.ObjectMovementUpdate update, System.UInt16 timeDilation) libremetaverse libremetaverse::TerseObjectUpdateEventArgs::new pub fn new(simulator: libremetaverse::Simulator, prim: libremetaverse::Primitive, update: libremetaverse::ObjectMovementUpdate, time_dilation: u16) -> Result owned,owned,owned,owned sync Result<_, crate::Error> direct_snake_case method mapped @@ -26181,7 +26181,7 @@ P:LibreMetaverse.EventQueueRunningEventArgs.Simulator property LibreMetaverse.Si P:LibreMetaverse.ExperienceInfoEventArgs.ExperienceInfo property LibreMetaverse.Messages.Linden.ExperienceInfoMessage LibreMetaverse.ExperienceInfoEventArgs.ExperienceInfo libremetaverse libremetaverse::ExperienceInfoEventArgs::experience_info pub fn experience_info(&self) -> libremetaverse::messages::linden::ExperienceInfoMessage shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.ExperiencePreferencesEventArgs.Preferences property LibreMetaverse.Messages.Linden.ExperiencePreferencesMessage LibreMetaverse.ExperiencePreferencesEventArgs.Preferences libremetaverse libremetaverse::ExperiencePreferencesEventArgs::preferences pub fn preferences(&self) -> libremetaverse::messages::linden::ExperiencePreferencesMessage shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.ExpiringCache`2.Count property System.Int32 LibreMetaverse.ExpiringCache.Count libremetaverse-types libremetaverse_types::ExpiringCache::count pub fn count(&self) -> i32 shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped -P:LibreMetaverse.ExpiringCache`2.Item(`0) indexer System.Object LibreMetaverse.ExpiringCache.Item libremetaverse-types libremetaverse_types::ExpiringCache::item pub fn item(&self, key: TKey) -> Option shared_self,owned sync Result<_, crate::Error> direct_snake_case method mapped +P:LibreMetaverse.ExpiringCache`2.Item(`0) indexer System.Object LibreMetaverse.ExpiringCache.Item libremetaverse-types libremetaverse_types::ExpiringCache::item pub fn item(&self, key: TKey) -> Option shared_self,owned sync none direct_snake_case method mapped P:LibreMetaverse.FindObjectByPathReplyEventArgs.InventoryObjectID property LibreMetaverse.UUID LibreMetaverse.FindObjectByPathReplyEventArgs.InventoryObjectID libremetaverse libremetaverse::FindObjectByPathReplyEventArgs::inventory_object_id pub fn inventory_object_id(&self) -> libremetaverse_types::UUID shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.FindObjectByPathReplyEventArgs.Path property System.String LibreMetaverse.FindObjectByPathReplyEventArgs.Path libremetaverse libremetaverse::FindObjectByPathReplyEventArgs::path pub fn path(&self) -> String shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped P:LibreMetaverse.FolderUpdatedEventArgs.FolderID property LibreMetaverse.UUID LibreMetaverse.FolderUpdatedEventArgs.FolderID libremetaverse libremetaverse::FolderUpdatedEventArgs::folder_id pub fn folder_id(&self) -> libremetaverse_types::UUID shared_self sync infallible_placeholder direct_snake_case accessor_methods mapped diff --git a/api/SEMVER-AUDIT.md b/api/SEMVER-AUDIT.md new file mode 100644 index 0000000..260aeb2 --- /dev/null +++ b/api/SEMVER-AUDIT.md @@ -0,0 +1,56 @@ +# First-release API and SemVer audit + +Generated by `metacrate-ci-matrix api-baseline-write`; do not edit by hand. + +The compatibility source is pinned at [`2aa70bb68513b39795da5d13c88f31b86e85a3ba`](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba). The baseline version is `0.0.1`. + +## Coverage and quality decisions + +- Mapped/exported public types: **3066 / 3066**; unexplained mappings: **0**. +- Mapped/exported public members: **30789 / 30789**; duplicate destinations: **0**. +- Rust naming and ownership: mapped function/constant names follow Rust conventions and every member uses a reviewed ownership term. +- External signature types: **142**; referenced support traits: **2**; accidental dependency types exposed by mapped signatures or public Rust declarations: **0**. +- Intentional delegate APM replacements: **80**; each uses a native callback and is compiled by the downstream fixture. +- Descriptively named overloads: **7151**; names remain fixed by the mapping ledger. +- Native `async fn` mappings: **334**; boxed object-safe trait futures with explicit `Send`: **26**. +- Callback signatures requiring `Send + Sync`: **137**; reviewed dyn-compatible thread-safe boundary traits: **6** (`IGridClient`, `IBakingTextureProvider`, `ICurrentOutfitPolicy`, `IRlvActionCallbacks`, `IRlvQueryCallbacks`, and `IVoiceLogger`). +- C# enum mappings: **243** (46 unknown-bit-retaining flag types); stable public error enums marked `non_exhaustive`: **7**. + +## Crate baselines + +| Crate | Mapped types | Mapped members | Rust declarations | Features | Declaration SHA-256 | +| --- | ---: | ---: | ---: | --- | --- | +| `libremetaverse` | 2711 | 27281 | 24505 | dds-bc67, default, jpeg2000, vorbis | `6eb9365e6833584b741afdb9816ec911f4d922aba561daedc0877d711de474b3` | +| `libremetaverse-types` | 45 | 942 | 871 | none | `c5ea3a9cab936bbb4c7fa527753ba5608de0115f13f5c59ad6a7c549dc0d97ca` | +| `libremetaverse-structured-data` | 16 | 295 | 304 | none | `b1c2ca9a683efe92ea22293384189f9e7c9a1926860893ab78c41829e641a8c3` | +| `libremetaverse-imaging` | 3 | 20 | 63 | default, jpeg2000 | `bc3f5cf5a8f472b3d4092032be35202787f443f06a4998fe2533e21aadef2e86` | +| `libremetaverse-imaging-skia` | 1 | 3 | 13 | default, skia | `a6a1445725410a036204496ad4953c84dcfea1e6a00390ce4e10d12d36f2876b` | +| `libremetaverse-prim-mesher` | 17 | 207 | 117 | none | `dde798e86c485948f1d67e093f8194a76082101416516df3f8dca4de04c2d415` | +| `libremetaverse-rendering-simple` | 1 | 6 | 12 | none | `4dc284ea0abb16de63494435758c8b7c5eeb57a80d2e32a141e488e5861f2fad` | +| `libremetaverse-rendering-mesh-foundry` | 1 | 14 | 25 | none | `3689f6b454061fdae699d5fb66a11482909fa25bda398e26c38327995a9e2896` | +| `libremetaverse-lsl-tools` | 164 | 768 | 870 | none | `030c2418e08b6ea73878ba7d9c3f6983df9d3d32dda46e298fb6755a9f7e9599` | +| `libremetaverse-rlv` | 28 | 499 | 301 | none | `f2e0a4fd808f5bb4e646a92205bac0cfe60497f0888a3ffe9a5a0fcb5dd4250a` | +| `libremetaverse-utilities` | 3 | 13 | 17 | none | `0a3cedfd67031ae890295206a7451b9b4945c16b5fac5d4dce86384fc4524595` | +| `libremetaverse-voice-vivox` | 64 | 531 | 382 | none | `97d0ee1665140421477427115aa961c4ebcd4e80ed8b0e625373fe86d9857899` | +| `libremetaverse-voice-webrtc` | 12 | 210 | 290 | default, real-audio | `08ec9c36f47c51d0ae51089097887898f272417ae3ef410faa9a56f96337c470` | +| `libremetaverse-openjpeg` | 0 | 0 | 64 | none | `9967a29eb406c56afd2a2189e1395efb073c52d2a86cc5958a419cf67de0658b` | +| `libremetaverse-opus` | 0 | 0 | 9 | none | `c474538b08ecbb7d178d5ba63c5d15f21d8647b01f49654dc5c83ac1e691ea47` | + +The exact mapping, exported-marker, feature, and normalized Rust declaration hashes are stored in [`SEMVER-BASELINE.json`](SEMVER-BASELINE.json). A change must be reviewed and classified before the baseline is re-recorded. + +## Intentional Rust migration recipes + +- C# `PascalCase` methods/properties become Rust `snake_case`; overloads use the ledger's stable descriptive suffix, never argument-count dispatch. +- C# reference ownership becomes owned values, shared or mutable borrows, `Arc`-backed managers, and explicit optional values as recorded per member. +- `Task` becomes `async fn -> Result`; object-safe async traits return `Pin> + Send + '_>>`. +- .NET events become `subscribe_*` calls returning an owned `Subscription`; callbacks are `Send + Sync` and dispatch outside internal locks. +- `IDisposable` becomes explicit idempotent shutdown plus `Drop`; cancellation remains a typed `Error::Cancelled`. +- Closed C# enums retain fixed discriminants and are SemVer-breaking to extend; flags retain unknown bits, while public Rust error enums are `non_exhaustive`. +- Optional native backends remain feature-gated (`jpeg2000`, `skia`, `vorbis`, `real-audio`); default and empty-default sets are part of the baseline. +- Capability HTTP injection uses project-owned `HttpMessageHandler`, `CapsHttpLimits`, and `with_native_transport`; the internal `reqwest` client is not part of the public surface. +- External C# signature types map to core/std, adopted cross-platform crates, or project-owned boundary types; no third-party crate path leaks through mapped signatures or public Rust declarations. + +## Compile consumers + +- `tests/api-compile` compiles every mapped type/member from a standalone downstream workspace. +- `tests/semver-port` is a runnable representative port covering lifecycle, value types, structured data, imaging, RLV, cancellation, dyn trait, and `Send`/`Sync` contracts without network access. diff --git a/api/SEMVER-BASELINE.json b/api/SEMVER-BASELINE.json new file mode 100644 index 0000000..ae2091d --- /dev/null +++ b/api/SEMVER-BASELINE.json @@ -0,0 +1,189 @@ +{ + "schema": 1, + "release": "0.0.1", + "upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba", + "mapped_public_types": 3066, + "mapped_members": 30789, + "external_signature_types": 142, + "support_traits": 2, + "intentional_differences": 80, + "descriptive_overloads": 7151, + "async_functions": 334, + "send_boxed_futures": 26, + "thread_safe_callbacks": 137, + "thread_safe_dyn_traits": 6, + "non_exhaustive_error_enums": 7, + "enum_types": 243, + "bitflag_types": 46, + "unexplained_mappings": 0, + "accidental_public_dependency_types": 0, + "type_mapping_sha256": "21025889c62b0f8666a2dda46a03ff4f28931f51156927822a1c04e97b49f221", + "member_mapping_sha256": "14a40b155c93a8d3def627e1774464b1f710e6857ed249145ed63304ed684ac8", + "exported_mapping_sha256": "5b8a502ce229d26732744f2b44c029fc4a86b403966dc2f9b4a1c913a7b242b9", + "rust_declaration_sha256": "99ea016ab97a4c195f045fc6c22a0a76a5616f436c485bfd7e82568177762219", + "crates": [ + { + "name": "libremetaverse", + "version": "0.0.1", + "features": { + "dds-bc67": [ + "dep:bcdec_rs" + ], + "default": [ + "dds-bc67" + ], + "jpeg2000": [ + "libremetaverse-imaging/jpeg2000" + ], + "vorbis": [ + "dep:vorbis_rs" + ] + }, + "mapped_types": 2711, + "mapped_members": 27281, + "public_declarations": 24505, + "declaration_sha256": "6eb9365e6833584b741afdb9816ec911f4d922aba561daedc0877d711de474b3" + }, + { + "name": "libremetaverse-types", + "version": "0.0.1", + "features": {}, + "mapped_types": 45, + "mapped_members": 942, + "public_declarations": 871, + "declaration_sha256": "c5ea3a9cab936bbb4c7fa527753ba5608de0115f13f5c59ad6a7c549dc0d97ca" + }, + { + "name": "libremetaverse-structured-data", + "version": "0.0.1", + "features": {}, + "mapped_types": 16, + "mapped_members": 295, + "public_declarations": 304, + "declaration_sha256": "b1c2ca9a683efe92ea22293384189f9e7c9a1926860893ab78c41829e641a8c3" + }, + { + "name": "libremetaverse-imaging", + "version": "0.0.1", + "features": { + "default": [], + "jpeg2000": [ + "dep:libremetaverse-openjpeg" + ] + }, + "mapped_types": 3, + "mapped_members": 20, + "public_declarations": 63, + "declaration_sha256": "bc3f5cf5a8f472b3d4092032be35202787f443f06a4998fe2533e21aadef2e86" + }, + { + "name": "libremetaverse-imaging-skia", + "version": "0.0.1", + "features": { + "default": [], + "skia": [ + "dep:skia-safe" + ] + }, + "mapped_types": 1, + "mapped_members": 3, + "public_declarations": 13, + "declaration_sha256": "a6a1445725410a036204496ad4953c84dcfea1e6a00390ce4e10d12d36f2876b" + }, + { + "name": "libremetaverse-prim-mesher", + "version": "0.0.1", + "features": {}, + "mapped_types": 17, + "mapped_members": 207, + "public_declarations": 117, + "declaration_sha256": "dde798e86c485948f1d67e093f8194a76082101416516df3f8dca4de04c2d415" + }, + { + "name": "libremetaverse-rendering-simple", + "version": "0.0.1", + "features": {}, + "mapped_types": 1, + "mapped_members": 6, + "public_declarations": 12, + "declaration_sha256": "4dc284ea0abb16de63494435758c8b7c5eeb57a80d2e32a141e488e5861f2fad" + }, + { + "name": "libremetaverse-rendering-mesh-foundry", + "version": "0.0.1", + "features": {}, + "mapped_types": 1, + "mapped_members": 14, + "public_declarations": 25, + "declaration_sha256": "3689f6b454061fdae699d5fb66a11482909fa25bda398e26c38327995a9e2896" + }, + { + "name": "libremetaverse-lsl-tools", + "version": "0.0.1", + "features": {}, + "mapped_types": 164, + "mapped_members": 768, + "public_declarations": 870, + "declaration_sha256": "030c2418e08b6ea73878ba7d9c3f6983df9d3d32dda46e298fb6755a9f7e9599" + }, + { + "name": "libremetaverse-rlv", + "version": "0.0.1", + "features": {}, + "mapped_types": 28, + "mapped_members": 499, + "public_declarations": 301, + "declaration_sha256": "f2e0a4fd808f5bb4e646a92205bac0cfe60497f0888a3ffe9a5a0fcb5dd4250a" + }, + { + "name": "libremetaverse-utilities", + "version": "0.0.1", + "features": {}, + "mapped_types": 3, + "mapped_members": 13, + "public_declarations": 17, + "declaration_sha256": "0a3cedfd67031ae890295206a7451b9b4945c16b5fac5d4dce86384fc4524595" + }, + { + "name": "libremetaverse-voice-vivox", + "version": "0.0.1", + "features": {}, + "mapped_types": 64, + "mapped_members": 531, + "public_declarations": 382, + "declaration_sha256": "97d0ee1665140421477427115aa961c4ebcd4e80ed8b0e625373fe86d9857899" + }, + { + "name": "libremetaverse-voice-webrtc", + "version": "0.0.1", + "features": { + "default": [], + "real-audio": [ + "dep:cpal" + ] + }, + "mapped_types": 12, + "mapped_members": 210, + "public_declarations": 290, + "declaration_sha256": "08ec9c36f47c51d0ae51089097887898f272417ae3ef410faa9a56f96337c470" + }, + { + "name": "libremetaverse-openjpeg", + "version": "0.0.1", + "features": {}, + "mapped_types": 0, + "mapped_members": 0, + "public_declarations": 64, + "declaration_sha256": "9967a29eb406c56afd2a2189e1395efb073c52d2a86cc5958a419cf67de0658b" + }, + { + "name": "libremetaverse-opus", + "version": "0.0.1", + "features": {}, + "mapped_types": 0, + "mapped_members": 0, + "public_declarations": 9, + "declaration_sha256": "c474538b08ecbb7d178d5ba63c5d15f21d8647b01f49654dc5c83ac1e691ea47" + } + ] +} diff --git a/ci/evidence/api-audit.json b/ci/evidence/api-audit.json new file mode 100644 index 0000000..2e992a6 --- /dev/null +++ b/ci/evidence/api-audit.json @@ -0,0 +1,16 @@ +{ + "schema": 1, + "release": "0.0.1", + "upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba", + "baseline_sha256": "9792357b40cd1cd888722dff2f97f19fbdc33a63d23c5bc451b717b17884a4e6", + "mapped_public_types": 3066, + "mapped_members": 30789, + "intentional_differences": 80, + "unexplained_mappings": 0, + "accidental_public_dependency_types": 0, + "thread_safe_dyn_traits": 6, + "non_exhaustive_error_enums": 7, + "downstream_compile_fixture": "tests/api-compile", + "representative_port": "tests/semver-port", + "status": "ok" +} diff --git a/ci/evidence/documentation-audit.json b/ci/evidence/documentation-audit.json index 1aa2b77..3ff6a9f 100644 --- a/ci/evidence/documentation-audit.json +++ b/ci/evidence/documentation-audit.json @@ -8,7 +8,7 @@ "documented_members": 30789, "tested_rust_snippets": 5, "linked_programs": 9, - "checked_local_links": 65, + "checked_local_links": 69, "required_guide_sections": 11, "status": "ok" } diff --git a/crates/libremetaverse-imaging/src/jpeg2000.rs b/crates/libremetaverse-imaging/src/jpeg2000.rs index 56d86d1..ff9ac0d 100644 --- a/crates/libremetaverse-imaging/src/jpeg2000.rs +++ b/crates/libremetaverse-imaging/src/jpeg2000.rs @@ -501,6 +501,7 @@ const fn map_decode_error(error: openjpeg::Error) -> Error { openjpeg::Error::LimitExceeded => Error::Argument, openjpeg::Error::Allocation => Error::InvalidOperation, openjpeg::Error::InvalidInput | openjpeg::Error::Codec => parse("JPEG 2000 codestream"), + _ => parse("JPEG 2000 backend"), } } @@ -508,6 +509,7 @@ const fn map_encode_error(error: openjpeg::Error) -> Error { match error { openjpeg::Error::InvalidInput | openjpeg::Error::LimitExceeded => Error::Argument, openjpeg::Error::Allocation | openjpeg::Error::Codec => Error::InvalidOperation, + _ => Error::InvalidOperation, } } diff --git a/crates/libremetaverse-openjpeg/src/lib.rs b/crates/libremetaverse-openjpeg/src/lib.rs index c292bad..8ac5dd3 100644 --- a/crates/libremetaverse-openjpeg/src/lib.rs +++ b/crates/libremetaverse-openjpeg/src/lib.rs @@ -76,6 +76,7 @@ pub struct ComponentRef<'a> { /// Stable error categories; `OpenJPEG` diagnostics never cross the safe API. #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum Error { InvalidInput, LimitExceeded, diff --git a/crates/libremetaverse-rlv/src/protocol.rs b/crates/libremetaverse-rlv/src/protocol.rs index 35c6ff1..7d636af 100644 --- a/crates/libremetaverse-rlv/src/protocol.rs +++ b/crates/libremetaverse-rlv/src/protocol.rs @@ -30,6 +30,7 @@ pub struct RlvSourceSpan { /// Stable malformed-input categories exposed by the native parser. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] pub enum RlvParseErrorKind { MessageTooLong, MissingPrefix, diff --git a/crates/libremetaverse-types/src/generated.rs b/crates/libremetaverse-types/src/generated.rs index 7c7bf17..cd60df1 100644 --- a/crates/libremetaverse-types/src/generated.rs +++ b/crates/libremetaverse-types/src/generated.rs @@ -1133,7 +1133,7 @@ pub use crate::enum_compat::EnumInfoExtensions; /// /// C# signature: `System.Object LibreMetaverse.ExpiringCache.Item`. /// Mapping contract: ownership `shared_self,owned`; async `sync`; -/// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. +/// errors `none`; overload `direct_snake_case`; kind `method`. pub use crate::collections::ExpiringCache; /// C# type: `T:LibreMetaverse.ExtraParamType`. diff --git a/crates/libremetaverse-types/src/shim.rs b/crates/libremetaverse-types/src/shim.rs index 9a2cb38..e9693f6 100644 --- a/crates/libremetaverse-types/src/shim.rs +++ b/crates/libremetaverse-types/src/shim.rs @@ -44,6 +44,7 @@ impl std::error::Error for NotImplemented {} /// Shared error contract for translated fallible APIs. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] pub enum Error { /// The API surface exists but its production body has not been ported yet. NotImplemented(NotImplemented), diff --git a/crates/libremetaverse-voice-vivox/src/protocol.rs b/crates/libremetaverse-voice-vivox/src/protocol.rs index 0d8e226..f57ad61 100644 --- a/crates/libremetaverse-voice-vivox/src/protocol.rs +++ b/crates/libremetaverse-voice-vivox/src/protocol.rs @@ -157,6 +157,7 @@ impl fmt::Debug for VivoxEvent { /// Failure from the native Vivox control protocol. #[derive(Debug)] +#[non_exhaustive] pub enum VivoxError { /// TCP or stream I/O failed. Io(io::Error), diff --git a/crates/libremetaverse-voice-webrtc/src/generated.rs b/crates/libremetaverse-voice-webrtc/src/generated.rs index 3f1cd4c..d7a32df 100644 --- a/crates/libremetaverse-voice-webrtc/src/generated.rs +++ b/crates/libremetaverse-voice-webrtc/src/generated.rs @@ -9,7 +9,7 @@ /// /// Native Rust mapping of C# `LibreMetaverse.Voice.WebRTC.IVoiceLogger` using decision /// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba). -pub trait IVoiceLogger { +pub trait IVoiceLogger: Send + Sync { /// C# member: `M:LibreMetaverse.Voice.WebRTC.IVoiceLogger.Debug(System.String,LibreMetaverse.GridClient)`. /// /// C# signature: `System.Void LibreMetaverse.Voice.WebRTC.IVoiceLogger.Debug(System.String message, LibreMetaverse.GridClient client = default)`. diff --git a/crates/libremetaverse-voice-webrtc/src/native.rs b/crates/libremetaverse-voice-webrtc/src/native.rs index 82f0c32..85afb7e 100644 --- a/crates/libremetaverse-voice-webrtc/src/native.rs +++ b/crates/libremetaverse-voice-webrtc/src/native.rs @@ -42,6 +42,7 @@ const MAX_DATA_BYTES: usize = 64 * 1024; /// Errors produced by the native WebRTC adapter. #[derive(Debug)] +#[non_exhaustive] pub enum WebRtcError { InvalidInput(&'static str), Io(std::io::Error), diff --git a/crates/libremetaverse/src/caps_http.rs b/crates/libremetaverse/src/caps_http.rs index 3510e1f..6690164 100644 --- a/crates/libremetaverse/src/caps_http.rs +++ b/crates/libremetaverse/src/caps_http.rs @@ -557,7 +557,7 @@ struct HttpCapsClientInner { disposed: AtomicBool, } -/// Native capability HTTP client with injectable fake and reqwest backends. +/// Native capability HTTP client with injectable fake and production backends. #[derive(Clone)] pub struct HttpCapsClient(Arc); @@ -614,24 +614,47 @@ impl HttpCapsClient { }))) } - pub fn with_reqwest_client( - client: reqwest::Client, + /// Creates the production HTTP backend without exposing the selected HTTP + /// implementation in the public API. + pub fn with_native_transport( + user_agent: &str, + timeout: Duration, + max_connections: usize, rate_limiter: Option, limits: CapsHttpLimits, ) -> Result { - Self::with_reqwest_client_and_concurrency(client, rate_limiter, limits, 32) + if timeout.is_zero() || max_connections == 0 { + return Err(Error::Argument); + } + limits.validate()?; + let max_redirects = limits.max_redirects; + let redirect = reqwest::redirect::Policy::custom(move |attempt| { + if attempt.previous().len() >= max_redirects { + return attempt.stop(); + } + match attempt.url().scheme() { + "http" | "https" => attempt.follow(), + _ => attempt.stop(), + } + }); + let client = reqwest::Client::builder() + .redirect(redirect) + .timeout(timeout) + .connect_timeout(timeout) + .pool_max_idle_per_host(max_connections) + .user_agent(user_agent) + .build() + .map_err(|_| Error::HttpRequest)?; + Self::with_reqwest_client(client, rate_limiter, limits, max_connections) } - fn with_reqwest_client_and_concurrency( + fn with_reqwest_client( client: reqwest::Client, rate_limiter: Option, limits: CapsHttpLimits, max_connections: usize, ) -> Result { limits.validate()?; - if max_connections == 0 { - return Err(Error::Argument); - } Ok(Self(Arc::new(HttpCapsClientInner { backend: HttpBackend::Reqwest(client), limits, @@ -648,33 +671,12 @@ impl HttpCapsClient { max_connections: usize, rate_limiter: CapsRateLimiter, ) -> Result { - if max_connections == 0 { - return Err(Error::Argument); - } - let limits = CapsHttpLimits::default(); - let max_redirects = limits.max_redirects; - let redirect = reqwest::redirect::Policy::custom(move |attempt| { - if attempt.previous().len() > max_redirects { - return attempt.stop(); - } - match attempt.url().scheme() { - "http" | "https" => attempt.follow(), - _ => attempt.stop(), - } - }); - let client = reqwest::Client::builder() - .redirect(redirect) - .timeout(timeout) - .connect_timeout(timeout) - .pool_max_idle_per_host(max_connections) - .user_agent(user_agent) - .build() - .map_err(|_| Error::HttpRequest)?; - Self::with_reqwest_client_and_concurrency( - client, - Some(rate_limiter), - limits, + Self::with_native_transport( + user_agent, + timeout, max_connections, + Some(rate_limiter), + CapsHttpLimits::default(), ) } diff --git a/crates/libremetaverse/src/client_core.rs b/crates/libremetaverse/src/client_core.rs index ec27d42..28bb43c 100644 --- a/crates/libremetaverse/src/client_core.rs +++ b/crates/libremetaverse/src/client_core.rs @@ -11,6 +11,7 @@ use std::sync::{Arc, Condvar, Mutex}; /// A validated client-core failure with no credential or capability payload. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] pub enum ClientCoreError { /// A public settings field failed validation. InvalidConfiguration { diff --git a/crates/libremetaverse/src/generated.rs b/crates/libremetaverse/src/generated.rs index b35d889..f4af055 100644 --- a/crates/libremetaverse/src/generated.rs +++ b/crates/libremetaverse/src/generated.rs @@ -17035,7 +17035,7 @@ pub trait IBakingTextureProvider: std::any::Any + Send + Sync { /// /// Native Rust mapping of C# `LibreMetaverse.IGridClient` using decision /// `native_metacrate_type`. See the [pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba). -pub trait IGridClient { +pub trait IGridClient: Send + Sync { /// C# member: `P:LibreMetaverse.IGridClient.AisClient`. /// /// C# signature: `LibreMetaverse.InventoryAISClient LibreMetaverse.IGridClient.AisClient`. @@ -19408,7 +19408,7 @@ impl InventoryManager { /// C# member: `M:LibreMetaverse.InventoryManager.CreateLinksAsync(LibreMetaverse.UUID,System.Collections.Generic.IEnumerable{System.ValueTuple{LibreMetaverse.InventoryBase,System.String}},System.Action{System.Boolean},System.Threading.CancellationToken)`. /// /// C# signature: `System.Threading.Tasks.Task LibreMetaverse.InventoryManager.CreateLinksAsync(LibreMetaverse.UUID folderID, System.Collections.Generic.IEnumerable> linksToCreate, System.Action callback, System.Threading.CancellationToken cancellationToken = default)`. - /// Mapping contract: ownership `shared_self,owned,owned,optional_owned,optional_owned`; async `sync`; + /// Mapping contract: ownership `shared_self,owned,owned,optional_owned,optional_owned`; async `async`; /// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. pub async fn create_links( &self, @@ -19562,7 +19562,7 @@ impl InventoryManager { /// C# member: `M:LibreMetaverse.InventoryManager.FolderContentsAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.Boolean,System.Boolean,LibreMetaverse.InventorySortOrder,System.Threading.CancellationToken,System.Boolean)`. /// /// C# signature: `System.Threading.Tasks.Task> LibreMetaverse.InventoryManager.FolderContentsAsync(LibreMetaverse.UUID folder, LibreMetaverse.UUID owner, System.Boolean fetchFolders, System.Boolean fetchItems, LibreMetaverse.InventorySortOrder order, System.Threading.CancellationToken cancellationToken = default, System.Boolean followLinks = default)`. - /// Mapping contract: ownership `shared_self,owned,owned,owned,owned,owned,optional_owned,optional_owned`; async `sync`; + /// Mapping contract: ownership `shared_self,owned,owned,owned,owned,owned,optional_owned,optional_owned`; async `async`; /// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. pub async fn folder_contents( &self, @@ -19604,7 +19604,7 @@ impl InventoryManager { /// C# member: `M:LibreMetaverse.InventoryManager.GetTaskInventoryAsync(LibreMetaverse.UUID,System.UInt32,LibreMetaverse.Simulator,System.Threading.CancellationToken)`. /// /// C# signature: `System.Threading.Tasks.Task> LibreMetaverse.InventoryManager.GetTaskInventoryAsync(LibreMetaverse.UUID objectID, System.UInt32 objectLocalID, LibreMetaverse.Simulator simulator = default, System.Threading.CancellationToken cancellationToken = default)`. - /// Mapping contract: ownership `shared_self,owned,owned,optional_owned,optional_owned`; async `sync`; + /// Mapping contract: ownership `shared_self,owned,owned,optional_owned,optional_owned`; async `async`; /// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. pub async fn get_task_inventory( &self, @@ -35686,12 +35686,12 @@ pub use crate::terrain_codec::TerrainCompressor; /// C# member: `M:LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken)`. /// /// C# signature: `System.Threading.Tasks.Task LibreMetaverse.TerrainManager.GetTerrainMaterialOverridesAsync(System.Threading.CancellationToken cancellationToken = default)`. -/// Mapping contract: ownership `shared_self,optional_owned`; async `sync`; +/// Mapping contract: ownership `shared_self,optional_owned`; async `async`; /// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. /// C# member: `M:LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[],System.Threading.CancellationToken)`. /// /// C# signature: `System.Threading.Tasks.Task LibreMetaverse.TerrainManager.SetTerrainMaterialOverridesAsync(LibreMetaverse.Assets.AssetMaterial[] overrides, System.Threading.CancellationToken cancellationToken = default)`. -/// Mapping contract: ownership `shared_self,owned,optional_owned`; async `sync`; +/// Mapping contract: ownership `shared_self,owned,optional_owned`; async `async`; /// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. pub use crate::terrain_manager::TerrainManager; @@ -37970,7 +37970,7 @@ pub mod appearance { /// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)`. /// /// C# signature: `System.Threading.Tasks.Task LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List addedItems, System.Collections.Generic.List removedItems, System.Threading.CancellationToken cancellationToken = default)`. - /// Mapping contract: ownership `shared_self,optional_shared,optional_shared,optional_owned`; async `sync`; + /// Mapping contract: ownership `shared_self,optional_shared,optional_shared,optional_owned`; async `async`; /// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. pub async fn report_item_change( &self, @@ -38273,7 +38273,7 @@ pub mod appearance { /// C# member: `M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)`. /// /// C# signature: `System.Threading.Tasks.Task LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List addedItems, System.Collections.Generic.List removedItems, System.Threading.CancellationToken cancellationToken = default)`. - /// Mapping contract: ownership `shared_self,optional_shared,optional_shared,optional_owned`; async `sync`; + /// Mapping contract: ownership `shared_self,optional_shared,optional_shared,optional_owned`; async `boxed_future`; /// errors `Result<_, crate::Error>`; overload `direct_snake_case`; kind `method`. fn report_item_change( &self, diff --git a/crates/libremetaverse/src/udp_transport.rs b/crates/libremetaverse/src/udp_transport.rs index 7b6acdc..0a87851 100644 --- a/crates/libremetaverse/src/udp_transport.rs +++ b/crates/libremetaverse/src/udp_transport.rs @@ -29,6 +29,7 @@ const THROTTLE_BURST_PERIODS: usize = 4; /// The variants deliberately carry no datagram bytes, credentials, endpoint /// query data, or operating-system error strings. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] pub enum UdpTransportError { InvalidConfiguration(&'static str), InvalidBuffer, diff --git a/crates/libremetaverse/tests/caps_http.rs b/crates/libremetaverse/tests/caps_http.rs index 1e3a474..99116a5 100644 --- a/crates/libremetaverse/tests/caps_http.rs +++ b/crates/libremetaverse/tests/caps_http.rs @@ -797,8 +797,32 @@ async fn read_http_request(stream: &mut tokio::net::TcpStream) -> Vec { } } +#[test] +fn native_transport_rejects_invalid_resource_policy_before_building() { + assert!(matches!( + HttpCapsClient::with_native_transport( + "MetaCrate-caps-test/0.0.1", + Duration::ZERO, + 1, + None, + CapsHttpLimits::default(), + ), + Err(Error::Argument) + )); + assert!(matches!( + HttpCapsClient::with_native_transport( + "MetaCrate-caps-test/0.0.1", + Duration::from_secs(1), + 0, + None, + CapsHttpLimits::default(), + ), + Err(Error::Argument) + )); +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn injected_reqwest_backend_follows_redirects_streams_and_decompresses() { +async fn native_http_backend_follows_redirects_streams_and_decompresses() { let listener = tokio::net::TcpListener::bind((std::net::Ipv4Addr::LOCALHOST, 0)) .await .expect("bind fake server"); @@ -842,12 +866,10 @@ async fn injected_reqwest_backend_follows_redirects_streams_and_decompresses() { } }); - let reqwest = reqwest::Client::builder() - .redirect(reqwest::redirect::Policy::limited(3)) - .build() - .expect("reqwest client"); - let client = HttpCapsClient::with_reqwest_client( - reqwest, + let client = HttpCapsClient::with_native_transport( + "MetaCrate-caps-test/0.0.1", + Duration::from_secs(5), + 4, None, CapsHttpLimits { max_request_bytes: 1_024, diff --git a/tests/semver-port/Cargo.lock b/tests/semver-port/Cargo.lock new file mode 100644 index 0000000..24c16cf --- /dev/null +++ b/tests/semver-port/Cargo.lock @@ -0,0 +1,3187 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "asn1-rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 2.0.20", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-lc-rs" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bcdec_rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09c37bc0e9f0924b7dae9988265ef3c76c88538f41a3b06caf4bed07cee5226" + +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "ccm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae3c82e4355234767756212c570e29833699ab63e6ffd161887314cc5b43847" +dependencies = [ + "aead", + "cipher", + "ctr", + "subtle", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20 0.9.1", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", + "zeroize", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid 0.10.2", + "crypto-common 0.2.2", +] + +[[package]] +name = "dimpl" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6aa42b0c64c3e5311a2afad224b32db1ee129d21c63daaaf8ea747b846cdbc" +dependencies = [ + "aes", + "aes-gcm", + "arrayvec", + "aws-lc-rs", + "ccm", + "chacha20 0.9.1", + "chacha20poly1305", + "der", + "ecdsa", + "generic-array", + "hkdf", + "hmac", + "log", + "nom 8.0.0", + "once_cell", + "p256", + "p384", + "pkcs8", + "rand 0.9.5", + "rand_core 0.6.4", + "rcgen", + "sec1", + "sha2 0.10.9", + "signature", + "spki", + "subtle", + "time", + "x25519-dalek", + "x509-cert", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" + +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" + +[[package]] +name = "futures-io" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" + +[[package]] +name = "futures-macro" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "futures-sink" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" + +[[package]] +name = "futures-task" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" + +[[package]] +name = "futures-util" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hybrid-array" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" + +[[package]] +name = "is" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08f9118d003d441f79c1070e84d0a2a89f35721ca8ae0cd5e1f9026dc4a2517" +dependencies = [ + "crc", + "serde", + "str0m-proto", + "subtle", + "tracing", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.20", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libremetaverse" +version = "0.0.1" +dependencies = [ + "base64", + "bcdec_rs", + "flate2", + "futures-channel", + "futures-util", + "libremetaverse-imaging", + "libremetaverse-structured-data", + "libremetaverse-types", + "mac_address2", + "os_info", + "reqwest", + "roxmltree", + "serde_json", + "tar", + "tokio", +] + +[[package]] +name = "libremetaverse-imaging" +version = "0.0.1" +dependencies = [ + "libremetaverse-types", +] + +[[package]] +name = "libremetaverse-opus" +version = "0.0.1" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libremetaverse-rlv" +version = "0.0.1" +dependencies = [ + "libremetaverse-types", +] + +[[package]] +name = "libremetaverse-structured-data" +version = "0.0.1" +dependencies = [ + "base64", + "libremetaverse-types", + "serde", + "serde_json", +] + +[[package]] +name = "libremetaverse-types" +version = "0.0.1" +dependencies = [ + "base64", + "getrandom 0.4.3", + "md-5", + "sha1", + "sha2 0.11.0", + "uuid", +] + +[[package]] +name = "libremetaverse-voice-webrtc" +version = "0.0.1" +dependencies = [ + "hound", + "libremetaverse", + "libremetaverse-opus", + "libremetaverse-structured-data", + "libremetaverse-types", + "serde", + "serde_json", + "str0m", + "tokio", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "mac_address2" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc5e04aa39a4dc96076bca67bf60b63201ff06f9568e62f7904293d39de8cb0b" +dependencies = [ + "nix 0.28.0", + "thiserror 1.0.69", + "widestring", + "windows-sys 0.52.0", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metacrate-semver-port" +version = "0.0.0" +dependencies = [ + "libremetaverse", + "libremetaverse-imaging", + "libremetaverse-rlv", + "libremetaverse-structured-data", + "libremetaverse-types", + "libremetaverse-voice-webrtc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases 0.2.2", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix 0.31.3", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "windows-sys 0.61.2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases 0.2.2", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.20", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.2", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.20", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases 0.2.2", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20 0.10.1", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + +[[package]] +name = "rcgen" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091e7a8e7d86e6feb87a27ce8e2cba29d49eff9507afeebefab7eeb2ca667fb4" +dependencies = [ + "aws-lc-rs", + "rustls-pki-types", + "time", + "x509-parser", + "yasna", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "roxmltree" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "sctp-proto" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f895c3c33ae20283f9129bd09db2ca69138798b372ef2b98bd2946d23ea4819b" +dependencies = [ + "bytes", + "crc", + "log", + "rand 0.9.5", + "rustc-hash", + "slab", + "thiserror 2.0.20", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "str0m" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4656b60e74d1a0cf7c407b0d992c5bc141c3e75193f4b1399817d54a180505f3" +dependencies = [ + "arrayvec", + "base64ct", + "combine", + "dimpl", + "fastrand", + "is", + "sctp-proto", + "serde", + "str0m-proto", + "str0m-rust-crypto", + "subtle", + "time", + "tracing", +] + +[[package]] +name = "str0m-proto" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8f3d99f2cf6c76a502e45feb896ea71e54787ede8744bcdb215acfbfcb905dd" +dependencies = [ + "base64ct", + "dimpl", + "fastrand", + "serde", + "subtle", + "time", +] + +[[package]] +name = "str0m-rust-crypto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947c2417bf43e47504911f92f47c22b6125a57d162e1661f1c77b78ebb3aa9d3" +dependencies = [ + "aes", + "aes-gcm", + "ctr", + "dimpl", + "hmac", + "p256", + "sha1", + "sha2 0.10.9", + "str0m-proto", + "time", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid 0.9.6", + "der", + "spki", +] + +[[package]] +name = "x509-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" +dependencies = [ + "asn1-rs", + "aws-lc-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.20", + "time", +] + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "yasna" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" +dependencies = [ + "bit-vec", + "time", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/tests/semver-port/Cargo.toml b/tests/semver-port/Cargo.toml new file mode 100644 index 0000000..24bf466 --- /dev/null +++ b/tests/semver-port/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "metacrate-semver-port" +version = "0.0.0" +edition = "2024" +rust-version = "1.96" +license = "BSD-3-Clause" +publish = false + +[dependencies] +libremetaverse = { path = "../../crates/libremetaverse" } +libremetaverse-imaging = { path = "../../crates/libremetaverse-imaging" } +libremetaverse-rlv = { path = "../../crates/libremetaverse-rlv" } +libremetaverse-structured-data = { path = "../../crates/libremetaverse-structured-data" } +libremetaverse-types = { path = "../../crates/libremetaverse-types" } +libremetaverse-voice-webrtc = { path = "../../crates/libremetaverse-voice-webrtc" } + +[workspace] diff --git a/tests/semver-port/src/main.rs b/tests/semver-port/src/main.rs new file mode 100644 index 0000000..7e75b4a --- /dev/null +++ b/tests/semver-port/src/main.rs @@ -0,0 +1,72 @@ +use libremetaverse::appearance::ICurrentOutfitPolicy; +use libremetaverse::{ + CapsHttpLimits, GridClient, HttpCapsClient, IBakingTextureProvider, IGridClient, +}; +use libremetaverse_imaging::{ManagedImage, ManagedImageImageChannels}; +use libremetaverse_rlv::{IRlvActionCallbacks, IRlvQueryCallbacks, RlvParser}; +use libremetaverse_structured_data::OSD; +use libremetaverse_types::UUID; +use libremetaverse_types::compat::CancellationTokenSource; +use libremetaverse_voice_webrtc::IVoiceLogger; +use std::future::Future; +use std::time::Duration; + +fn assert_send_sync() {} + +fn assert_send_future(future: T) { + drop(future); +} + +#[allow(clippy::type_complexity)] +fn assert_dyn_compatible( + _grid: Option<&dyn IGridClient>, + _baker: Option<&dyn IBakingTextureProvider>, + _outfit: Option<&dyn ICurrentOutfitPolicy>, + _actions: Option<&dyn IRlvActionCallbacks>, + _queries: Option<&dyn IRlvQueryCallbacks>, + _logger: Option<&dyn IVoiceLogger>, +) { +} + +fn main() -> Result<(), Box> { + assert_send_sync::(); + assert_send_sync::(); + assert_dyn_compatible(None, None, None, None, None, None); + assert_send_sync::>(); + assert_send_sync::>(); + assert_send_sync::>(); + assert_send_sync::>(); + assert_send_sync::>(); + assert_send_sync::>(); + + let cancellation = CancellationTokenSource::new(); + let token = cancellation.token(); + assert_send_future(token.cancelled()); + cancellation.cancel(); + + let id = UUID::new_with_u_int64(103)?; + let value = OSD::from_integer_with_int32(103)?; + assert_eq!(value.as_integer()?, 103); + + let image = ManagedImage::new(2, 2, ManagedImageImageChannels::COLOR)?; + assert_eq!(image.to_interleaved(6)?.len(), 12); + + let rlv = RlvParser::parse_message("@version=42", id.guid(), "SemVer Port")?; + assert_eq!(rlv.commands.len(), 1); + + let native_http = HttpCapsClient::with_native_transport( + "MetaCrate-SemVer-Port/0.0.1", + Duration::from_secs(1), + 1, + None, + CapsHttpLimits::default(), + )?; + drop(native_http); + + let client = GridClient::new()?; + let as_interface: &dyn IGridClient = &client; + assert_eq!(as_interface.settings(), client.settings()); + assert_send_future(client.dispose_with_method_87ebceff()); + client.dispose_with_method()?; + Ok(()) +} diff --git a/tools/ci-matrix/Cargo.toml b/tools/ci-matrix/Cargo.toml index f3e3605..c7cc6a8 100644 --- a/tools/ci-matrix/Cargo.toml +++ b/tools/ci-matrix/Cargo.toml @@ -11,6 +11,7 @@ publish = false [dependencies] serde = { version = "1", features = ["derive"] } serde_json = "1" +sha2 = "0.11" [lints] workspace = true diff --git a/tools/ci-matrix/src/api_surface.rs b/tools/ci-matrix/src/api_surface.rs new file mode 100644 index 0000000..bbef4c6 --- /dev/null +++ b/tools/ci-matrix/src/api_surface.rs @@ -0,0 +1,1338 @@ +use super::{MatrixError, Result}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs::{self, OpenOptions}; +use std::io::Write as _; +use std::path::{Path, PathBuf}; + +const BASELINE_PATH: &str = "api/SEMVER-BASELINE.json"; +const REPORT_PATH: &str = "api/SEMVER-AUDIT.md"; +const MEMBER_PATH: &str = "api/RUST-MAPPING.tsv"; +const TYPE_PATH: &str = "api/RUST-TYPES.tsv"; +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 REQUIRED_NON_EXHAUSTIVE_ERRORS: [(&str, &str); 7] = [ + ("crates/libremetaverse-types/src/shim.rs", "Error"), + ("crates/libremetaverse-openjpeg/src/lib.rs", "Error"), + ( + "crates/libremetaverse-voice-webrtc/src/native.rs", + "WebRtcError", + ), + ( + "crates/libremetaverse/src/client_core.rs", + "ClientCoreError", + ), + ( + "crates/libremetaverse/src/udp_transport.rs", + "UdpTransportError", + ), + ( + "crates/libremetaverse-voice-vivox/src/protocol.rs", + "VivoxError", + ), + ( + "crates/libremetaverse-rlv/src/protocol.rs", + "RlvParseErrorKind", + ), +]; + +const REQUIRED_THREAD_SAFE_TRAITS: [(&str, &str); 6] = [ + ( + "crates/libremetaverse/src/generated.rs", + "pub trait IBakingTextureProvider: std::any::Any + Send + Sync", + ), + ( + "crates/libremetaverse/src/generated.rs", + "pub trait IGridClient: Send + Sync", + ), + ( + "crates/libremetaverse/src/generated.rs", + "pub trait ICurrentOutfitPolicy: Send + Sync", + ), + ( + "crates/libremetaverse-rlv/src/generated.rs", + "pub trait IRlvActionCallbacks: Send + Sync", + ), + ( + "crates/libremetaverse-rlv/src/generated.rs", + "pub trait IRlvQueryCallbacks: Send + Sync", + ), + ( + "crates/libremetaverse-voice-webrtc/src/generated.rs", + "pub trait IVoiceLogger: Send + Sync", + ), +]; + +const OWNERSHIP_TERMS: [&str; 14] = [ + "none", + "owned_value", + "shared_self", + "shared_or_mutable_self", + "mutable_self", + "owned", + "optional_owned", + "mutable_borrow", + "optional_mutable_borrow", + "mutable_output", + "shared_borrow", + "optional_shared", + "shared", + "stored_callback", +]; + +type Row = BTreeMap; + +#[derive(Debug)] +struct QualityCounts { + intentional_differences: usize, + descriptive_overloads: usize, + async_functions: usize, + send_boxed_futures: usize, + thread_safe_callbacks: usize, + enum_types: usize, + bitflag_types: usize, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +struct ApiBaseline { + schema: u32, + release: String, + upstream_commit: String, + mapped_public_types: usize, + mapped_members: usize, + external_signature_types: usize, + support_traits: usize, + intentional_differences: usize, + descriptive_overloads: usize, + async_functions: usize, + send_boxed_futures: usize, + thread_safe_callbacks: usize, + thread_safe_dyn_traits: usize, + non_exhaustive_error_enums: usize, + enum_types: usize, + bitflag_types: usize, + unexplained_mappings: usize, + accidental_public_dependency_types: usize, + type_mapping_sha256: String, + member_mapping_sha256: String, + exported_mapping_sha256: String, + rust_declaration_sha256: String, + crates: Vec, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +struct CrateSurface { + name: String, + version: String, + features: BTreeMap>, + mapped_types: usize, + mapped_members: usize, + public_declarations: usize, + declaration_sha256: String, +} + +#[derive(Debug, Serialize)] +struct ApiEvidence<'a> { + schema: u32, + release: &'a str, + upstream_commit: &'a str, + baseline_sha256: String, + mapped_public_types: usize, + mapped_members: usize, + intentional_differences: usize, + unexplained_mappings: usize, + accidental_public_dependency_types: usize, + thread_safe_dyn_traits: usize, + non_exhaustive_error_enums: usize, + downstream_compile_fixture: &'static str, + representative_port: &'static str, + status: &'static str, +} + +/// Writes the reviewed first-release API/SemVer baseline and audit report. +/// +/// # Errors +/// +/// Returns an error when the mapped/exported surface or a quality invariant is +/// incomplete, or when the deterministic output cannot be written. +pub fn write_api_baseline(root: &Path) -> Result<()> { + let baseline = inspect(root)?; + fs::write(root.join(BASELINE_PATH), baseline_json(&baseline)?)?; + fs::write(root.join(REPORT_PATH), render_report(&baseline))?; + Ok(()) +} + +/// Audits the current API against the checked first-release `SemVer` baseline. +/// +/// # Errors +/// +/// Returns an error for missing/unexplained mappings, a changed public surface, +/// weak async/thread/error contracts, feature drift, leaked dependency types, +/// stale reports, or evidence I/O failures. +pub fn audit_api_surface(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 current = inspect(root)?; + let baseline_bytes = fs::read(root.join(BASELINE_PATH))?; + let baseline: ApiBaseline = serde_json::from_slice(&baseline_bytes)?; + if baseline != current { + return Err(MatrixError::new( + "public API differs from api/SEMVER-BASELINE.json; review the SemVer impact before recording a new baseline", + )); + } + if fs::read_to_string(root.join(REPORT_PATH))? != render_report(¤t) { + return Err(MatrixError::new( + "API/SemVer audit report is stale; run api-baseline-write after review", + )); + } + + let record = ApiEvidence { + schema: 1, + release: ¤t.release, + upstream_commit: ¤t.upstream_commit, + baseline_sha256: sha256(&baseline_bytes), + mapped_public_types: current.mapped_public_types, + mapped_members: current.mapped_members, + intentional_differences: current.intentional_differences, + unexplained_mappings: current.unexplained_mappings, + accidental_public_dependency_types: current.accidental_public_dependency_types, + thread_safe_dyn_traits: current.thread_safe_dyn_traits, + non_exhaustive_error_enums: current.non_exhaustive_error_enums, + downstream_compile_fixture: "tests/api-compile", + representative_port: "tests/semver-port", + 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 { + let (type_header, type_rows) = read_tsv(&root.join(TYPE_PATH))?; + let (member_header, member_rows) = read_tsv(&root.join(MEMBER_PATH))?; + validate_mapping_headers(&type_header, &member_header)?; + validate_unique(&type_rows, "csharp_type_id", "type mapping")?; + validate_unique(&member_rows, "csharp_id", "member mapping")?; + validate_unique(&member_rows, "rust_item_path", "Rust member destination")?; + validate_members(&member_rows)?; + validate_types(&type_rows)?; + + let (public_type_ids, member_ids) = mapped_ids(&type_rows, &member_rows); + let (exported_types, exported_members, exported_bytes) = exported_mappings(root)?; + if public_type_ids != exported_types || member_ids != exported_members { + return Err(MatrixError::new(format!( + "mapped/exported coverage mismatch: types {}/{}, members {}/{}", + exported_types.len(), + public_type_ids.len(), + exported_members.len(), + member_ids.len() + ))); + } + + validate_non_exhaustive_errors(root)?; + validate_thread_safe_traits(root)?; + validate_dependency_roots(&member_rows)?; + + let workspace = fs::read_to_string(root.join("Cargo.toml"))?; + let release = package_value(&workspace, "version") + .ok_or_else(|| MatrixError::new("workspace package version is missing"))?; + let (crates, declarations) = inspect_crates(root, &release, &type_rows, &member_rows)?; + let counts = quality_counts(&type_rows, &member_rows); + + let baseline = ApiBaseline { + schema: 1, + release, + upstream_commit: UPSTREAM_COMMIT.to_owned(), + mapped_public_types: public_type_ids.len(), + mapped_members: member_ids.len(), + external_signature_types: type_rows + .iter() + .filter(|row| { + !field(row, "csharp_type_id").starts_with("T:LibreMetaverse") + && field(row, "source_kind") != "referenced_support_trait" + }) + .count(), + support_traits: type_rows + .iter() + .filter(|row| field(row, "source_kind") == "referenced_support_trait") + .count(), + intentional_differences: counts.intentional_differences, + descriptive_overloads: counts.descriptive_overloads, + async_functions: counts.async_functions, + send_boxed_futures: counts.send_boxed_futures, + thread_safe_callbacks: counts.thread_safe_callbacks, + thread_safe_dyn_traits: REQUIRED_THREAD_SAFE_TRAITS.len(), + non_exhaustive_error_enums: REQUIRED_NON_EXHAUSTIVE_ERRORS.len(), + enum_types: counts.enum_types, + bitflag_types: counts.bitflag_types, + unexplained_mappings: 0, + accidental_public_dependency_types: 0, + type_mapping_sha256: sha256(&fs::read(root.join(TYPE_PATH))?), + member_mapping_sha256: sha256(&fs::read(root.join(MEMBER_PATH))?), + exported_mapping_sha256: sha256(&exported_bytes), + rust_declaration_sha256: sha256(declarations.join("\n").as_bytes()), + crates, + }; + validate_totals(&baseline)?; + Ok(baseline) +} + +fn validate_mapping_headers(type_header: &[String], member_header: &[String]) -> Result<()> { + require_columns( + type_header, + &[ + "csharp_type_id", + "csharp_signature", + "source_kind", + "rust_crate", + "rust_path", + "mapping_decision", + "inheritance_mapping", + "enum_mapping", + "status", + ], + TYPE_PATH, + )?; + require_columns( + member_header, + &[ + "csharp_id", + "csharp_kind", + "csharp_signature", + "rust_crate", + "rust_item_path", + "rust_signature", + "ownership", + "asyncness", + "error_model", + "overload_decision", + "mapping_kind", + "status", + ], + MEMBER_PATH, + ) +} + +fn mapped_ids(type_rows: &[Row], member_rows: &[Row]) -> (BTreeSet, BTreeSet) { + let public_type_ids = type_rows + .iter() + .filter(|row| { + field(row, "csharp_type_id").starts_with("T:LibreMetaverse") + && field(row, "source_kind") != "referenced_support_trait" + }) + .map(|row| field(row, "csharp_type_id").to_owned()) + .collect::>(); + let member_ids = member_rows + .iter() + .map(|row| field(row, "csharp_id").to_owned()) + .collect::>(); + (public_type_ids, member_ids) +} + +fn validate_dependency_roots(member_rows: &[Row]) -> Result<()> { + let accidental_roots = accidental_dependency_roots(member_rows); + if !accidental_roots.is_empty() { + return Err(MatrixError::new(format!( + "mapped public signatures expose dependency roots: {}", + accidental_roots.into_iter().collect::>().join(", ") + ))); + } + Ok(()) +} + +fn inspect_crates( + root: &Path, + release: &str, + type_rows: &[Row], + member_rows: &[Row], +) -> Result<(Vec, Vec)> { + let mut declarations = Vec::new(); + let mut crates = Vec::new(); + for (name, relative) in PUBLIC_CRATES { + let directory = root.join(relative); + let manifest = fs::read_to_string(directory.join("Cargo.toml"))?; + if package_value(&manifest, "name").as_deref() != Some(name) { + return Err(MatrixError::new(format!( + "public crate manifest mismatch for {name}" + ))); + } + let crate_version = if manifest + .lines() + .any(|line| line.trim() == "version.workspace = true") + { + release.to_owned() + } else { + package_value(&manifest, "version") + .ok_or_else(|| MatrixError::new(format!("public crate {name} lacks a version")))? + }; + if crate_version != release { + return Err(MatrixError::new(format!( + "public crate {name} has version {crate_version}, expected release {release}" + ))); + } + let features = parse_features(&manifest)?; + validate_features(name, &features)?; + let crate_declarations = crate_declarations(&directory)?; + validate_declaration_dependencies(name, &manifest, &crate_declarations)?; + let declaration_sha256 = sha256(crate_declarations.join("\n").as_bytes()); + declarations.extend( + crate_declarations + .iter() + .map(|declaration| format!("{name}\t{declaration}")), + ); + crates.push(CrateSurface { + name: name.to_owned(), + version: release.to_owned(), + features, + mapped_types: type_rows + .iter() + .filter(|row| { + field(row, "rust_crate") == name + && field(row, "csharp_type_id").starts_with("T:LibreMetaverse") + && field(row, "source_kind") != "referenced_support_trait" + }) + .count(), + mapped_members: member_rows + .iter() + .filter(|row| field(row, "rust_crate") == name) + .count(), + public_declarations: crate_declarations.len(), + declaration_sha256, + }); + } + declarations.sort(); + Ok((crates, declarations)) +} + +fn quality_counts(type_rows: &[Row], member_rows: &[Row]) -> QualityCounts { + let intentional_differences = member_rows + .iter() + .filter(|row| field(row, "status") == "intentional_difference") + .count(); + let descriptive_overloads = member_rows + .iter() + .filter(|row| field(row, "overload_decision") != "direct_snake_case") + .count(); + let async_functions = member_rows + .iter() + .filter(|row| field(row, "asyncness") == "async") + .count(); + let send_boxed_futures = member_rows + .iter() + .filter(|row| field(row, "asyncness") == "boxed_future") + .count(); + let thread_safe_callbacks = member_rows + .iter() + .filter(|row| field(row, "rust_signature").contains("dyn Fn")) + .count(); + let enum_types = type_rows + .iter() + .filter(|row| { + field(row, "csharp_type_id").starts_with("T:LibreMetaverse") + && field(row, "source_kind") == "enum" + }) + .count(); + let bitflag_types = type_rows + .iter() + .filter(|row| { + field(row, "csharp_type_id").starts_with("T:LibreMetaverse") + && field(row, "enum_mapping").starts_with("bitflags<") + }) + .count(); + QualityCounts { + intentional_differences, + descriptive_overloads, + async_functions, + send_boxed_futures, + thread_safe_callbacks, + enum_types, + bitflag_types, + } +} + +fn validate_members(rows: &[Row]) -> Result<()> { + let ownership_terms = OWNERSHIP_TERMS.into_iter().collect::>(); + for row in rows { + let id = field(row, "csharp_id"); + if [ + "csharp_id", + "csharp_kind", + "csharp_signature", + "rust_crate", + "rust_item_path", + "rust_signature", + "ownership", + "asyncness", + "error_model", + "overload_decision", + "mapping_kind", + "status", + ] + .into_iter() + .any(|name| field(row, name).is_empty()) + { + return Err(MatrixError::new(format!("mapping {id} has an empty field"))); + } + if field(row, "ownership") + .split(',') + .any(|term| !ownership_terms.contains(term)) + { + return Err(MatrixError::new(format!( + "mapping {id} has an unexplained ownership term" + ))); + } + let signature = field(row, "rust_signature"); + validate_rust_naming(id, signature)?; + match field(row, "asyncness") { + "sync" if signature.contains("pub async fn") => { + return Err(MatrixError::new(format!( + "mapping {id} marks an async function as synchronous" + ))); + } + "async" if !signature.contains("pub async fn") => { + return Err(MatrixError::new(format!( + "mapping {id} lacks an async Rust signature" + ))); + } + "boxed_future" + if !signature.contains("dyn std::future::Future") + || !signature.contains("+ Send") => + { + return Err(MatrixError::new(format!( + "mapping {id} lacks a Send boxed future" + ))); + } + "sync" | "async" | "boxed_future" => {} + value => { + return Err(MatrixError::new(format!( + "mapping {id} has unknown asyncness {value}" + ))); + } + } + if signature.contains("dyn Fn") && !signature.contains("+ Send + Sync") { + return Err(MatrixError::new(format!( + "mapping {id} exposes a callback without Send + Sync" + ))); + } + match field(row, "error_model") { + "Result<_, crate::Error>" if !signature.contains("Result<") => { + return Err(MatrixError::new(format!( + "mapping {id} declares Result errors without a Result signature" + ))); + } + "none" | "infallible_placeholder" if signature.contains("Result<") => { + return Err(MatrixError::new(format!( + "mapping {id} returns Result without a matching error model" + ))); + } + "Result<_, crate::Error>" | "none" | "infallible_placeholder" => {} + value => { + return Err(MatrixError::new(format!( + "mapping {id} has unknown error model {value}" + ))); + } + } + let intentional = field(row, "status") == "intentional_difference"; + if intentional != (field(row, "mapping_kind") == "native_callback_replaces_delegate_apm") { + return Err(MatrixError::new(format!( + "mapping {id} has an unexplained intentional difference" + ))); + } + if !matches!(field(row, "status"), "mapped" | "intentional_difference") { + return Err(MatrixError::new(format!( + "mapping {id} has an unresolved status" + ))); + } + } + Ok(()) +} + +fn validate_rust_naming(id: &str, signature: &str) -> Result<()> { + let function = ["pub async fn ", "pub fn ", "async fn ", "fn "] + .into_iter() + .find_map(|prefix| signature.strip_prefix(prefix)); + if let Some(function) = function { + let name = function + .split(|character: char| !(character.is_ascii_alphanumeric() || character == '_')) + .next() + .unwrap_or_default(); + if name.is_empty() + || name.starts_with(|character: char| character.is_ascii_digit()) + || !name.chars().all(|character| { + character.is_ascii_lowercase() || character.is_ascii_digit() || character == '_' + }) + { + return Err(MatrixError::new(format!( + "mapping {id} has a non-idiomatic Rust function name: {name}" + ))); + } + } + for prefix in ["pub const ", "pub static "] { + if let Some(item) = signature.strip_prefix(prefix) { + let name = item + .split(|character: char| !(character.is_ascii_alphanumeric() || character == '_')) + .next() + .unwrap_or_default(); + if name.is_empty() + || !name.chars().all(|character| { + character.is_ascii_uppercase() || character.is_ascii_digit() || character == '_' + }) + { + return Err(MatrixError::new(format!( + "mapping {id} has a non-idiomatic Rust constant name: {name}" + ))); + } + } + } + Ok(()) +} + +fn validate_types(rows: &[Row]) -> Result<()> { + for row in rows { + let id = field(row, "csharp_type_id"); + if [ + "csharp_type_id", + "csharp_signature", + "source_kind", + "rust_crate", + "rust_path", + "mapping_decision", + "inheritance_mapping", + "enum_mapping", + "status", + ] + .into_iter() + .any(|name| field(row, name).is_empty()) + || field(row, "status") != "mapped" + { + return Err(MatrixError::new(format!( + "type mapping {id} is incomplete or unresolved" + ))); + } + if !matches!( + field(row, "mapping_decision"), + "native_metacrate_type" + | "native_cross_platform_replacement" + | "stdlib_or_adopted_crate" + | "language" + | "native_metacrate_support_trait" + ) { + return Err(MatrixError::new(format!( + "type mapping {id} has an unexplained decision" + ))); + } + } + Ok(()) +} + +fn validate_totals(baseline: &ApiBaseline) -> Result<()> { + if baseline.mapped_public_types != 3_066 + || baseline.mapped_members != 30_789 + || baseline.external_signature_types != 142 + || baseline.support_traits != 2 + || baseline.intentional_differences != 80 + || baseline.descriptive_overloads != 7_151 + || baseline.async_functions != 334 + || baseline.send_boxed_futures != 26 + || baseline.thread_safe_callbacks != 137 + || baseline.enum_types != 243 + || baseline.bitflag_types != 46 + || baseline.unexplained_mappings != 0 + || baseline.accidental_public_dependency_types != 0 + { + return Err(MatrixError::new( + "API quality totals differ from the reviewed first-release surface", + )); + } + Ok(()) +} + +fn exported_mappings(root: &Path) -> Result<(BTreeSet, BTreeSet, Vec)> { + let mut types = BTreeSet::new(); + let mut members = BTreeSet::new(); + let mut canonical = Vec::new(); + for (_, relative) in PUBLIC_CRATES { + let path = root.join(relative).join("src/generated.rs"); + if !path.exists() { + continue; + } + for line in fs::read_to_string(&path)?.lines().map(str::trim) { + if let Some(id) = marker(line, "/// C# type: `") { + if !types.insert(id.to_owned()) { + return Err(MatrixError::new(format!( + "duplicate exported type marker {id}" + ))); + } + canonical.push(format!("type\t{id}")); + } + if let Some(id) = marker(line, "/// C# member: `") { + if !members.insert(id.to_owned()) { + return Err(MatrixError::new(format!( + "duplicate exported member marker {id}" + ))); + } + canonical.push(format!("member\t{id}")); + } + } + } + canonical.sort_unstable(); + Ok((types, members, canonical.join("\n").into_bytes())) +} + +fn marker<'a>(line: &'a str, prefix: &str) -> Option<&'a str> { + line.strip_prefix(prefix)?.strip_suffix("`.") +} + +fn accidental_dependency_roots(rows: &[Row]) -> BTreeSet { + let mut roots = BTreeSet::new(); + for row in rows { + for root in path_roots(field(row, "rust_signature")) { + if !matches!(root.as_str(), "crate" | "std" | "core" | "self" | "super") + && !root.starts_with("libremetaverse") + { + roots.insert(root); + } + } + } + roots +} + +fn validate_declaration_dependencies( + crate_name: &str, + manifest: &str, + declarations: &[String], +) -> Result<()> { + let dependencies = dependency_roots(manifest); + for declaration in declarations { + let surface = dependency_surface(declaration); + for root in path_roots(&surface).intersection(&dependencies) { + if root.starts_with("libremetaverse") { + continue; + } + return Err(MatrixError::new(format!( + "public Rust declaration exposes dependency root {root}: {crate_name}/{declaration}" + ))); + } + } + Ok(()) +} + +fn dependency_surface(declaration: &str) -> String { + let source = declaration + .split_once('\t') + .map_or(declaration, |(_, source)| source); + if !source.starts_with("pub struct ") && !source.starts_with("pub union ") { + return source.to_owned(); + } + + let (opening, closing) = if let Some(opening) = source.find('{') { + (opening, '}') + } else if let Some(opening) = source.find('(') { + (opening, ')') + } else { + return source.to_owned(); + }; + let Some(closing) = source.rfind(closing).filter(|closing| *closing > opening) else { + return source.to_owned(); + }; + let mut public = source[..opening].to_owned(); + for field in split_top_level(&source[opening + 1..closing], ',') { + let field = field.trim(); + if field.starts_with("pub ") { + public.push(' '); + public.push_str(field); + } + } + public +} + +fn split_top_level(value: &str, separator: char) -> Vec<&str> { + let mut fields = Vec::new(); + let mut start = 0; + let mut depth = 0usize; + for (index, character) in value.char_indices() { + match character { + '<' | '(' | '[' | '{' => depth += 1, + '>' | ')' | ']' | '}' => depth = depth.saturating_sub(1), + candidate if candidate == separator && depth == 0 => { + fields.push(&value[start..index]); + start = index + character.len_utf8(); + } + _ => {} + } + } + fields.push(&value[start..]); + fields +} + +fn dependency_roots(manifest: &str) -> BTreeSet { + let mut roots = BTreeSet::new(); + let mut normal_dependencies = false; + for line in manifest.lines().map(str::trim) { + if line.starts_with('[') { + normal_dependencies = line == "[dependencies]" + || (line.starts_with("[target.") && line.ends_with(".dependencies]")); + continue; + } + if !normal_dependencies || line.is_empty() || line.starts_with('#') { + continue; + } + if let Some((name, _)) = line.split_once('=') { + roots.insert(name.trim().replace('-', "_")); + } + } + roots +} + +fn path_roots(value: &str) -> BTreeSet { + let bytes = value.as_bytes(); + let mut roots = BTreeSet::new(); + let mut index = 0; + while index < bytes.len() { + if bytes[index].is_ascii_lowercase() + && (index == 0 + || (!bytes[index - 1].is_ascii_alphanumeric() + && bytes[index - 1] != b'_' + && bytes[index - 1] != b':')) + { + let start = index; + index += 1; + while index < bytes.len() + && (bytes[index].is_ascii_alphanumeric() || bytes[index] == b'_') + { + index += 1; + } + let mut separator = index; + while separator < bytes.len() && bytes[separator].is_ascii_whitespace() { + separator += 1; + } + if bytes.get(separator..separator + 2) == Some(b"::") { + roots.insert(value[start..index].to_owned()); + } + } else { + index += 1; + } + } + roots +} + +fn validate_non_exhaustive_errors(root: &Path) -> Result<()> { + for (relative, name) in REQUIRED_NON_EXHAUSTIVE_ERRORS { + let text = fs::read_to_string(root.join(relative))?; + let declaration = format!("pub enum {name}"); + let Some(position) = text.find(&declaration) else { + return Err(MatrixError::new(format!( + "missing public error enum {name}" + ))); + }; + let prefix = &text[..position]; + let attribute = prefix + .rsplit_once("#[non_exhaustive]") + .map(|(_, tail)| tail); + if attribute.is_none_or(|tail| tail.contains("pub enum")) { + return Err(MatrixError::new(format!( + "public error enum {name} must be non_exhaustive before release" + ))); + } + } + Ok(()) +} + +fn validate_thread_safe_traits(root: &Path) -> Result<()> { + for (relative, declaration) in REQUIRED_THREAD_SAFE_TRAITS { + if !fs::read_to_string(root.join(relative))?.contains(declaration) { + return Err(MatrixError::new(format!( + "public callback trait lacks its Send + Sync contract: {declaration}" + ))); + } + } + Ok(()) +} + +fn validate_features(name: &str, actual: &BTreeMap>) -> Result<()> { + let expected = match name { + "libremetaverse" => BTreeMap::from([ + ("dds-bc67".to_owned(), vec!["dep:bcdec_rs".to_owned()]), + ("default".to_owned(), vec!["dds-bc67".to_owned()]), + ( + "jpeg2000".to_owned(), + vec!["libremetaverse-imaging/jpeg2000".to_owned()], + ), + ("vorbis".to_owned(), vec!["dep:vorbis_rs".to_owned()]), + ]), + "libremetaverse-imaging" => BTreeMap::from([ + ("default".to_owned(), Vec::new()), + ( + "jpeg2000".to_owned(), + vec!["dep:libremetaverse-openjpeg".to_owned()], + ), + ]), + "libremetaverse-imaging-skia" => BTreeMap::from([ + ("default".to_owned(), Vec::new()), + ("skia".to_owned(), vec!["dep:skia-safe".to_owned()]), + ]), + "libremetaverse-voice-webrtc" => BTreeMap::from([ + ("default".to_owned(), Vec::new()), + ("real-audio".to_owned(), vec!["dep:cpal".to_owned()]), + ]), + _ => BTreeMap::new(), + }; + if actual != &expected { + return Err(MatrixError::new(format!( + "public feature surface changed for {name}: expected {expected:?}, found {actual:?}" + ))); + } + Ok(()) +} + +fn parse_features(manifest: &str) -> Result>> { + let mut features = BTreeMap::new(); + let mut in_features = false; + for line in manifest.lines() { + let line = line.trim(); + if line.starts_with('[') { + in_features = line == "[features]"; + continue; + } + if !in_features || line.is_empty() || line.starts_with('#') { + continue; + } + let (name, values) = line + .split_once('=') + .ok_or_else(|| MatrixError::new("feature declaration lacks ="))?; + let values = serde_json::from_str::>(values.trim())?; + if features.insert(name.trim().to_owned(), values).is_some() { + return Err(MatrixError::new("duplicate feature declaration")); + } + } + Ok(features) +} + +fn crate_declarations(directory: &Path) -> Result> { + let mut files = Vec::new(); + collect_rs(&directory.join("src"), &mut files)?; + files.sort(); + let mut declarations = Vec::new(); + for file in files { + let relative = file.strip_prefix(directory).unwrap_or(&file); + for declaration in extract_public_declarations(&fs::read_to_string(&file)?) { + declarations.push(format!("{}\t{declaration}", relative.display())); + } + } + declarations.sort(); + Ok(declarations) +} + +fn collect_rs(directory: &Path, files: &mut Vec) -> Result<()> { + for entry in fs::read_dir(directory)? { + let path = entry?.path(); + if path.is_dir() { + collect_rs(&path, files)?; + } else if path.extension().and_then(|value| value.to_str()) == Some("rs") { + files.push(path); + } + } + Ok(()) +} + +fn extract_public_declarations(source: &str) -> Vec { + let lines = source.lines().collect::>(); + let mut declarations = Vec::new(); + let mut index = 0; + while index < lines.len() { + let trimmed = lines[index].trim_start(); + if !trimmed.starts_with("pub ") { + index += 1; + continue; + } + let whole_block = ["pub struct ", "pub enum ", "pub union ", "pub trait "] + .iter() + .any(|prefix| trimmed.starts_with(prefix)); + let inline_module = trimmed.starts_with("pub mod ") && trimmed.contains('{'); + let mut chunk = Vec::new(); + let mut braces = 0isize; + let mut opened = false; + loop { + let line = lines[index].trim(); + if !line.starts_with("///") && !line.starts_with("//") { + let selected = if !whole_block && !inline_module { + line.split_once('{') + .map_or(line, |(header, _)| header.trim()) + } else if inline_module { + line.split_once('{') + .map_or(line, |(header, _)| header.trim()) + } else { + line + }; + if !selected.is_empty() { + chunk.push(selected); + } + } + if whole_block { + braces += brace_delta(line); + opened |= line.contains('{'); + } + let finished = if whole_block { + (opened && braces == 0) || (!opened && line.ends_with(';')) + } else { + line.contains('{') || line.ends_with(';') + }; + index += 1; + if finished || index == lines.len() { + break; + } + } + let normalized = chunk + .join(" ") + .split_whitespace() + .collect::>() + .join(" "); + if !normalized.is_empty() { + declarations.push(normalized); + } + } + declarations +} + +fn brace_delta(line: &str) -> isize { + let mut delta = 0; + let mut quoted = false; + let mut escaped = false; + let mut characters = line.chars().peekable(); + while let Some(character) = characters.next() { + if !quoted && character == '/' && characters.peek() == Some(&'/') { + break; + } + if quoted { + if escaped { + escaped = false; + } else if character == '\\' { + escaped = true; + } else if character == '"' { + quoted = false; + } + continue; + } + match character { + '"' => quoted = true, + '{' => delta += 1, + '}' => delta -= 1, + _ => {} + } + } + delta +} + +fn read_tsv(path: &Path) -> Result<(Vec, Vec)> { + 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::>(); + let mut rows = Vec::new(); + for line in lines.filter(|line| !line.is_empty()) { + let values = line.split('\t').collect::>(); + if values.len() != header.len() { + return Err(MatrixError::new(format!( + "{} contains a malformed row", + path.display() + ))); + } + rows.push( + header + .iter() + .cloned() + .zip(values.into_iter().map(str::to_owned)) + .collect(), + ); + } + Ok((header, rows)) +} + +fn require_columns(header: &[String], required: &[&str], path: &str) -> Result<()> { + for field in required { + if !header.iter().any(|candidate| candidate == field) { + return Err(MatrixError::new(format!( + "{path} lacks required column {field}" + ))); + } + } + Ok(()) +} + +fn validate_unique(rows: &[Row], key: &str, label: &str) -> Result<()> { + let mut values = BTreeSet::new(); + for row in rows { + let value = field(row, key); + if value.is_empty() || !values.insert(value) { + return Err(MatrixError::new(format!( + "{label} values must be nonempty and unique: {value}" + ))); + } + } + Ok(()) +} + +fn field<'a>(row: &'a Row, name: &str) -> &'a str { + row.get(name).map_or("", String::as_str) +} + +fn package_value(manifest: &str, key: &str) -> Option { + let mut in_package = false; + for line in manifest.lines() { + let line = line.trim(); + if line.starts_with('[') { + in_package = matches!(line, "[package]" | "[workspace.package]"); + continue; + } + if in_package + && let Some(value) = line.strip_prefix(&format!("{key} = \"")) + && let Some(value) = value.strip_suffix('"') + { + return Some(value.to_owned()); + } + } + None +} + +fn baseline_json(baseline: &ApiBaseline) -> Result> { + let mut bytes = serde_json::to_vec_pretty(baseline)?; + bytes.push(b'\n'); + Ok(bytes) +} + +fn sha256(bytes: &[u8]) -> String { + let digest = Sha256::digest(bytes); + let mut output = String::with_capacity(digest.len() * 2); + for byte in digest { + use std::fmt::Write as _; + write!(output, "{byte:02x}").expect("writing to a String cannot fail"); + } + output +} + +fn render_report(baseline: &ApiBaseline) -> String { + let crates = baseline + .crates + .iter() + .map(|surface| { + let features = if surface.features.is_empty() { + "none".to_owned() + } else { + surface + .features + .keys() + .cloned() + .collect::>() + .join(", ") + }; + format!( + "| `{}` | {} | {} | {} | {} | `{}` |", + surface.name, + surface.mapped_types, + surface.mapped_members, + surface.public_declarations, + features, + surface.declaration_sha256 + ) + }) + .collect::>() + .join("\n"); + format!( + "# First-release API and SemVer audit\n\n\ +Generated by `metacrate-ci-matrix api-baseline-write`; do not edit by hand.\n\n\ +The compatibility source is pinned at [`{commit}`](https://github.com/cinderblocks/libremetaverse/tree/{commit}). \ +The baseline version is `{release}`.\n\n\ +## Coverage and quality decisions\n\n\ +- Mapped/exported public types: **{types} / {types}**; unexplained mappings: **0**.\n\ +- Mapped/exported public members: **{members} / {members}**; duplicate destinations: **0**.\n\ +- Rust naming and ownership: mapped function/constant names follow Rust conventions and every member uses a reviewed ownership term.\n\ +- External signature types: **{external}**; referenced support traits: **{support}**; accidental dependency types exposed by mapped signatures or public Rust declarations: **0**.\n\ +- Intentional delegate APM replacements: **{differences}**; each uses a native callback and is compiled by the downstream fixture.\n\ +- Descriptively named overloads: **{overloads}**; names remain fixed by the mapping ledger.\n\ +- Native `async fn` mappings: **{async_functions}**; boxed object-safe trait futures with explicit `Send`: **{boxed}**.\n\ +- Callback signatures requiring `Send + Sync`: **{callbacks}**; reviewed dyn-compatible thread-safe boundary traits: **{traits}** (`IGridClient`, `IBakingTextureProvider`, `ICurrentOutfitPolicy`, `IRlvActionCallbacks`, `IRlvQueryCallbacks`, and `IVoiceLogger`).\n\ +- C# enum mappings: **{enums}** ({bitflags} unknown-bit-retaining flag types); stable public error enums marked `non_exhaustive`: **{errors}**.\n\n\ +## Crate baselines\n\n\ +| Crate | Mapped types | Mapped members | Rust declarations | Features | Declaration SHA-256 |\n\ +| --- | ---: | ---: | ---: | --- | --- |\n\ +{crates}\n\n\ +The exact mapping, exported-marker, feature, and normalized Rust declaration hashes are stored in \ +[`SEMVER-BASELINE.json`](SEMVER-BASELINE.json). A change must be reviewed and classified before the baseline is re-recorded.\n\n\ +## Intentional Rust migration recipes\n\n\ +- C# `PascalCase` methods/properties become Rust `snake_case`; overloads use the ledger's stable descriptive suffix, never argument-count dispatch.\n\ +- C# reference ownership becomes owned values, shared or mutable borrows, `Arc`-backed managers, and explicit optional values as recorded per member.\n\ +- `Task` becomes `async fn -> Result`; object-safe async traits return `Pin> + Send + '_>>`.\n\ +- .NET events become `subscribe_*` calls returning an owned `Subscription`; callbacks are `Send + Sync` and dispatch outside internal locks.\n\ +- `IDisposable` becomes explicit idempotent shutdown plus `Drop`; cancellation remains a typed `Error::Cancelled`.\n\ +- Closed C# enums retain fixed discriminants and are SemVer-breaking to extend; flags retain unknown bits, while public Rust error enums are `non_exhaustive`.\n\ +- Optional native backends remain feature-gated (`jpeg2000`, `skia`, `vorbis`, `real-audio`); default and empty-default sets are part of the baseline.\n\ +- Capability HTTP injection uses project-owned `HttpMessageHandler`, `CapsHttpLimits`, and `with_native_transport`; the internal `reqwest` client is not part of the public surface.\n\ +- External C# signature types map to core/std, adopted cross-platform crates, or project-owned boundary types; no third-party crate path leaks through mapped signatures or public Rust declarations.\n\n\ +## Compile consumers\n\n\ +- `tests/api-compile` compiles every mapped type/member from a standalone downstream workspace.\n\ +- `tests/semver-port` is a runnable representative port covering lifecycle, value types, structured data, imaging, RLV, cancellation, dyn trait, and `Send`/`Sync` contracts without network access.\n", + commit = baseline.upstream_commit, + release = baseline.release, + types = baseline.mapped_public_types, + members = baseline.mapped_members, + external = baseline.external_signature_types, + support = baseline.support_traits, + differences = baseline.intentional_differences, + overloads = baseline.descriptive_overloads, + async_functions = baseline.async_functions, + boxed = baseline.send_boxed_futures, + callbacks = baseline.thread_safe_callbacks, + traits = baseline.thread_safe_dyn_traits, + enums = baseline.enum_types, + bitflags = baseline.bitflag_types, + errors = baseline.non_exhaustive_error_enums, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn declaration_extractor_ignores_function_bodies_and_private_items() { + let source = r" + struct Private; + pub struct Public { pub value: i32, hidden: bool } + impl Public { + pub fn value( + &self, + ) -> i32 { self.value } + fn hidden(&self) -> bool { self.hidden } + } + "; + let declarations = extract_public_declarations(source); + assert_eq!(declarations.len(), 2); + assert!(declarations[0].starts_with("pub struct Public")); + assert_eq!(declarations[1], "pub fn value( &self, ) -> i32"); + assert!(!declarations.join(" ").contains("self.value")); + } + + #[test] + fn path_root_parser_ignores_nested_modules() { + let roots = + path_roots("pub fn f(value: libremetaverse::packets::Packet) -> std::io::Result<()>;"); + assert_eq!( + roots, + BTreeSet::from(["libremetaverse".to_owned(), "std".to_owned()]) + ); + } + + #[test] + fn dependency_root_parser_covers_normal_target_and_renamed_crates() { + let manifest = r#" + [dependencies] + normal-crate = "1" + [dev-dependencies] + ignored_dev = "1" + [target.'cfg(unix)'.dependencies] + target-crate = "1" + [build-dependencies] + ignored_build = "1" + "#; + assert_eq!( + dependency_roots(manifest), + BTreeSet::from(["normal_crate".to_owned(), "target_crate".to_owned()]) + ); + } + + #[test] + fn dependency_surface_excludes_private_but_retains_public_struct_fields() { + let declaration = "src/example.rs\tpub struct Example { private: dep::Hidden, pub visible: dep::Visible<(T, T)>, pub(crate) scoped: dep::Scoped, }"; + assert_eq!( + dependency_surface(declaration), + "pub struct Example pub visible: dep::Visible<(T, T)>" + ); + assert_eq!( + dependency_surface("src/example.rs\tpub struct Tuple(pub dep::Visible, dep::Hidden);"), + "pub struct Tuple pub dep::Visible" + ); + } + + #[test] + fn naming_audit_accepts_rust_conventions_and_rejects_pascal_case_functions() { + validate_rust_naming( + "good", + "pub async fn request_asset_2() -> Result<(), Error>", + ) + .unwrap(); + validate_rust_naming("constant", "pub const MAX_BYTES: usize").unwrap(); + assert!(validate_rust_naming("bad", "pub fn RequestAsset() -> bool").is_err()); + } + + #[test] + fn repository_surface_has_reviewed_totals() { + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."); + let baseline = inspect(&root).unwrap(); + assert_eq!(baseline.mapped_public_types, 3_066); + assert_eq!(baseline.mapped_members, 30_789); + assert_eq!(baseline.unexplained_mappings, 0); + assert_eq!(baseline.accidental_public_dependency_types, 0); + } +} diff --git a/tools/ci-matrix/src/lib.rs b/tools/ci-matrix/src/lib.rs index 421cfab..ab4bfe1 100644 --- a/tools/ci-matrix/src/lib.rs +++ b/tools/ci-matrix/src/lib.rs @@ -11,9 +11,11 @@ use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus, Stdio}; use std::time::{SystemTime, UNIX_EPOCH}; +mod api_surface; mod dependency; mod documentation; +pub use api_surface::{audit_api_surface, write_api_baseline}; pub use dependency::audit_dependencies; pub use documentation::{audit_documentation, write_documentation_report}; diff --git a/tools/ci-matrix/src/main.rs b/tools/ci-matrix/src/main.rs index 92aceb7..ec84766 100644 --- a/tools/ci-matrix/src/main.rs +++ b/tools/ci-matrix/src/main.rs @@ -1,6 +1,6 @@ use metacrate_ci_matrix::{ - audit, audit_dependencies, audit_documentation, load, run, workspace_root, - write_documentation_report, + audit, audit_api_surface, audit_dependencies, audit_documentation, load, run, workspace_root, + write_api_baseline, write_documentation_report, }; use std::path::{Path, PathBuf}; @@ -67,9 +67,27 @@ fn execute() -> Result<(), Box> { audit_documentation(&root, &evidence)?; println!("documentation coverage: ok ({})", evidence.display()); } + Some("api-baseline-write") if arguments.next().is_none() => { + write_api_baseline(&root)?; + println!("API/SemVer baseline and report: updated"); + } + Some("api-audit") => { + let flag = arguments + .next() + .ok_or("api-audit requires --evidence FILE")?; + let evidence = arguments + .next() + .ok_or("api-audit requires --evidence FILE")?; + if flag != "--evidence" || arguments.next().is_some() { + return Err("usage: ci-matrix api-audit --evidence FILE".into()); + } + let evidence = absolute_or_rooted(&root, &evidence); + audit_api_surface(&root, &evidence)?; + println!("API/SemVer surface: ok ({})", evidence.display()); + } _ => { return Err( - "usage: ci-matrix audit | run PROFILE --evidence FILE | dependency-audit --evidence FILE | documentation-report | documentation-audit --evidence FILE" + "usage: ci-matrix audit | run PROFILE --evidence FILE | dependency-audit --evidence FILE | documentation-report | documentation-audit --evidence FILE | api-baseline-write | api-audit --evidence FILE" .into(), ); } diff --git a/tools/generate_api_shims.py b/tools/generate_api_shims.py index 0e1f77b..947837d 100644 --- a/tools/generate_api_shims.py +++ b/tools/generate_api_shims.py @@ -2921,10 +2921,12 @@ NATIVE_MESSAGE_CODEC_TYPES = { OPEN_ENUM_IDS = {"T:LibreMetaverse.BakeType", "T:LibreMetaverse.HoleType"} TRAIT_SUPERTRAITS = { "T:LibreMetaverse.IBakingTextureProvider": "std::any::Any + Send + Sync", + "T:LibreMetaverse.IGridClient": "Send + Sync", "T:LibreMetaverse.Interfaces.IMessage": "std::any::Any", "T:LibreMetaverse.Appearance.ICurrentOutfitPolicy": "Send + Sync", "T:LibreMetaverse.RLV.IRlvActionCallbacks": "Send + Sync", "T:LibreMetaverse.RLV.IRlvQueryCallbacks": "Send + Sync", + "T:LibreMetaverse.Voice.WebRTC.IVoiceLogger": "Send + Sync", } CORE_NAMESPACES = ( "LibreMetaverse.Http", diff --git a/tools/generate_rust_mapping.py b/tools/generate_rust_mapping.py index 9702e98..34c202b 100644 --- a/tools/generate_rust_mapping.py +++ b/tools/generate_rust_mapping.py @@ -1271,7 +1271,19 @@ def parameter_slug(value: str) -> str: def member_signature(mapper: Mapper, owner: dict, item: dict, rust_name: str) -> tuple[str, str, str, str, str]: if item["doc_id"] in MEMBER_SIGNATURE_OVERRIDES: signature, ownership = MEMBER_SIGNATURE_OVERRIDES[item["doc_id"]] - return signature, ownership, "sync", "Result<_, crate::Error>", "method" + error_model = ( + "none" + if item["doc_id"] == "P:LibreMetaverse.ExpiringCache`2.Item(`0)" + else "Result<_, crate::Error>" + ) + asyncness = ( + "async" + if signature.startswith("pub async fn") + else "boxed_future" + if "dyn std::future::Future" in signature + else "sync" + ) + return signature, ownership, asyncness, error_model, "method" generics = generic_names(item, owner) mapped_parameters = [map_parameter(mapper, parameter, generics) for parameter in item.get("parameters", [])] packet_owner = owner["doc_id"].startswith("T:LibreMetaverse.Packets.")