From 374d0f375f0d19f4c83153aea44153d94c3cf05c Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 27 Apr 2026 01:50:27 -0400 Subject: [PATCH] fix clippy warnings, rustfmt, and switch CI to nix toolchain Resolve all clippy lints and apply rustfmt so CI checks pass cleanly. Switch CI from dtolnay/rust-toolchain to nix develop so the runner uses the exact same toolchain as the flake, avoiding version skew. --- .github/workflows/ci.yml | 31 +- crates/pack-abi/src/hash.rs | 143 ++++----- crates/pack-abi/src/lib.rs | 169 +++++++--- crates/pack-abi/src/value.rs | 152 ++++++--- crates/pack-abi/tests/derive_tests.rs | 5 +- crates/pack-derive/src/lib.rs | 184 ++++++----- examples/test_alloc.rs | 21 +- examples/transforms/main.rs | 44 +-- flake.nix | 4 +- src/abi/mod.rs | 125 +++++--- src/bin/pact.rs | 47 ++- src/codegen.rs | 58 ++-- src/compose/merger.rs | 66 ++-- src/compose/parser.rs | 273 ++++++++++++---- src/interface_impl.rs | 57 ++-- src/lib.rs | 32 +- src/main.rs | 27 +- src/metadata.rs | 430 +++++++++++++++----------- src/parser/mod.rs | 30 +- src/parser/pact.rs | 173 +++++++---- src/parser/validation.rs | 39 +-- src/parser/wit.rs | 28 +- src/runtime/composition.rs | 148 +++++---- src/runtime/host.rs | 106 +++++-- src/runtime/interface_check.rs | 7 +- src/runtime/mod.rs | 216 ++++++++----- src/transform.rs | 5 +- src/types.rs | 6 +- tests/abi_roundtrip.rs | 12 +- tests/composition.rs | 16 +- tests/host_functions.rs | 103 ++++-- tests/schema_validation.rs | 20 +- tests/type_metadata.rs | 18 +- tests/wasm_execution.rs | 191 ++++++++---- 34 files changed, 1883 insertions(+), 1103 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ebc924b..3589be6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,26 +17,18 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - toolchain: "1.85.0" - components: clippy, rustfmt + - uses: DeterminateSystems/nix-installer-action@main - - uses: Swatinem/rust-cache@v2 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y pkg-config libssl-dev cmake clang llvm + - uses: DeterminateSystems/magic-nix-cache-action@main - name: Check - run: cargo check --workspace + run: nix develop --command cargo check --workspace - name: Clippy - run: cargo clippy --workspace -- -D warnings + run: nix develop --command cargo clippy --workspace -- -D warnings - name: Format - run: cargo fmt --all -- --check + run: nix develop --command cargo fmt --all -- --check test: name: Test @@ -44,16 +36,9 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - toolchain: "1.85.0" - - - uses: Swatinem/rust-cache@v2 + - uses: DeterminateSystems/nix-installer-action@main - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y pkg-config libssl-dev cmake clang llvm + - uses: DeterminateSystems/magic-nix-cache-action@main - name: Test - run: cargo test --workspace + run: nix develop --command cargo test --workspace diff --git a/crates/pack-abi/src/hash.rs b/crates/pack-abi/src/hash.rs index a29778b..950bd5c 100644 --- a/crates/pack-abi/src/hash.rs +++ b/crates/pack-abi/src/hash.rs @@ -74,114 +74,86 @@ impl TypeHash { /// Hash for the `bool` type. pub const HASH_BOOL: TypeHash = TypeHash::from_bytes([ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u8` type. pub const HASH_U8: TypeHash = TypeHash::from_bytes([ - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u16` type. pub const HASH_U16: TypeHash = TypeHash::from_bytes([ - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u32` type. pub const HASH_U32: TypeHash = TypeHash::from_bytes([ - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u64` type. pub const HASH_U64: TypeHash = TypeHash::from_bytes([ - 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s8` type. pub const HASH_S8: TypeHash = TypeHash::from_bytes([ - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s16` type. pub const HASH_S16: TypeHash = TypeHash::from_bytes([ - 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s32` type. pub const HASH_S32: TypeHash = TypeHash::from_bytes([ - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s64` type. pub const HASH_S64: TypeHash = TypeHash::from_bytes([ - 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `f32` type. pub const HASH_F32: TypeHash = TypeHash::from_bytes([ - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `f64` type. pub const HASH_F64: TypeHash = TypeHash::from_bytes([ - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `char` type. pub const HASH_CHAR: TypeHash = TypeHash::from_bytes([ - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `string` type. pub const HASH_STRING: TypeHash = TypeHash::from_bytes([ - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `flags` type. pub const HASH_FLAGS: TypeHash = TypeHash::from_bytes([ - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); // ============================================================================ @@ -260,18 +232,12 @@ const TAG_INTERFACE: u8 = 0x17; /// Hash a list type: `list`. pub fn hash_list(element: &TypeHash) -> TypeHash { - TypeHasher::new() - .tag(TAG_LIST) - .child(element) - .finish() + TypeHasher::new().tag(TAG_LIST).child(element).finish() } /// Hash an option type: `option`. pub fn hash_option(inner: &TypeHash) -> TypeHash { - TypeHasher::new() - .tag(TAG_OPTION) - .child(inner) - .finish() + TypeHasher::new().tag(TAG_OPTION).child(inner).finish() } /// Hash a result type: `result`. @@ -285,9 +251,7 @@ pub fn hash_result(ok: &TypeHash, err: &TypeHash) -> TypeHash { /// Hash a tuple type: `tuple`. pub fn hash_tuple(elements: &[TypeHash]) -> TypeHash { - let mut hasher = TypeHasher::new() - .tag(TAG_TUPLE) - .count(elements.len()); + let mut hasher = TypeHasher::new().tag(TAG_TUPLE).count(elements.len()); for elem in elements { hasher = hasher.child(elem); @@ -299,9 +263,7 @@ pub fn hash_tuple(elements: &[TypeHash]) -> TypeHash { /// Hash a record type (structural - name NOT included). /// Fields should be in canonical order (sorted by name). pub fn hash_record(fields: &[(&str, TypeHash)]) -> TypeHash { - let mut hasher = TypeHasher::new() - .tag(TAG_RECORD) - .count(fields.len()); + let mut hasher = TypeHasher::new().tag(TAG_RECORD).count(fields.len()); for (name, type_hash) in fields { hasher = hasher.string(name).child(type_hash); @@ -313,9 +275,7 @@ pub fn hash_record(fields: &[(&str, TypeHash)]) -> TypeHash { /// Hash a variant type (structural - name NOT included). /// Cases should be in canonical order (sorted by name). pub fn hash_variant(cases: &[(&str, Option)]) -> TypeHash { - let mut hasher = TypeHasher::new() - .tag(TAG_VARIANT) - .count(cases.len()); + let mut hasher = TypeHasher::new().tag(TAG_VARIANT).count(cases.len()); for (name, payload) in cases { hasher = hasher.string(name); @@ -333,9 +293,7 @@ pub fn hash_variant(cases: &[(&str, Option)]) -> TypeHash { /// Param names are NOT included (just types in order). /// Result types are included in order. pub fn hash_function(params: &[TypeHash], results: &[TypeHash]) -> TypeHash { - let mut hasher = TypeHasher::new() - .tag(TAG_FUNCTION) - .count(params.len()); + let mut hasher = TypeHasher::new().tag(TAG_FUNCTION).count(params.len()); for param in params { hasher = hasher.child(param); @@ -390,10 +348,8 @@ pub fn hash_interface( /// 2. This produces a "template hash" /// 3. The template hash IS the final hash (self-reference is structural) pub const HASH_SELF_REF: TypeHash = TypeHash::from_bytes([ - 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); #[cfg(test)] @@ -403,9 +359,20 @@ mod tests { #[test] fn test_primitive_hashes_are_unique() { let primitives = [ - HASH_BOOL, HASH_U8, HASH_U16, HASH_U32, HASH_U64, - HASH_S8, HASH_S16, HASH_S32, HASH_S64, - HASH_F32, HASH_F64, HASH_CHAR, HASH_STRING, HASH_FLAGS, + HASH_BOOL, + HASH_U8, + HASH_U16, + HASH_U32, + HASH_U64, + HASH_S8, + HASH_S16, + HASH_S32, + HASH_S64, + HASH_F32, + HASH_F64, + HASH_CHAR, + HASH_STRING, + HASH_FLAGS, ]; for (i, a) in primitives.iter().enumerate() { @@ -465,13 +432,19 @@ mod tests { let iface_a = hash_interface( "math", &[], - &[Binding { name: "add", hash: hash_function(&[HASH_S32], &[HASH_S32]) }], + &[Binding { + name: "add", + hash: hash_function(&[HASH_S32], &[HASH_S32]), + }], ); let iface_b = hash_interface( "math", &[], - &[Binding { name: "inc", hash: hash_function(&[HASH_S32], &[HASH_S32]) }], + &[Binding { + name: "inc", + hash: hash_function(&[HASH_S32], &[HASH_S32]), + }], ); // Different binding names = different interface hash diff --git a/crates/pack-abi/src/lib.rs b/crates/pack-abi/src/lib.rs index b87ec16..3fec161 100644 --- a/crates/pack-abi/src/lib.rs +++ b/crates/pack-abi/src/lib.rs @@ -30,15 +30,34 @@ mod hash; mod value; pub use hash::{ - Binding, TypeHash, TypeHasher, - // Primitive hashes - HASH_BOOL, HASH_CHAR, HASH_F32, HASH_F64, HASH_FLAGS, - HASH_S8, HASH_S16, HASH_S32, HASH_S64, - HASH_U8, HASH_U16, HASH_U32, HASH_U64, - HASH_STRING, HASH_SELF_REF, + hash_function, + hash_interface, // Compound hash functions - hash_list, hash_option, hash_result, hash_tuple, - hash_record, hash_variant, hash_function, hash_interface, + hash_list, + hash_option, + hash_record, + hash_result, + hash_tuple, + hash_variant, + Binding, + TypeHash, + TypeHasher, + // Primitive hashes + HASH_BOOL, + HASH_CHAR, + HASH_F32, + HASH_F64, + HASH_FLAGS, + HASH_S16, + HASH_S32, + HASH_S64, + HASH_S8, + HASH_SELF_REF, + HASH_STRING, + HASH_U16, + HASH_U32, + HASH_U64, + HASH_U8, }; pub use value::{FromValue, Value, ValueType}; @@ -129,9 +148,9 @@ pub mod __private { pub use alloc::vec::Vec; pub use core::convert::From; pub use core::convert::TryFrom; + pub use core::option::Option::{self, None, Some}; pub use core::result::Result; pub use core::result::Result::{Err, Ok}; - pub use core::option::Option::{self, None, Some}; } use alloc::format; @@ -318,13 +337,17 @@ impl GraphBuffer { let version = cursor.read_u16()?; if version != VERSION { - return Err(AbiError::InvalidEncoding(String::from("Unsupported version"))); + return Err(AbiError::InvalidEncoding(String::from( + "Unsupported version", + ))); } let _flags = cursor.read_u16()?; let node_count = cursor.read_u32()? as usize; if node_count > limits.max_node_count { - return Err(AbiError::InvalidEncoding(String::from("Node count exceeds limit"))); + return Err(AbiError::InvalidEncoding(String::from( + "Node count exceeds limit", + ))); } let root = cursor.read_u32()?; @@ -342,7 +365,9 @@ impl GraphBuffer { } if (root as usize) >= nodes.len() { - return Err(AbiError::InvalidEncoding(String::from("Root index out of range"))); + return Err(AbiError::InvalidEncoding(String::from( + "Root index out of range", + ))); } if !cursor.is_eof() { @@ -360,10 +385,14 @@ impl GraphBuffer { pub fn validate_basic_with_limits(&self, limits: &Limits) -> Result<(), AbiError> { let node_count = self.nodes.len(); if (self.root as usize) >= node_count { - return Err(AbiError::InvalidEncoding(String::from("Root index out of range"))); + return Err(AbiError::InvalidEncoding(String::from( + "Root index out of range", + ))); } if node_count > limits.max_node_count { - return Err(AbiError::InvalidEncoding(String::from("Node count exceeds limit"))); + return Err(AbiError::InvalidEncoding(String::from( + "Node count exceeds limit", + ))); } for (index, node) in self.nodes.iter().enumerate() { @@ -449,15 +478,25 @@ impl GraphBuffer { } cursor.read_bytes(count * width)?; } - NodeKind::List | NodeKind::Option | NodeKind::Record | NodeKind::Variant | NodeKind::Result => { + NodeKind::List + | NodeKind::Option + | NodeKind::Record + | NodeKind::Variant + | NodeKind::Result => { // These have variable-length type tags or string headers // Skip detailed validation, just ensure payload is not too large (already checked) } } // Don't check for trailing bytes on v2 nodes with variable headers - if !matches!(node.kind, NodeKind::List | NodeKind::Option | NodeKind::Record | NodeKind::Variant | NodeKind::Result) - && !cursor.is_eof() + if !matches!( + node.kind, + NodeKind::List + | NodeKind::Option + | NodeKind::Record + | NodeKind::Variant + | NodeKind::Result + ) && !cursor.is_eof() { return Err(AbiError::InvalidEncoding(format!( "Trailing payload bytes at node {index}" @@ -579,7 +618,11 @@ fn fixed_width(ty: &ValueType) -> Option { } /// Encode a single primitive value into a contiguous byte buffer (for Array nodes). -fn encode_array_element(value: &Value, expected: &ValueType, out: &mut Vec) -> Result<(), AbiError> { +fn encode_array_element( + value: &Value, + expected: &ValueType, + out: &mut Vec, +) -> Result<(), AbiError> { match (value, expected) { (Value::Bool(v), ValueType::Bool) => out.push(if *v { 1 } else { 0 }), (Value::U8(v), ValueType::U8) => out.push(*v), @@ -594,9 +637,11 @@ fn encode_array_element(value: &Value, expected: &ValueType, out: &mut Vec) (Value::F64(v), ValueType::F64) => out.extend_from_slice(&v.to_le_bytes()), (Value::Char(v), ValueType::Char) => out.extend_from_slice(&(*v as u32).to_le_bytes()), (Value::Flags(v), ValueType::Flags) => out.extend_from_slice(&v.to_le_bytes()), - _ => return Err(AbiError::InvalidEncoding( - String::from("Array element type mismatch"), - )), + _ => { + return Err(AbiError::InvalidEncoding(String::from( + "Array element type mismatch", + ))) + } } Ok(()) } @@ -623,14 +668,18 @@ fn decode_array_element(cursor: &mut Cursor<'_>, elem_type: &ValueType) -> Resul } ValueType::Char => { let raw = cursor.read_u32()?; - Value::Char(char::from_u32(raw).ok_or_else(|| { - AbiError::InvalidEncoding(String::from("Invalid char in array")) - })?) + Value::Char( + char::from_u32(raw).ok_or_else(|| { + AbiError::InvalidEncoding(String::from("Invalid char in array")) + })?, + ) } ValueType::Flags => Value::Flags(cursor.read_u64()?), - _ => return Err(AbiError::InvalidEncoding( - String::from("Non-primitive type in array"), - )), + _ => { + return Err(AbiError::InvalidEncoding(String::from( + "Non-primitive type in array", + ))) + } }) } @@ -723,15 +772,17 @@ fn decode_value_type(cursor: &mut Cursor<'_>) -> Result { TYPE_RECORD => { let len = cursor.read_u32()? as usize; let bytes = cursor.read_bytes(len)?; - let name = core::str::from_utf8(bytes) - .map_err(|_| AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")))?; + let name = core::str::from_utf8(bytes).map_err(|_| { + AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")) + })?; Ok(ValueType::Record(String::from(name))) } TYPE_VARIANT => { let len = cursor.read_u32()? as usize; let bytes = cursor.read_bytes(len)?; - let name = core::str::from_utf8(bytes) - .map_err(|_| AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")))?; + let name = core::str::from_utf8(bytes).map_err(|_| { + AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")) + })?; Ok(ValueType::Variant(String::from(name))) } TYPE_TUPLE => { @@ -885,7 +936,11 @@ impl GraphCodec for Value { payload, })) } - Value::Result { ok_type, err_type, value } => { + Value::Result { + ok_type, + err_type, + value, + } => { // v2 format: [ok_type:type_tag*, err_type:type_tag*, tag:u32, has_payload:u8, child_index?:u32] let mut payload = Vec::new(); encode_value_type(ok_type, &mut payload); @@ -934,7 +989,12 @@ impl GraphCodec for Value { payload, })) } - Value::Variant { type_name, case_name, tag, payload: var_payload } => { + Value::Variant { + type_name, + case_name, + tag, + payload: var_payload, + } => { // Encode children first let mut child_indices = Vec::with_capacity(var_payload.len()); for item in var_payload { @@ -984,9 +1044,9 @@ fn decode_value( ))); } - let node = decoder.node(index).ok_or_else(|| { - AbiError::InvalidEncoding(format!("Node index {index} out of range")) - })?; + let node = decoder + .node(index) + .ok_or_else(|| AbiError::InvalidEncoding(format!("Node index {index} out of range")))?; let mut cursor = Cursor::new(&node.payload); let value = match node.kind { NodeKind::Bool => Value::Bool(cursor.read_u8()? == 1), @@ -1062,16 +1122,18 @@ fn decode_value( // v2 format: [type_name_len:u32, type_name:utf8, field_count:u32, field_names:(len:u32, name:utf8)*, child_indices:u32*] let type_name_len = cursor.read_u32()? as usize; let type_name_bytes = cursor.read_bytes(type_name_len)?; - let type_name = core::str::from_utf8(type_name_bytes) - .map_err(|_| AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")))?; + let type_name = core::str::from_utf8(type_name_bytes).map_err(|_| { + AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")) + })?; let type_name = String::from(type_name); let count = cursor.read_u32()? as usize; let mut field_names = Vec::with_capacity(count); for _ in 0..count { let name_len = cursor.read_u32()? as usize; let name_bytes = cursor.read_bytes(name_len)?; - let name = core::str::from_utf8(name_bytes) - .map_err(|_| AbiError::InvalidEncoding(String::from("Invalid UTF-8 in field name")))?; + let name = core::str::from_utf8(name_bytes).map_err(|_| { + AbiError::InvalidEncoding(String::from("Invalid UTF-8 in field name")) + })?; field_names.push(String::from(name)); } let mut fields = Vec::with_capacity(count); @@ -1120,21 +1182,29 @@ fn decode_value( Err(alloc::boxed::Box::new(inner)) } } else { - return Err(AbiError::InvalidEncoding(String::from("Result must have payload"))); + return Err(AbiError::InvalidEncoding(String::from( + "Result must have payload", + ))); }; - Value::Result { ok_type, err_type, value } + Value::Result { + ok_type, + err_type, + value, + } } NodeKind::Variant => { // v2 format: [type_name_len:u32, type_name:utf8, case_name_len:u32, case_name:utf8, tag:u32, payload_count:u32, child_indices:u32*] let type_name_len = cursor.read_u32()? as usize; let type_name_bytes = cursor.read_bytes(type_name_len)?; - let type_name = core::str::from_utf8(type_name_bytes) - .map_err(|_| AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")))?; + let type_name = core::str::from_utf8(type_name_bytes).map_err(|_| { + AbiError::InvalidEncoding(String::from("Invalid UTF-8 in type name")) + })?; let type_name = String::from(type_name); let case_name_len = cursor.read_u32()? as usize; let case_name_bytes = cursor.read_bytes(case_name_len)?; - let case_name = core::str::from_utf8(case_name_bytes) - .map_err(|_| AbiError::InvalidEncoding(String::from("Invalid UTF-8 in case name")))?; + let case_name = core::str::from_utf8(case_name_bytes).map_err(|_| { + AbiError::InvalidEncoding(String::from("Invalid UTF-8 in case name")) + })?; let case_name = String::from(case_name); let tag = cursor.read_u32()? as usize; let payload_count = cursor.read_u32()? as usize; @@ -1143,7 +1213,12 @@ fn decode_value( let child = cursor.read_u32()?; payload.push(decode_value(decoder, child, cache, visiting)?); } - Value::Variant { type_name, case_name, tag, payload } + Value::Variant { + type_name, + case_name, + tag, + payload, + } } }; diff --git a/crates/pack-abi/src/value.rs b/crates/pack-abi/src/value.rs index d6f6e8a..84d39d3 100644 --- a/crates/pack-abi/src/value.rs +++ b/crates/pack-abi/src/value.rs @@ -24,9 +24,12 @@ pub enum ValueType { String, List(Box), Option(Box), - Result { ok: Box, err: Box }, - Record(String), // type name - Variant(String), // type name + Result { + ok: Box, + err: Box, + }, + Record(String), // type name + Variant(String), // type name Tuple(Vec), Flags, } @@ -51,11 +54,29 @@ pub enum Value { String(String), // Compound types WITH type info - List { elem_type: ValueType, items: Vec }, - Option { inner_type: ValueType, value: Option> }, - Result { ok_type: ValueType, err_type: ValueType, value: core::result::Result, Box> }, - Record { type_name: String, fields: Vec<(String, Value)> }, - Variant { type_name: String, case_name: String, tag: usize, payload: Vec }, + List { + elem_type: ValueType, + items: Vec, + }, + Option { + inner_type: ValueType, + value: Option>, + }, + Result { + ok_type: ValueType, + err_type: ValueType, + value: core::result::Result, Box>, + }, + Record { + type_name: String, + fields: Vec<(String, Value)>, + }, + Variant { + type_name: String, + case_name: String, + tag: usize, + payload: Vec, + }, // Keep Tuple as-is (no type info needed - positional) Tuple(Vec), @@ -114,7 +135,9 @@ impl Value { Value::String(_) => ValueType::String, Value::List { elem_type, .. } => ValueType::List(Box::new(elem_type.clone())), Value::Option { inner_type, .. } => ValueType::Option(Box::new(inner_type.clone())), - Value::Result { ok_type, err_type, .. } => ValueType::Result { + Value::Result { + ok_type, err_type, .. + } => ValueType::Result { ok: Box::new(ok_type.clone()), err: Box::new(err_type.clone()), }, @@ -131,66 +154,97 @@ impl Value { // ============================================================================ impl From for Value { - fn from(v: bool) -> Self { Value::Bool(v) } + fn from(v: bool) -> Self { + Value::Bool(v) + } } impl From for Value { - fn from(v: u8) -> Self { Value::U8(v) } + fn from(v: u8) -> Self { + Value::U8(v) + } } impl From for Value { - fn from(v: u16) -> Self { Value::U16(v) } + fn from(v: u16) -> Self { + Value::U16(v) + } } impl From for Value { - fn from(v: u32) -> Self { Value::U32(v) } + fn from(v: u32) -> Self { + Value::U32(v) + } } impl From for Value { - fn from(v: u64) -> Self { Value::U64(v) } + fn from(v: u64) -> Self { + Value::U64(v) + } } impl From for Value { - fn from(v: i8) -> Self { Value::S8(v) } + fn from(v: i8) -> Self { + Value::S8(v) + } } impl From for Value { - fn from(v: i16) -> Self { Value::S16(v) } + fn from(v: i16) -> Self { + Value::S16(v) + } } impl From for Value { - fn from(v: i32) -> Self { Value::S32(v) } + fn from(v: i32) -> Self { + Value::S32(v) + } } impl From for Value { - fn from(v: i64) -> Self { Value::S64(v) } + fn from(v: i64) -> Self { + Value::S64(v) + } } impl From for Value { - fn from(v: f32) -> Self { Value::F32(v) } + fn from(v: f32) -> Self { + Value::F32(v) + } } impl From for Value { - fn from(v: f64) -> Self { Value::F64(v) } + fn from(v: f64) -> Self { + Value::F64(v) + } } impl From for Value { - fn from(v: char) -> Self { Value::Char(v) } + fn from(v: char) -> Self { + Value::Char(v) + } } impl From for Value { - fn from(v: String) -> Self { Value::String(v) } + fn from(v: String) -> Self { + Value::String(v) + } } impl From<&str> for Value { - fn from(v: &str) -> Self { Value::String(String::from(v)) } + fn from(v: &str) -> Self { + Value::String(String::from(v)) + } } impl> From> for Value { fn from(v: Vec) -> Self { let items: Vec = v.into_iter().map(Into::into).collect(); // Infer elem_type from first item, default to S32 - let elem_type = items.first().map(|v| v.infer_type()).unwrap_or(ValueType::S32); + let elem_type = items + .first() + .map(|v| v.infer_type()) + .unwrap_or(ValueType::S32); Value::List { elem_type, items } } } @@ -198,7 +252,10 @@ impl> From> for Value { impl, const N: usize> From<[T; N]> for Value { fn from(v: [T; N]) -> Self { let items: Vec = v.into_iter().map(Into::into).collect(); - let elem_type = items.first().map(|v| v.infer_type()).unwrap_or(ValueType::S32); + let elem_type = items + .first() + .map(|v| v.infer_type()) + .unwrap_or(ValueType::S32); Value::List { elem_type, items } } } @@ -218,13 +275,12 @@ impl, const N: usize> TryFrom .into_iter() .enumerate() .map(|(i, item)| { - T::try_from(item) - .map_err(|e| ConversionError::IndexError(i, Box::new(e))) + T::try_from(item).map_err(|e| ConversionError::IndexError(i, Box::new(e))) }) .collect::, _>>()?; - vec.try_into().map_err(|_| ConversionError::ExpectedList( - String::from("array conversion failed"), - )) + vec.try_into().map_err(|_| { + ConversionError::ExpectedList(String::from("array conversion failed")) + }) } other => Err(ConversionError::ExpectedList(format!("{:?}", other))), } @@ -445,7 +501,10 @@ impl> TryFrom for Vec { impl + Ord> From> for Value { fn from(v: BTreeSet) -> Self { let items: Vec = v.into_iter().map(Into::into).collect(); - let elem_type = items.first().map(|v| v.infer_type()).unwrap_or(ValueType::S32); + let elem_type = items + .first() + .map(|v| v.infer_type()) + .unwrap_or(ValueType::S32); Value::List { elem_type, items } } } @@ -472,13 +531,18 @@ impl + Ord, V: Into> From> for Value { .into_iter() .map(|(k, v)| Value::Tuple(Vec::from([k.into(), v.into()]))) .collect(); - let elem_type = items.first().map(|v| v.infer_type()).unwrap_or(ValueType::S32); + let elem_type = items + .first() + .map(|v| v.infer_type()) + .unwrap_or(ValueType::S32); Value::List { elem_type, items } } } -impl + Ord, V: TryFrom> - TryFrom for BTreeMap +impl< + K: TryFrom + Ord, + V: TryFrom, + > TryFrom for BTreeMap { type Error = ConversionError; fn try_from(v: Value) -> Result { @@ -536,7 +600,9 @@ impl FromValue for Option { fn from_value(v: Value) -> Result { match v { Value::Option { value: None, .. } => Ok(None), - Value::Option { value: Some(inner), .. } => { + Value::Option { + value: Some(inner), .. + } => { let value = T::from_value(*inner)?; Ok(Some(value)) } @@ -549,23 +615,31 @@ impl FromValue for Option { impl FromValue for core::result::Result { fn from_value(v: Value) -> Result { match v { - Value::Result { value: Ok(inner), .. } => { + Value::Result { + value: Ok(inner), .. + } => { let value = T::from_value(*inner) .map_err(|e| ConversionError::PayloadError(Box::new(e)))?; Ok(Ok(value)) } - Value::Result { value: Err(inner), .. } => { + Value::Result { + value: Err(inner), .. + } => { let value = E::from_value(*inner) .map_err(|e| ConversionError::PayloadError(Box::new(e)))?; Ok(Err(value)) } // Also support legacy variant encoding for backwards compatibility - Value::Variant { tag: 0, payload, .. } if !payload.is_empty() => { + Value::Variant { + tag: 0, payload, .. + } if !payload.is_empty() => { let value = T::from_value(payload.into_iter().next().unwrap()) .map_err(|e| ConversionError::PayloadError(Box::new(e)))?; Ok(Ok(value)) } - Value::Variant { tag: 1, payload, .. } if !payload.is_empty() => { + Value::Variant { + tag: 1, payload, .. + } if !payload.is_empty() => { let value = E::from_value(payload.into_iter().next().unwrap()) .map_err(|e| ConversionError::PayloadError(Box::new(e)))?; Ok(Err(value)) diff --git a/crates/pack-abi/tests/derive_tests.rs b/crates/pack-abi/tests/derive_tests.rs index 6620201..f48e01c 100644 --- a/crates/pack-abi/tests/derive_tests.rs +++ b/crates/pack-abi/tests/derive_tests.rs @@ -220,10 +220,7 @@ fn recursive_enum_leaf() { fn recursive_enum_nested() { let original = Tree::Node(vec![ Tree::Leaf(1), - Tree::Node(vec![ - Tree::Leaf(2), - Tree::Leaf(3), - ]), + Tree::Node(vec![Tree::Leaf(2), Tree::Leaf(3)]), Tree::Leaf(4), ]); let value: Value = original.clone().into(); diff --git a/crates/pack-derive/src/lib.rs b/crates/pack-derive/src/lib.rs index cac060b..40de558 100644 --- a/crates/pack-derive/src/lib.rs +++ b/crates/pack-derive/src/lib.rs @@ -35,11 +35,8 @@ //! ``` use proc_macro::TokenStream; -use quote::{quote, format_ident}; -use syn::{ - parse_macro_input, Data, DeriveInput, Fields, - Attribute, Meta, -}; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, Attribute, Data, DeriveInput, Fields, Meta}; /// Extract the crate path from `#[graph(crate = "...")]` attribute. /// Defaults to `pack_abi` if not specified. @@ -54,10 +51,10 @@ fn get_crate_path(attrs: &[Attribute]) -> proc_macro2::TokenStream { if let Some(rest) = rest.strip_prefix('=') { let rest = rest.trim(); if rest.starts_with('"') && rest.ends_with('"') { - let path_str = &rest[1..rest.len()-1]; + let path_str = &rest[1..rest.len() - 1]; // Convert string path to token stream - let path: syn::Path = syn::parse_str(path_str) - .expect("Invalid crate path"); + let path: syn::Path = + syn::parse_str(path_str).expect("Invalid crate path"); return quote! { #path }; } } @@ -119,7 +116,11 @@ pub fn derive_graph_value(input: TokenStream) -> TokenStream { expanded.into() } -fn derive_struct(input: &DeriveInput, data: &syn::DataStruct, krate: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { +fn derive_struct( + input: &DeriveInput, + data: &syn::DataStruct, + krate: &proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); @@ -150,16 +151,21 @@ fn derive_struct(input: &DeriveInput, data: &syn::DataStruct, krate: &proc_macro let field_count = fields.named.len(); // Generate field accessors for From impl - let field_accessors: Vec<_> = fields.named.iter().map(|f| { - let field_name = f.ident.as_ref().unwrap(); - let field_name_str = get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string()); - quote! { - ( - #krate::__private::String::from(#field_name_str), - #krate::Value::from(value.#field_name) - ) - } - }).collect(); + let field_accessors: Vec<_> = fields + .named + .iter() + .map(|f| { + let field_name = f.ident.as_ref().unwrap(); + let field_name_str = + get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string()); + quote! { + ( + #krate::__private::String::from(#field_name_str), + #krate::Value::from(value.#field_name) + ) + } + }) + .collect(); let type_name_str = name.to_string(); @@ -201,9 +207,7 @@ fn derive_struct(input: &DeriveInput, data: &syn::DataStruct, krate: &proc_macro } Fields::Unnamed(fields) => { // Tuple struct -> Value::Tuple - let field_indices: Vec<_> = (0..fields.unnamed.len()) - .map(syn::Index::from) - .collect(); + let field_indices: Vec<_> = (0..fields.unnamed.len()).map(syn::Index::from).collect(); let field_from_value: Vec<_> = fields.unnamed.iter().enumerate().map(|(i, f)| { let field_type = &f.ty; @@ -283,83 +287,99 @@ fn derive_struct(input: &DeriveInput, data: &syn::DataStruct, krate: &proc_macro } } -fn derive_enum(input: &DeriveInput, data: &syn::DataEnum, krate: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { +fn derive_enum( + input: &DeriveInput, + data: &syn::DataEnum, + krate: &proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { let name = &input.ident; let type_name_str = name.to_string(); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); // Generate match arms for From for Value - let to_value_arms: Vec<_> = data.variants.iter().enumerate().map(|(default_tag, variant)| { - let variant_name = &variant.ident; - let case_name_str = variant_name.to_string(); - let tag = get_tag(&variant.attrs).unwrap_or(default_tag); + let to_value_arms: Vec<_> = data + .variants + .iter() + .enumerate() + .map(|(default_tag, variant)| { + let variant_name = &variant.ident; + let case_name_str = variant_name.to_string(); + let tag = get_tag(&variant.attrs).unwrap_or(default_tag); + + match &variant.fields { + Fields::Named(fields) => { + let field_names: Vec<_> = fields + .named + .iter() + .map(|f| f.ident.as_ref().unwrap()) + .collect(); + // For named fields, we wrap in a Record as the single payload element + let field_to_value: Vec<_> = fields + .named + .iter() + .map(|f| { + let field_name = f.ident.as_ref().unwrap(); + let field_name_str = + get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string()); + quote! { + ( + #krate::__private::String::from(#field_name_str), + #krate::Value::from(#field_name) + ) + } + }) + .collect(); - match &variant.fields { - Fields::Named(fields) => { - let field_names: Vec<_> = fields.named.iter() - .map(|f| f.ident.as_ref().unwrap()) - .collect(); - // For named fields, we wrap in a Record as the single payload element - let field_to_value: Vec<_> = fields.named.iter().map(|f| { - let field_name = f.ident.as_ref().unwrap(); - let field_name_str = get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string()); quote! { - ( - #krate::__private::String::from(#field_name_str), - #krate::Value::from(#field_name) - ) - } - }).collect(); - - quote! { - #name::#variant_name { #(#field_names),* } => { - #krate::Value::Variant { - type_name: #krate::__private::String::from(#type_name_str), - case_name: #krate::__private::String::from(#case_name_str), - tag: #tag, - payload: #krate::__private::vec![ - #krate::Value::Record { - type_name: #krate::__private::String::from(#case_name_str), - fields: #krate::__private::vec![#(#field_to_value),*], - } - ], + #name::#variant_name { #(#field_names),* } => { + #krate::Value::Variant { + type_name: #krate::__private::String::from(#type_name_str), + case_name: #krate::__private::String::from(#case_name_str), + tag: #tag, + payload: #krate::__private::vec![ + #krate::Value::Record { + type_name: #krate::__private::String::from(#case_name_str), + fields: #krate::__private::vec![#(#field_to_value),*], + } + ], + } } } } - } - Fields::Unnamed(fields) => { - let field_names: Vec<_> = (0..fields.unnamed.len()) - .map(|i| format_ident!("f{}", i)) - .collect(); + Fields::Unnamed(fields) => { + let field_names: Vec<_> = (0..fields.unnamed.len()) + .map(|i| format_ident!("f{}", i)) + .collect(); - // Payload is a vec of all the field values - quote! { - #name::#variant_name(#(#field_names),*) => { - #krate::Value::Variant { - type_name: #krate::__private::String::from(#type_name_str), - case_name: #krate::__private::String::from(#case_name_str), - tag: #tag, - payload: #krate::__private::vec![ - #(#krate::Value::from(#field_names)),* - ], + // Payload is a vec of all the field values + quote! { + #name::#variant_name(#(#field_names),*) => { + #krate::Value::Variant { + type_name: #krate::__private::String::from(#type_name_str), + case_name: #krate::__private::String::from(#case_name_str), + tag: #tag, + payload: #krate::__private::vec![ + #(#krate::Value::from(#field_names)),* + ], + } } } } - } - Fields::Unit => { - quote! { - #name::#variant_name => { - #krate::Value::Variant { - type_name: #krate::__private::String::from(#type_name_str), - case_name: #krate::__private::String::from(#case_name_str), - tag: #tag, - payload: #krate::__private::vec![], + Fields::Unit => { + quote! { + #name::#variant_name => { + #krate::Value::Variant { + type_name: #krate::__private::String::from(#type_name_str), + case_name: #krate::__private::String::from(#case_name_str), + tag: #tag, + payload: #krate::__private::vec![], + } } } } } - } - }).collect(); + }) + .collect(); // Generate match arms for TryFrom for T let from_value_arms: Vec<_> = data.variants.iter().enumerate().map(|(default_tag, variant)| { @@ -495,7 +515,7 @@ fn get_rename(attrs: &[Attribute]) -> Option { if let Some(rest) = rest.strip_prefix('=') { let rest = rest.trim(); if rest.starts_with('"') && rest.ends_with('"') { - return Some(rest[1..rest.len()-1].to_string()); + return Some(rest[1..rest.len() - 1].to_string()); } } } diff --git a/examples/test_alloc.rs b/examples/test_alloc.rs index 99f80d1..7b545c6 100644 --- a/examples/test_alloc.rs +++ b/examples/test_alloc.rs @@ -1,5 +1,5 @@ -use pack::runtime::Runtime; use pack::abi::Value; +use pack::runtime::Runtime; fn main() -> anyhow::Result<()> { // Test with direct wasmtime to confirm it works @@ -14,9 +14,9 @@ fn main() -> anyhow::Result<()> { } fn test_direct() -> anyhow::Result<()> { + use pack::abi::{decode, encode, Value}; + use pack::runtime::{RESULT_LEN_OFFSET, RESULT_PTR_OFFSET}; use wasmtime::*; - use pack::abi::{encode, decode, Value}; - use pack::runtime::{RESULT_PTR_OFFSET, RESULT_LEN_OFFSET}; let wasm_path = "/home/colin/work/pack/packages/echo/target/wasm32-unknown-unknown/release/echo_package.wasm"; let wasm_bytes = std::fs::read(wasm_path)?; @@ -39,12 +39,15 @@ fn test_direct() -> anyhow::Result<()> { memory.write(&mut store, in_ptr as usize, &input_bytes)?; let echo = instance.get_typed_func::<(i32, i32, i32, i32), i32>(&mut store, "echo")?; - let status = echo.call(&mut store, ( - in_ptr, - input_bytes.len() as i32, - RESULT_PTR_OFFSET as i32, - RESULT_LEN_OFFSET as i32 - ))?; + let status = echo.call( + &mut store, + ( + in_ptr, + input_bytes.len() as i32, + RESULT_PTR_OFFSET as i32, + RESULT_LEN_OFFSET as i32, + ), + )?; println!("Status: {}", status); // Try freeing the input buffer diff --git a/examples/transforms/main.rs b/examples/transforms/main.rs index 1f28145..136e02e 100644 --- a/examples/transforms/main.rs +++ b/examples/transforms/main.rs @@ -1,7 +1,4 @@ -use pack::{ - parse_pact_dir_with_registry, PactExport, TransformRegistry, - generate_rust, -}; +use pack::{generate_rust, parse_pact_dir_with_registry, PactExport, TransformRegistry}; fn main() -> Result<(), Box> { let (root, type_registry) = parse_pact_dir_with_registry("examples/transforms")?; @@ -10,16 +7,28 @@ fn main() -> Result<(), Box> { println!("=== Parsed Interfaces ===\n"); for child in &root.children { println!("Interface: {}", child.name); - println!(" Uses: {:?}", child.uses.iter().map(|u| { - if u.transform_args.is_empty() { - u.interface.clone() - } else { - format!("{}({:?})", u.interface, u.transform_args) - } - }).collect::>()); - println!(" Aliases: {:?}", child.aliases.iter().map(|a| { - format!("{} = {}({:?})", a.name, a.transform, a.args) - }).collect::>()); + println!( + " Uses: {:?}", + child + .uses + .iter() + .map(|u| { + if u.transform_args.is_empty() { + u.interface.clone() + } else { + format!("{}({:?})", u.interface, u.transform_args) + } + }) + .collect::>() + ); + println!( + " Aliases: {:?}", + child + .aliases + .iter() + .map(|a| { format!("{} = {}({:?})", a.name, a.transform, a.args) }) + .collect::>() + ); println!(); } @@ -33,11 +42,8 @@ fn main() -> Result<(), Box> { println!(); println!("=== Transformed rpc(calculator) ===\n"); - let rpc_calc = type_registry.get_transformed_interface( - "rpc", - "calculator", - &transform_registry - )?; + let rpc_calc = + type_registry.get_transformed_interface("rpc", "calculator", &transform_registry)?; println!("Name: {}", rpc_calc.name); println!("Types added:"); diff --git a/flake.nix b/flake.nix index 27bbe65..e16be1c 100644 --- a/flake.nix +++ b/flake.nix @@ -122,14 +122,14 @@ fi TITLE=$(echo "$DESCRIPTION" | head -1) - BRANCH=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-' | head -c 50) + BRANCH=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-' | ${pkgs.gnused}/bin/sed 's/--*/-/g; s/^-//; s/-$//' | head -c 50) echo "Creating PR: $TITLE" echo "Branch: $BRANCH" echo "" jj bookmark create "$BRANCH" -r @ 2>/dev/null || jj bookmark set "$BRANCH" -r @ - jj git push --bookmark "$BRANCH" + jj git push --bookmark "$BRANCH" --allow-new ${pkgs.gh}/bin/gh pr create \ --title "$TITLE" \ diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 8a26fa1..17c2a07 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -90,13 +90,14 @@ pub struct GraphBuffer { pub root: u32, } +#[derive(Default)] pub struct Encoder { nodes: Vec, } impl Encoder { pub fn new() -> Self { - Self { nodes: Vec::new() } + Self::default() } pub fn push_node(&mut self, node: Node) -> u32 { @@ -196,7 +197,9 @@ impl GraphBuffer { let _flags = cursor.read_u16()?; let node_count = cursor.read_u32()? as usize; if node_count > limits.max_node_count { - return Err(AbiError::InvalidEncoding("Node count exceeds limit".to_string())); + return Err(AbiError::InvalidEncoding( + "Node count exceeds limit".to_string(), + )); } let root = cursor.read_u32()?; @@ -214,7 +217,9 @@ impl GraphBuffer { } if (root as usize) >= nodes.len() { - return Err(AbiError::InvalidEncoding("Root index out of range".to_string())); + return Err(AbiError::InvalidEncoding( + "Root index out of range".to_string(), + )); } if !cursor.is_eof() { @@ -232,10 +237,14 @@ impl GraphBuffer { pub fn validate_basic_with_limits(&self, limits: &Limits) -> Result<(), AbiError> { let node_count = self.nodes.len(); if (self.root as usize) >= node_count { - return Err(AbiError::InvalidEncoding("Root index out of range".to_string())); + return Err(AbiError::InvalidEncoding( + "Root index out of range".to_string(), + )); } if node_count > limits.max_node_count { - return Err(AbiError::InvalidEncoding("Node count exceeds limit".to_string())); + return Err(AbiError::InvalidEncoding( + "Node count exceeds limit".to_string(), + )); } for (index, node) in self.nodes.iter().enumerate() { @@ -269,9 +278,7 @@ impl GraphBuffer { NodeKind::Char => { let value = cursor.read_u32()?; let ch = char::from_u32(value).ok_or_else(|| { - AbiError::InvalidEncoding(format!( - "Invalid char scalar at node {index}" - )) + AbiError::InvalidEncoding(format!("Invalid char scalar at node {index}")) })?; let _ = ch; } @@ -282,9 +289,7 @@ impl GraphBuffer { let len = cursor.read_u32()? as usize; let bytes = cursor.read_bytes(len)?; std::str::from_utf8(bytes).map_err(|_| { - AbiError::InvalidEncoding(format!( - "Invalid UTF-8 string at node {index}" - )) + AbiError::InvalidEncoding(format!("Invalid UTF-8 string at node {index}")) })?; } NodeKind::Tuple => { @@ -322,15 +327,25 @@ impl GraphBuffer { } cursor.read_bytes(count * width)?; } - NodeKind::List | NodeKind::Option | NodeKind::Record | NodeKind::Variant | NodeKind::Result => { + NodeKind::List + | NodeKind::Option + | NodeKind::Record + | NodeKind::Variant + | NodeKind::Result => { // These have variable-length type tags or string headers // Skip detailed validation, just ensure payload is not too large (already checked) } } // Don't check for trailing bytes on v2 nodes with variable headers - if !matches!(node.kind, NodeKind::List | NodeKind::Option | NodeKind::Record | NodeKind::Variant | NodeKind::Result) - && !cursor.is_eof() + if !matches!( + node.kind, + NodeKind::List + | NodeKind::Option + | NodeKind::Record + | NodeKind::Variant + | NodeKind::Result + ) && !cursor.is_eof() { return Err(AbiError::InvalidEncoding(format!( "Trailing payload bytes at node {index}" @@ -452,7 +467,11 @@ fn fixed_width(ty: &ValueType) -> Option { } /// Encode a single primitive value into a contiguous byte buffer (for Array nodes). -fn encode_array_element(value: &Value, expected: &ValueType, out: &mut Vec) -> Result<(), AbiError> { +fn encode_array_element( + value: &Value, + expected: &ValueType, + out: &mut Vec, +) -> Result<(), AbiError> { match (value, expected) { (Value::Bool(v), ValueType::Bool) => out.push(if *v { 1 } else { 0 }), (Value::U8(v), ValueType::U8) => out.push(*v), @@ -467,9 +486,11 @@ fn encode_array_element(value: &Value, expected: &ValueType, out: &mut Vec) (Value::F64(v), ValueType::F64) => out.extend_from_slice(&v.to_le_bytes()), (Value::Char(v), ValueType::Char) => out.extend_from_slice(&(*v as u32).to_le_bytes()), (Value::Flags(v), ValueType::Flags) => out.extend_from_slice(&v.to_le_bytes()), - _ => return Err(AbiError::InvalidEncoding( - "Array element type mismatch".to_string(), - )), + _ => { + return Err(AbiError::InvalidEncoding( + "Array element type mismatch".to_string(), + )) + } } Ok(()) } @@ -496,14 +517,18 @@ fn decode_array_element(cursor: &mut Cursor<'_>, elem_type: &ValueType) -> Resul } ValueType::Char => { let raw = cursor.read_u32()?; - Value::Char(char::from_u32(raw).ok_or_else(|| { - AbiError::InvalidEncoding("Invalid char in array".to_string()) - })?) + Value::Char( + char::from_u32(raw).ok_or_else(|| { + AbiError::InvalidEncoding("Invalid char in array".to_string()) + })?, + ) } ValueType::Flags => Value::Flags(cursor.read_u64()?), - _ => return Err(AbiError::InvalidEncoding( - "Non-primitive type in array".to_string(), - )), + _ => { + return Err(AbiError::InvalidEncoding( + "Non-primitive type in array".to_string(), + )) + } }) } @@ -586,7 +611,10 @@ fn decode_value_type(cursor: &mut Cursor<'_>) -> Result { TYPE_RESULT => { let ok = decode_value_type(cursor)?; let err = decode_value_type(cursor)?; - Ok(ValueType::Result { ok: Box::new(ok), err: Box::new(err) }) + Ok(ValueType::Result { + ok: Box::new(ok), + err: Box::new(err), + }) } TYPE_RECORD => { let len = cursor.read_u32()? as usize; @@ -600,7 +628,9 @@ fn decode_value_type(cursor: &mut Cursor<'_>) -> Result { let len = cursor.read_u32()? as usize; let bytes = cursor.read_bytes(len)?; let name = std::str::from_utf8(bytes) - .map_err(|_| AbiError::InvalidEncoding("Invalid UTF-8 in variant name".to_string()))? + .map_err(|_| { + AbiError::InvalidEncoding("Invalid UTF-8 in variant name".to_string()) + })? .to_string(); Ok(ValueType::Variant(name)) } @@ -704,7 +734,8 @@ impl GraphCodec for Value { if fixed_width(elem_type).is_some() { return Err(AbiError::InvalidEncoding( "List with primitive elem_type contains non-matching items; \ - fix the elem_type or the items".to_string(), + fix the elem_type or the items" + .to_string(), )); } // Encode children first @@ -755,7 +786,11 @@ impl GraphCodec for Value { payload, })) } - Value::Result { ok_type, err_type, value } => { + Value::Result { + ok_type, + err_type, + value, + } => { // v2 format: [ok_type:type_tag*, err_type:type_tag*, tag:u32, has_payload:u8, child_index?:u32] let mut payload = Vec::new(); encode_value_type(ok_type, &mut payload); @@ -804,7 +839,12 @@ impl GraphCodec for Value { payload, })) } - Value::Variant { type_name, case_name, tag, payload: var_payload } => { + Value::Variant { + type_name, + case_name, + tag, + payload: var_payload, + } => { // Encode children first let mut child_indices = Vec::with_capacity(var_payload.len()); for item in var_payload { @@ -854,9 +894,9 @@ fn decode_value( )); } - let node = decoder.node(index).ok_or_else(|| { - AbiError::InvalidEncoding(format!("Node index {index} out of range")) - })?; + let node = decoder + .node(index) + .ok_or_else(|| AbiError::InvalidEncoding(format!("Node index {index} out of range")))?; let mut cursor = Cursor::new(&node.payload); let value = match node.kind { NodeKind::Bool => Value::Bool(cursor.read_u8()? == 1), @@ -947,7 +987,9 @@ fn decode_value( let name_len = cursor.read_u32()? as usize; let name_bytes = cursor.read_bytes(name_len)?; let name = std::str::from_utf8(name_bytes) - .map_err(|_| AbiError::InvalidEncoding("Invalid UTF-8 in field name".to_string()))? + .map_err(|_| { + AbiError::InvalidEncoding("Invalid UTF-8 in field name".to_string()) + })? .to_string(); field_names.push(name); } @@ -995,9 +1037,15 @@ fn decode_value( Err(Box::new(inner)) } } else { - return Err(AbiError::InvalidEncoding("Result must have payload".to_string())); + return Err(AbiError::InvalidEncoding( + "Result must have payload".to_string(), + )); }; - Value::Result { ok_type, err_type, value } + Value::Result { + ok_type, + err_type, + value, + } } NodeKind::Variant => { // v2 format: [type_name_len:u32, type_name:utf8, case_name_len:u32, case_name:utf8, tag:u32, payload_count:u32, child_indices:u32*] @@ -1018,7 +1066,12 @@ fn decode_value( let child = cursor.read_u32()?; payload.push(decode_value(decoder, child, cache, visiting)?); } - Value::Variant { type_name, case_name, tag, payload } + Value::Variant { + type_name, + case_name, + tag, + payload, + } } }; diff --git a/src/bin/pact.rs b/src/bin/pact.rs index 06a73cd..c655580 100644 --- a/src/bin/pact.rs +++ b/src/bin/pact.rs @@ -7,8 +7,8 @@ use clap::{Parser, Subcommand}; use pack::{ - parse_pact_file, parse_pact_dir_with_registry, PactInterface, TypeRegistry, - codegen, TypeDef, Type, + codegen, parse_pact_dir_with_registry, parse_pact_file, PactInterface, Type, TypeDef, + TypeRegistry, }; use std::path::PathBuf; @@ -56,8 +56,7 @@ fn main() -> anyhow::Result<()> { } fn check_command(file: &PathBuf) -> anyhow::Result<()> { - let interface = parse_pact_file(file) - .map_err(|e| anyhow::anyhow!("{}", e))?; + let interface = parse_pact_file(file).map_err(|e| anyhow::anyhow!("{}", e))?; print_interface_summary(&interface, 0); println!("\n✓ {} parsed successfully", file.display()); @@ -65,8 +64,8 @@ fn check_command(file: &PathBuf) -> anyhow::Result<()> { } fn check_dir_command(dir: &PathBuf) -> anyhow::Result<()> { - let (root, registry) = parse_pact_dir_with_registry(dir) - .map_err(|e| anyhow::anyhow!("{}", e))?; + let (root, registry) = + parse_pact_dir_with_registry(dir).map_err(|e| anyhow::anyhow!("{}", e))?; println!("Parsed {} interfaces:", root.children.len()); for child in &root.children { @@ -99,12 +98,19 @@ fn validate_uses(interface: &PactInterface, registry: &TypeRegistry, errors: &mu match registry.resolve_use(use_decl) { Ok(types) => { if use_decl.items.is_empty() { - println!(" ✓ {}: use {} (all {} types)", - interface.name, use_decl.interface, types.len()); + println!( + " ✓ {}: use {} (all {} types)", + interface.name, + use_decl.interface, + types.len() + ); } else { - println!(" ✓ {}: use {}.{{{}}}", - interface.name, use_decl.interface, - use_decl.items.join(", ")); + println!( + " ✓ {}: use {}.{{{}}}", + interface.name, + use_decl.interface, + use_decl.items.join(", ") + ); } } Err(e) => { @@ -121,12 +127,11 @@ fn validate_uses(interface: &PactInterface, registry: &TypeRegistry, errors: &mu fn codegen_command(path: &PathBuf, output: Option<&std::path::Path>) -> anyhow::Result<()> { let interface = if path.is_dir() { - let (root, _registry) = parse_pact_dir_with_registry(path) - .map_err(|e| anyhow::anyhow!("{}", e))?; + let (root, _registry) = + parse_pact_dir_with_registry(path).map_err(|e| anyhow::anyhow!("{}", e))?; root } else { - parse_pact_file(path) - .map_err(|e| anyhow::anyhow!("{}", e))? + parse_pact_file(path).map_err(|e| anyhow::anyhow!("{}", e))? }; let code = codegen::generate_rust(&interface); @@ -159,7 +164,12 @@ fn print_interface_summary(interface: &PactInterface, indent: usize) { if use_decl.items.is_empty() { println!("{} use {}", prefix, use_decl.interface); } else { - println!("{} use {}.{{{}}}", prefix, use_decl.interface, use_decl.items.join(", ")); + println!( + "{} use {}.{{{}}}", + prefix, + use_decl.interface, + use_decl.items.join(", ") + ); } } @@ -233,7 +243,10 @@ fn format_type(ty: &Type) -> String { Type::Option(inner) => format!("option<{}>", format_type(inner)), Type::Result { ok, err } => format!("result<{}, {}>", format_type(ok), format_type(err)), Type::Tuple(types) => { - format!("tuple<{}>", types.iter().map(|t| format_type(t)).collect::>().join(", ")) + format!( + "tuple<{}>", + types.iter().map(format_type).collect::>().join(", ") + ) } Type::Ref(path) => path.to_string(), Type::Value => "value".to_string(), diff --git a/src/codegen.rs b/src/codegen.rs index 1b5031f..dea22c6 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -21,14 +21,14 @@ pub fn generate_rust(interface: &PactInterface) -> String { // Generate type definitions for typedef in &interface.types { output.push_str(&generate_typedef(typedef)); - output.push_str("\n"); + output.push('\n'); } // Generate import traits (what the actor can call) for import in &interface.imports { if let PactImport::Interface(name) = import { output.push_str(&generate_import_trait(name)); - output.push_str("\n"); + output.push('\n'); } } @@ -44,7 +44,7 @@ pub fn generate_rust(interface: &PactInterface) -> String { for line in child_code.lines() { output.push_str(" "); output.push_str(line); - output.push_str("\n"); + output.push('\n'); } output.push_str("}\n\n"); } @@ -70,7 +70,8 @@ fn generate_record(name: &str, fields: &[Field]) -> String { out.push_str(&format!("pub struct {} {{\n", rust_name)); for field in fields { - out.push_str(&format!(" pub {}: {},\n", + out.push_str(&format!( + " pub {}: {},\n", to_snake_case(&field.name), type_to_rust(&field.ty) )); @@ -92,7 +93,11 @@ fn generate_variant(name: &str, cases: &[Case]) -> String { if case.payload == Type::Unit { out.push_str(&format!(" {},\n", case_name)); } else { - out.push_str(&format!(" {}({}),\n", case_name, type_to_rust(&case.payload))); + out.push_str(&format!( + " {}({}),\n", + case_name, + type_to_rust(&case.payload) + )); } } @@ -125,14 +130,17 @@ fn generate_flags(name: &str, flags: &[String]) -> String { out.push_str(&format!("impl {} {{\n", rust_name)); for (i, flag) in flags.iter().enumerate() { - out.push_str(&format!(" pub const {}: Self = Self(1 << {});\n", - to_screaming_snake_case(flag), i)); + out.push_str(&format!( + " pub const {}: Self = Self(1 << {});\n", + to_screaming_snake_case(flag), + i + )); } - out.push_str("\n"); + out.push('\n'); out.push_str(" pub fn contains(self, other: Self) -> bool {\n"); out.push_str(" (self.0 & other.0) == other.0\n"); out.push_str(" }\n"); - out.push_str("\n"); + out.push('\n'); out.push_str(" pub fn insert(&mut self, other: Self) {\n"); out.push_str(" self.0 |= other.0;\n"); out.push_str(" }\n"); @@ -142,7 +150,11 @@ fn generate_flags(name: &str, flags: &[String]) -> String { } fn generate_alias(name: &str, target: &Type) -> String { - format!("pub type {} = {};\n", to_pascal_case(name), type_to_rust(target)) + format!( + "pub type {} = {};\n", + to_pascal_case(name), + type_to_rust(target) + ) } fn generate_import_trait(name: &str) -> String { @@ -163,7 +175,9 @@ fn generate_export_trait(interface: &PactInterface) -> String { let generics = if interface.type_params.is_empty() { String::new() } else { - let params: Vec = interface.type_params.iter() + let params: Vec = interface + .type_params + .iter() .map(|tp| tp.name.clone()) .collect(); format!("<{}>", params.join(", ")) @@ -176,7 +190,9 @@ fn generate_export_trait(interface: &PactInterface) -> String { if let PactExport::Function(func) = export { // Build function signature let fn_name = to_snake_case(&func.name); - let params: Vec = func.params.iter() + let params: Vec = func + .params + .iter() .map(|p| format!("{}: {}", to_snake_case(&p.name), type_to_rust(&p.ty))) .collect(); @@ -191,13 +207,14 @@ fn generate_export_trait(interface: &PactInterface) -> String { } else if func.results.len() == 1 { format!(" -> {}", type_to_rust(&func.results[0])) } else { - let types: Vec = func.results.iter() - .map(|t| type_to_rust(t)) - .collect(); + let types: Vec = func.results.iter().map(type_to_rust).collect(); format!(" -> ({})", types.join(", ")) }; - out.push_str(&format!(" fn {}({}){};\n", fn_name, params_str, return_type)); + out.push_str(&format!( + " fn {}({}){};\n", + fn_name, params_str, return_type + )); } } @@ -227,7 +244,7 @@ fn type_to_rust(ty: &Type) -> String { format!("Result<{}, {}>", type_to_rust(ok), type_to_rust(err)) } Type::Tuple(items) => { - let types: Vec = items.iter().map(|t| type_to_rust(t)).collect(); + let types: Vec = items.iter().map(type_to_rust).collect(); format!("({})", types.join(", ")) } Type::Ref(path) => { @@ -243,7 +260,7 @@ fn type_to_rust(ty: &Type) -> String { // ============================================================================ fn to_pascal_case(s: &str) -> String { - s.split(|c| c == '-' || c == '_' || c == '(' || c == ')' || c == '<' || c == '>' || c == ',') + s.split(['-', '_', '(', ')', '<', '>', ',']) .filter(|part| !part.is_empty()) .map(|part| { let mut chars = part.chars(); @@ -385,7 +402,10 @@ mod tests { assert_eq!(to_snake_case("rpc(calculator)"), "rpc_calculator"); // Nested transforms: traced(rpc(calculator)) - assert_eq!(to_pascal_case("traced(rpc(calculator))"), "TracedRpcCalculator"); + assert_eq!( + to_pascal_case("traced(rpc(calculator))"), + "TracedRpcCalculator" + ); // Generic-style: list assert_eq!(to_pascal_case("list"), "ListString"); diff --git a/src/compose/merger.rs b/src/compose/merger.rs index e2a0f6d..e318038 100644 --- a/src/compose/merger.rs +++ b/src/compose/merger.rs @@ -266,7 +266,9 @@ impl Merger { wiring.import_fn.clone(), ); if let Some(&old_import_idx) = resolved_imports.get(&key) { - consumer_remap.functions.insert(old_import_idx, new_func_idx); + consumer_remap + .functions + .insert(old_import_idx, new_func_idx); } } @@ -353,7 +355,7 @@ impl Merger { for module in &self.modules { in_degree.insert(&module.name, 0); } - for (_, providers) in &deps { + for providers in deps.values() { for provider in providers { *in_degree.get_mut(provider).unwrap() += 1; } @@ -693,7 +695,11 @@ impl MergedModule { memory_index, offset_expr, } => { - let new_mem_idx = remap.memories.get(memory_index).copied().unwrap_or(*memory_index); + let new_mem_idx = remap + .memories + .get(memory_index) + .copied() + .unwrap_or(*memory_index); // Apply data offset if this is a constant offset let new_offset = match offset_expr { ConstExpr::I32Const(orig_offset) => { @@ -740,7 +746,11 @@ impl MergedModule { table_index, offset_expr, } => { - let new_table_idx = remap.tables.get(table_index).copied().unwrap_or(*table_index); + let new_table_idx = remap + .tables + .get(table_index) + .copied() + .unwrap_or(*table_index); ElementKind::Active { table_index: new_table_idx, offset_expr: offset_expr.clone(), @@ -764,16 +774,10 @@ impl MergedModule { if !self.types.is_empty() { let mut types = TypeSection::new(); for func_type in &self.types { - let params: Vec = func_type - .params() - .iter() - .map(convert_val_type) - .collect(); - let results: Vec = func_type - .results() - .iter() - .map(convert_val_type) - .collect(); + let params: Vec = + func_type.params().iter().map(convert_val_type).collect(); + let results: Vec = + func_type.results().iter().map(convert_val_type).collect(); types.ty().function(params, results); } module.section(&types); @@ -965,7 +969,11 @@ fn convert_stored_operator(op: &StoredOperator, remap: &IndexRemap) -> Instructi table_index, } => { let new_type = remap.types.get(type_index).copied().unwrap_or(*type_index); - let new_table = remap.tables.get(table_index).copied().unwrap_or(*table_index); + let new_table = remap + .tables + .get(table_index) + .copied() + .unwrap_or(*table_index); Instruction::CallIndirect { type_index: new_type, table_index: new_table, @@ -980,7 +988,11 @@ fn convert_stored_operator(op: &StoredOperator, remap: &IndexRemap) -> Instructi table_index, } => { let new_type = remap.types.get(type_index).copied().unwrap_or(*type_index); - let new_table = remap.tables.get(table_index).copied().unwrap_or(*table_index); + let new_table = remap + .tables + .get(table_index) + .copied() + .unwrap_or(*table_index); Instruction::ReturnCallIndirect { type_index: new_type, table_index: new_table, @@ -1262,18 +1274,16 @@ fn convert_stored_operator(op: &StoredOperator, remap: &IndexRemap) -> Instructi StoredOperator::I64TruncSatF64U => Instruction::I64TruncSatF64U, // Reference types - StoredOperator::RefNull(kind) => { - match kind { - RefTypeKind::Func => Instruction::RefNull(HeapType::Abstract { - shared: false, - ty: wasm_encoder::AbstractHeapType::Func, - }), - RefTypeKind::Extern => Instruction::RefNull(HeapType::Abstract { - shared: false, - ty: wasm_encoder::AbstractHeapType::Extern, - }), - } - } + StoredOperator::RefNull(kind) => match kind { + RefTypeKind::Func => Instruction::RefNull(HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Func, + }), + RefTypeKind::Extern => Instruction::RefNull(HeapType::Abstract { + shared: false, + ty: wasm_encoder::AbstractHeapType::Extern, + }), + }, StoredOperator::RefIsNull => Instruction::RefIsNull, StoredOperator::RefFunc(idx) => { let new_idx = remap.functions.get(idx).copied().unwrap_or(*idx); diff --git a/src/compose/parser.rs b/src/compose/parser.rs index eac2252..cb05593 100644 --- a/src/compose/parser.rs +++ b/src/compose/parser.rs @@ -6,9 +6,9 @@ use std::collections::HashMap; use wasmparser::{ - BinaryReaderError, DataSectionReader, ElementSectionReader, ExportSectionReader, - FunctionBody, FunctionSectionReader, GlobalSectionReader, ImportSectionReader, - MemorySectionReader, Operator, Parser, Payload, TableSectionReader, TypeSectionReader, + BinaryReaderError, DataSectionReader, ElementSectionReader, ExportSectionReader, FunctionBody, + FunctionSectionReader, GlobalSectionReader, ImportSectionReader, MemorySectionReader, Operator, + Parser, Payload, TableSectionReader, TypeSectionReader, }; use super::error::ComposeError; @@ -202,43 +202,156 @@ pub enum StoredOperator { F64Const(f64), // Comparison operators - I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU, - I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU, - F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge, - F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge, + I32Eqz, + I32Eq, + I32Ne, + I32LtS, + I32LtU, + I32GtS, + I32GtU, + I32LeS, + I32LeU, + I32GeS, + I32GeU, + I64Eqz, + I64Eq, + I64Ne, + I64LtS, + I64LtU, + I64GtS, + I64GtU, + I64LeS, + I64LeU, + I64GeS, + I64GeU, + F32Eq, + F32Ne, + F32Lt, + F32Gt, + F32Le, + F32Ge, + F64Eq, + F64Ne, + F64Lt, + F64Gt, + F64Le, + F64Ge, // Numeric operators - i32 - I32Clz, I32Ctz, I32Popcnt, I32Add, I32Sub, I32Mul, I32DivS, I32DivU, - I32RemS, I32RemU, I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr, + I32Clz, + I32Ctz, + I32Popcnt, + I32Add, + I32Sub, + I32Mul, + I32DivS, + I32DivU, + I32RemS, + I32RemU, + I32And, + I32Or, + I32Xor, + I32Shl, + I32ShrS, + I32ShrU, + I32Rotl, + I32Rotr, // Numeric operators - i64 - I64Clz, I64Ctz, I64Popcnt, I64Add, I64Sub, I64Mul, I64DivS, I64DivU, - I64RemS, I64RemU, I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr, + I64Clz, + I64Ctz, + I64Popcnt, + I64Add, + I64Sub, + I64Mul, + I64DivS, + I64DivU, + I64RemS, + I64RemU, + I64And, + I64Or, + I64Xor, + I64Shl, + I64ShrS, + I64ShrU, + I64Rotl, + I64Rotr, // Numeric operators - f32 - F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt, - F32Add, F32Sub, F32Mul, F32Div, F32Min, F32Max, F32Copysign, + F32Abs, + F32Neg, + F32Ceil, + F32Floor, + F32Trunc, + F32Nearest, + F32Sqrt, + F32Add, + F32Sub, + F32Mul, + F32Div, + F32Min, + F32Max, + F32Copysign, // Numeric operators - f64 - F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, F64Nearest, F64Sqrt, - F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign, + F64Abs, + F64Neg, + F64Ceil, + F64Floor, + F64Trunc, + F64Nearest, + F64Sqrt, + F64Add, + F64Sub, + F64Mul, + F64Div, + F64Min, + F64Max, + F64Copysign, // Conversions I32WrapI64, - I32TruncF32S, I32TruncF32U, I32TruncF64S, I32TruncF64U, - I64ExtendI32S, I64ExtendI32U, - I64TruncF32S, I64TruncF32U, I64TruncF64S, I64TruncF64U, - F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64, - F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32, - I32ReinterpretF32, I64ReinterpretF64, F32ReinterpretI32, F64ReinterpretI64, + I32TruncF32S, + I32TruncF32U, + I32TruncF64S, + I32TruncF64U, + I64ExtendI32S, + I64ExtendI32U, + I64TruncF32S, + I64TruncF32U, + I64TruncF64S, + I64TruncF64U, + F32ConvertI32S, + F32ConvertI32U, + F32ConvertI64S, + F32ConvertI64U, + F32DemoteF64, + F64ConvertI32S, + F64ConvertI32U, + F64ConvertI64S, + F64ConvertI64U, + F64PromoteF32, + I32ReinterpretF32, + I64ReinterpretF64, + F32ReinterpretI32, + F64ReinterpretI64, // Sign extension - I32Extend8S, I32Extend16S, - I64Extend8S, I64Extend16S, I64Extend32S, + I32Extend8S, + I32Extend16S, + I64Extend8S, + I64Extend16S, + I64Extend32S, // Saturating truncation - I32TruncSatF32S, I32TruncSatF32U, I32TruncSatF64S, I32TruncSatF64U, - I64TruncSatF32S, I64TruncSatF32U, I64TruncSatF64S, I64TruncSatF64U, + I32TruncSatF32S, + I32TruncSatF32U, + I32TruncSatF64S, + I32TruncSatF64U, + I64TruncSatF32S, + I64TruncSatF32U, + I64TruncSatF64S, + I64TruncSatF64U, // Reference types RefNull(RefTypeKind), @@ -292,7 +405,10 @@ pub struct DataSegment { #[derive(Debug, Clone)] pub enum DataSegmentKind { /// Active segment with memory index and offset expression. - Active { memory_index: u32, offset_expr: ConstExpr }, + Active { + memory_index: u32, + offset_expr: ConstExpr, + }, /// Passive segment. Passive, } @@ -391,7 +507,11 @@ impl ParsedModule { Ok(()) } - fn parse_types(&mut self, name: &str, reader: TypeSectionReader<'_>) -> Result<(), ComposeError> { + fn parse_types( + &mut self, + name: &str, + reader: TypeSectionReader<'_>, + ) -> Result<(), ComposeError> { for rec_group in reader { let rec_group = rec_group.map_err(|e| parse_error(name, e))?; for ty in rec_group.into_types() { @@ -566,7 +686,9 @@ impl ParsedModule { // Parse operators let mut operators = Vec::new(); - let ops_reader = body.get_operators_reader().map_err(|e| parse_error(name, e))?; + let ops_reader = body + .get_operators_reader() + .map_err(|e| parse_error(name, e))?; for op in ops_reader { let op = op.map_err(|e| parse_error(name, e))?; if let Some(stored) = convert_operator(op) { @@ -578,7 +700,11 @@ impl ParsedModule { Ok(()) } - fn parse_data(&mut self, name: &str, reader: DataSectionReader<'_>) -> Result<(), ComposeError> { + fn parse_data( + &mut self, + name: &str, + reader: DataSectionReader<'_>, + ) -> Result<(), ComposeError> { for data in reader { let data = data.map_err(|e| parse_error(name, e))?; let kind = match data.kind { @@ -606,12 +732,16 @@ impl ParsedModule { /// Get all function exports. pub fn function_exports(&self) -> impl Iterator { - self.exports.iter().filter(|e| e.kind == ExportKind::Function) + self.exports + .iter() + .filter(|e| e.kind == ExportKind::Function) } /// Get all function imports. pub fn function_imports(&self) -> impl Iterator { - self.imports.iter().filter(|i| matches!(i.kind, ImportKind::Function(_))) + self.imports + .iter() + .filter(|i| matches!(i.kind, ImportKind::Function(_))) } /// Get the type of a function by its index in the function index space. @@ -648,22 +778,29 @@ impl ParsedModule { } /// Parse a const expression. -fn parse_const_expr(name: &str, expr: wasmparser::ConstExpr<'_>) -> Result { +fn parse_const_expr( + name: &str, + expr: wasmparser::ConstExpr<'_>, +) -> Result { let reader = expr.get_operators_reader(); for op_result in reader { let op = op_result.map_err(|e| parse_error(name, e))?; match op { Operator::I32Const { value } => return Ok(ConstExpr::I32Const(value)), Operator::I64Const { value } => return Ok(ConstExpr::I64Const(value)), - Operator::F32Const { value } => return Ok(ConstExpr::F32Const(f32::from_bits(value.bits()))), - Operator::F64Const { value } => return Ok(ConstExpr::F64Const(f64::from_bits(value.bits()))), + Operator::F32Const { value } => { + return Ok(ConstExpr::F32Const(f32::from_bits(value.bits()))) + } + Operator::F64Const { value } => { + return Ok(ConstExpr::F64Const(f64::from_bits(value.bits()))) + } Operator::GlobalGet { global_index } => return Ok(ConstExpr::GlobalGet(global_index)), Operator::RefNull { hty } => { let kind = match hty { - wasmparser::HeapType::Abstract { ty, .. } => match ty { - wasmparser::AbstractHeapType::Extern => RefTypeKind::Extern, - _ => RefTypeKind::Func, - }, + wasmparser::HeapType::Abstract { + ty: wasmparser::AbstractHeapType::Extern, + .. + } => RefTypeKind::Extern, _ => RefTypeKind::Func, }; return Ok(ConstExpr::RefNull(kind)); @@ -696,13 +833,22 @@ fn convert_operator(op: Operator<'_>) -> Option { }, Operator::Return => StoredOperator::Return, Operator::Call { function_index } => StoredOperator::Call(function_index), - Operator::CallIndirect { type_index, table_index, .. } => { - StoredOperator::CallIndirect { type_index, table_index } - } + Operator::CallIndirect { + type_index, + table_index, + .. + } => StoredOperator::CallIndirect { + type_index, + table_index, + }, Operator::ReturnCall { function_index } => StoredOperator::ReturnCall(function_index), - Operator::ReturnCallIndirect { type_index, table_index } => { - StoredOperator::ReturnCallIndirect { type_index, table_index } - } + Operator::ReturnCallIndirect { + type_index, + table_index, + } => StoredOperator::ReturnCallIndirect { + type_index, + table_index, + }, // Parametric Operator::Drop => StoredOperator::Drop, @@ -722,12 +868,17 @@ fn convert_operator(op: Operator<'_>) -> Option { Operator::TableGrow { table } => StoredOperator::TableGrow(table), Operator::TableSize { table } => StoredOperator::TableSize(table), Operator::TableFill { table } => StoredOperator::TableFill(table), - Operator::TableCopy { dst_table, src_table } => { - StoredOperator::TableCopy { dst: dst_table, src: src_table } - } - Operator::TableInit { elem_index, table } => { - StoredOperator::TableInit { elem: elem_index, table } - } + Operator::TableCopy { + dst_table, + src_table, + } => StoredOperator::TableCopy { + dst: dst_table, + src: src_table, + }, + Operator::TableInit { elem_index, table } => StoredOperator::TableInit { + elem: elem_index, + table, + }, Operator::ElemDrop { elem_index } => StoredOperator::ElemDrop(elem_index), // Memory load operations @@ -760,13 +911,15 @@ fn convert_operator(op: Operator<'_>) -> Option { // Memory operations Operator::MemorySize { mem, .. } => StoredOperator::MemorySize(mem), Operator::MemoryGrow { mem, .. } => StoredOperator::MemoryGrow(mem), - Operator::MemoryInit { data_index, mem } => { - StoredOperator::MemoryInit { data: data_index, mem } - } + Operator::MemoryInit { data_index, mem } => StoredOperator::MemoryInit { + data: data_index, + mem, + }, Operator::DataDrop { data_index } => StoredOperator::DataDrop(data_index), - Operator::MemoryCopy { dst_mem, src_mem } => { - StoredOperator::MemoryCopy { dst: dst_mem, src: src_mem } - } + Operator::MemoryCopy { dst_mem, src_mem } => StoredOperator::MemoryCopy { + dst: dst_mem, + src: src_mem, + }, Operator::MemoryFill { mem } => StoredOperator::MemoryFill(mem), // Constants @@ -933,10 +1086,10 @@ fn convert_operator(op: Operator<'_>) -> Option { // Reference types Operator::RefNull { hty } => { let kind = match hty { - wasmparser::HeapType::Abstract { ty, .. } => match ty { - wasmparser::AbstractHeapType::Extern => RefTypeKind::Extern, - _ => RefTypeKind::Func, - }, + wasmparser::HeapType::Abstract { + ty: wasmparser::AbstractHeapType::Extern, + .. + } => RefTypeKind::Extern, _ => RefTypeKind::Func, }; StoredOperator::RefNull(kind) diff --git a/src/interface_impl.rs b/src/interface_impl.rs index 8be79ab..d5c35ab 100644 --- a/src/interface_impl.rs +++ b/src/interface_impl.rs @@ -352,7 +352,10 @@ impl InterfaceImpl { /// /// Useful for per-function verification. pub fn function_hash(&self, name: &str) -> Option { - self.functions.iter().find(|f| f.name == name).map(|f| f.hash()) + self.functions + .iter() + .find(|f| f.name == name) + .map(|f| f.hash()) } /// Get the interface name. @@ -451,7 +454,12 @@ impl PackParams for (A, B, C) { impl PackParams for (A, B, C, D) { fn pack_types() -> Vec { - vec![A::pack_type(), B::pack_type(), C::pack_type(), D::pack_type()] + vec![ + A::pack_type(), + B::pack_type(), + C::pack_type(), + D::pack_type(), + ] } } @@ -469,14 +477,16 @@ impl HostFunc<(), Ret> for F where F: Fn() -> Ret, Ret: PackType, -{} +{ +} impl HostFunc<(A,), Ret> for F where F: Fn(A) -> Ret, A: PackType, Ret: PackType, -{} +{ +} impl HostFunc<(A, B), Ret> for F where @@ -484,7 +494,8 @@ where A: PackType, B: PackType, Ret: PackType, -{} +{ +} impl HostFunc<(A, B, C), Ret> for F where @@ -493,7 +504,8 @@ where B: PackType, C: PackType, Ret: PackType, -{} +{ +} impl HostFunc<(A, B, C, D), Ret> for F where @@ -503,7 +515,8 @@ where C: PackType, D: PackType, Ret: PackType, -{} +{ +} #[cfg(test)] mod tests { @@ -534,9 +547,7 @@ mod tests { .func("greet", |name: String| -> String { format!("Hello, {}!", name) }) - .func("add", |a: i32, b: i32| -> i32 { - a + b - }); + .func("add", |a: i32, b: i32| -> i32 { a + b }); assert_eq!(interface.name(), "test:example/api"); assert_eq!(interface.functions.len(), 2); @@ -561,7 +572,7 @@ mod tests { .func("bar", |s: String| -> String { s }); let interface2 = InterfaceImpl::new("test:api") - .func("bar", |s: String| -> String { s }) // Different order + .func("bar", |s: String| -> String { s }) // Different order .func("foo", |x: i32| -> i32 { x }); // Same interface, different declaration order -> same hash @@ -570,11 +581,9 @@ mod tests { #[test] fn test_interface_hash_differs_on_signature() { - let interface1 = InterfaceImpl::new("test:api") - .func("foo", |x: i32| -> i32 { x }); + let interface1 = InterfaceImpl::new("test:api").func("foo", |x: i32| -> i32 { x }); - let interface2 = InterfaceImpl::new("test:api") - .func("foo", |x: i64| -> i64 { x }); // Different type! + let interface2 = InterfaceImpl::new("test:api").func("foo", |x: i64| -> i64 { x }); // Different type! assert_ne!(interface1.hash(), interface2.hash()); } @@ -602,19 +611,31 @@ mod tests { assert_eq!(interface.functions.len(), 3); // Check function names - let names: Vec<&str> = interface.functions.iter().map(|f| f.name.as_str()).collect(); + let names: Vec<&str> = interface + .functions + .iter() + .map(|f| f.name.as_str()) + .collect(); assert!(names.contains(&"log")); assert!(names.contains(&"get-chain")); assert!(names.contains(&"shutdown")); // Check log function signature - let log_fn = interface.functions.iter().find(|f| f.name == "log").unwrap(); + let log_fn = interface + .functions + .iter() + .find(|f| f.name == "log") + .unwrap(); assert_eq!(log_fn.params.len(), 1); assert!(matches!(log_fn.params[0], Type::String)); assert!(log_fn.results.is_empty()); // Check get-chain function signature - let get_chain_fn = interface.functions.iter().find(|f| f.name == "get-chain").unwrap(); + let get_chain_fn = interface + .functions + .iter() + .find(|f| f.name == "get-chain") + .unwrap(); assert!(get_chain_fn.params.is_empty()); assert_eq!(get_chain_fn.results.len(), 1); assert!(matches!(get_chain_fn.results[0], Type::List(_))); diff --git a/src/lib.rs b/src/lib.rs index 9c2457a..279afce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,19 +60,21 @@ pub mod transform; pub mod types; pub use abi::{decode, encode}; -pub use interface_impl::{ - FuncSignature, HostFunc, InterfaceImpl, PackParams, PackType, -}; +pub use interface_impl::{FuncSignature, HostFunc, InterfaceImpl, PackParams, PackType}; pub use metadata::{ - decode_metadata, decode_metadata_with_hashes, encode_metadata, encode_metadata_with_hashes, - compute_interface_hash, compute_interface_hashes, hash_type, hash_function_from_sig, - hash_function, hash_interface, hash_list, hash_option, hash_record, - hash_result, hash_tuple, hash_variant, Binding, - CaseDesc, FieldDesc, FunctionSignature, InterfaceHash, MetadataError, - MetadataWithHashes, PackageMetadata, ParamSignature, TypeDesc, TypeHash, - HASH_BOOL, HASH_CHAR, HASH_F32, HASH_F64, HASH_FLAGS, HASH_S16, HASH_S32, - HASH_S64, HASH_S8, HASH_STRING, HASH_U16, HASH_U32, HASH_U64, HASH_U8, - validate_value_in_type_space, TypeValidationError, + compute_interface_hash, compute_interface_hashes, decode_metadata, decode_metadata_with_hashes, + encode_metadata, encode_metadata_with_hashes, hash_function, hash_function_from_sig, + hash_interface, hash_list, hash_option, hash_record, hash_result, hash_tuple, hash_type, + hash_variant, validate_value_in_type_space, Binding, CaseDesc, FieldDesc, FunctionSignature, + InterfaceHash, MetadataError, MetadataWithHashes, PackageMetadata, ParamSignature, TypeDesc, + TypeHash, TypeValidationError, HASH_BOOL, HASH_CHAR, HASH_F32, HASH_F64, HASH_FLAGS, HASH_S16, + HASH_S32, HASH_S64, HASH_S8, HASH_STRING, HASH_U16, HASH_U32, HASH_U64, HASH_U8, +}; +pub use parser::{ + parse_pact, parse_pact_dir, parse_pact_dir_with_registry, parse_pact_file, Interface, + InterfaceAlias, InterfacePath, InterfaceTypes, Metadata, MetadataValue, PactExport, + PactFileError, PactImport, PactInterface, PactUse, ResolvedScope, ResolvedUse, TypeDef, + TypeParam, TypeRegistry, World, WorldItem, }; pub use runtime::{ validate_instance_implements_interface, AsyncCompiledModule, AsyncCtx, AsyncInstance, @@ -80,12 +82,6 @@ pub use runtime::{ HostFunctionError, HostFunctionErrorKind, HostFunctionProvider, HostLinkerBuilder, Instance, InterfaceBuilder, InterfaceError, LinkerError, Runtime, }; -pub use parser::{ - parse_pact, parse_pact_dir, parse_pact_dir_with_registry, parse_pact_file, - Interface, InterfaceAlias, InterfacePath, InterfaceTypes, Metadata, MetadataValue, PactExport, - PactFileError, PactImport, PactInterface, PactUse, ResolvedScope, ResolvedUse, TypeDef, - TypeParam, TypeRegistry, World, WorldItem, -}; pub use transform::{InterfaceTransform, RpcTransform, TransformRegistry}; pub use types::{Arena, Case, Field, Function, Param, Type, TypePath}; diff --git a/src/main.rs b/src/main.rs index 905cd6b..1a46275 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,16 +74,14 @@ fn inspect_command(wasm_file: &PathBuf, show_hashes: bool, json: bool) -> anyhow } else { print_json(&metadata.arena)?; } + } else if show_hashes { + print_metadata_with_hashes( + &metadata.arena, + &metadata.import_hashes, + &metadata.export_hashes, + ); } else { - if show_hashes { - print_metadata_with_hashes( - &metadata.arena, - &metadata.import_hashes, - &metadata.export_hashes, - ); - } else { - print_metadata(&metadata.arena); - } + print_metadata(&metadata.arena); } Ok(()) @@ -156,10 +154,7 @@ fn print_functions(functions: &[Function], indent: &str) { std::collections::BTreeMap::new(); for func in functions { - by_interface - .entry(&func.interface) - .or_default() - .push(func); + by_interface.entry(&func.interface).or_default().push(func); } for (interface, funcs) in by_interface { @@ -190,7 +185,11 @@ fn format_results(results: &[Type]) -> String { } else { format!( "({})", - results.iter().map(format_type).collect::>().join(", ") + results + .iter() + .map(format_type) + .collect::>() + .join(", ") ) } } diff --git a/src/metadata.rs b/src/metadata.rs index ea6430a..94102b4 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -64,7 +64,11 @@ impl TypeHash { /// Format as short hex (first 8 chars). pub fn to_short_hex(&self) -> String { - self.0.iter().take(4).map(|b| format!("{:02x}", b)).collect() + self.0 + .iter() + .take(4) + .map(|b| format!("{:02x}", b)) + .collect() } /// Const function to create from bytes (for compile-time constants). @@ -81,10 +85,8 @@ impl TypeHash { /// /// This matches the HASH_SELF_REF constant in pack-abi for consistency. pub const HASH_SELF_REF: TypeHash = TypeHash::from_bytes_const([ - 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); impl std::fmt::Display for TypeHash { @@ -99,114 +101,86 @@ impl std::fmt::Display for TypeHash { /// Hash for the `bool` type. pub const HASH_BOOL: TypeHash = TypeHash([ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u8` type. pub const HASH_U8: TypeHash = TypeHash([ - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u16` type. pub const HASH_U16: TypeHash = TypeHash([ - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u32` type. pub const HASH_U32: TypeHash = TypeHash([ - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `u64` type. pub const HASH_U64: TypeHash = TypeHash([ - 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s8` type. pub const HASH_S8: TypeHash = TypeHash([ - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s16` type. pub const HASH_S16: TypeHash = TypeHash([ - 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s32` type. pub const HASH_S32: TypeHash = TypeHash([ - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `s64` type. pub const HASH_S64: TypeHash = TypeHash([ - 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `f32` type. pub const HASH_F32: TypeHash = TypeHash([ - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `f64` type. pub const HASH_F64: TypeHash = TypeHash([ - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `char` type. pub const HASH_CHAR: TypeHash = TypeHash([ - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `string` type. pub const HASH_STRING: TypeHash = TypeHash([ - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); /// Hash for the `flags` type. pub const HASH_FLAGS: TypeHash = TypeHash([ - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); // ============================================================================ @@ -232,7 +206,9 @@ struct TypeHasher { impl TypeHasher { fn new() -> Self { - Self { hasher: Sha256::new() } + Self { + hasher: Sha256::new(), + } } fn tag(mut self, tag: u8) -> Self { @@ -276,7 +252,11 @@ pub fn hash_option(inner: &TypeHash) -> TypeHash { /// Hash a result type. pub fn hash_result(ok: &TypeHash, err: &TypeHash) -> TypeHash { - TypeHasher::new().tag(HASH_TAG_RESULT).child(ok).child(err).finish() + TypeHasher::new() + .tag(HASH_TAG_RESULT) + .child(ok) + .child(err) + .finish() } /// Hash a tuple type. @@ -707,10 +687,22 @@ fn decode_interface_hash(value: Value) -> Result { Value::Tuple(parts) => { // Legacy: tuple of 4 u64s if parts.len() == 4 { - let a = match &parts[0] { Value::U64(v) => *v, _ => 0 }; - let b = match &parts[1] { Value::U64(v) => *v, _ => 0 }; - let c = match &parts[2] { Value::U64(v) => *v, _ => 0 }; - let d = match &parts[3] { Value::U64(v) => *v, _ => 0 }; + let a = match &parts[0] { + Value::U64(v) => *v, + _ => 0, + }; + let b = match &parts[1] { + Value::U64(v) => *v, + _ => 0, + }; + let c = match &parts[2] { + Value::U64(v) => *v, + _ => 0, + }; + let d = match &parts[3] { + Value::U64(v) => *v, + _ => 0, + }; hash = TypeHash::from_u64s(a, b, c, d); } } @@ -731,9 +723,15 @@ fn decode_interface_hash(value: Value) -> Result { /// Decode a list of function signatures. /// Returns (interface_name, Function) pairs. -fn decode_func_sig_list(value: Value, type_defs: &mut Vec) -> Result, MetadataError> { +fn decode_func_sig_list( + value: Value, + type_defs: &mut Vec, +) -> Result, MetadataError> { match value { - Value::List { items, .. } => items.into_iter().map(|v| decode_func_sig(v, type_defs)).collect(), + Value::List { items, .. } => items + .into_iter() + .map(|v| decode_func_sig(v, type_defs)) + .collect(), _ => Err(MetadataError::InvalidStructure( "expected list of function signatures".into(), )), @@ -742,7 +740,10 @@ fn decode_func_sig_list(value: Value, type_defs: &mut Vec) -> Result) -> Result<(String, Function), MetadataError> { +fn decode_func_sig( + value: Value, + type_defs: &mut Vec, +) -> Result<(String, Function), MetadataError> { match value { Value::Record { fields, .. } => { let mut interface = String::new(); @@ -784,9 +785,15 @@ fn decode_func_sig(value: Value, type_defs: &mut Vec) -> Result<(String } } -fn decode_param_list(value: Value, type_defs: &mut Vec) -> Result, MetadataError> { +fn decode_param_list( + value: Value, + type_defs: &mut Vec, +) -> Result, MetadataError> { match value { - Value::List { items, .. } => items.into_iter().map(|v| decode_param(v, type_defs)).collect(), + Value::List { items, .. } => items + .into_iter() + .map(|v| decode_param(v, type_defs)) + .collect(), _ => Err(MetadataError::InvalidStructure( "expected list of parameters".into(), )), @@ -821,21 +828,25 @@ fn decode_param(value: Value, type_defs: &mut Vec) -> Result) -> Result, MetadataError> { +fn decode_type_list( + value: Value, + type_defs: &mut Vec, +) -> Result, MetadataError> { match value { - Value::List { items, .. } => items.into_iter().map(|v| decode_type_collecting(v, type_defs)).collect(), + Value::List { items, .. } => items + .into_iter() + .map(|v| decode_type_collecting(v, type_defs)) + .collect(), _ => Err(MetadataError::InvalidStructure( "expected list of types".into(), )), } } -fn decode_type(value: Value) -> Result { - let mut type_defs = Vec::new(); - decode_type_collecting(value, &mut type_defs) -} - -fn decode_type_collecting(value: Value, type_defs: &mut Vec) -> Result { +fn decode_type_collecting( + value: Value, + type_defs: &mut Vec, +) -> Result { match value { Value::Variant { tag, payload, .. } => { let tag = tag as u32; @@ -906,8 +917,10 @@ fn decode_type_collecting(value: Value, type_defs: &mut Vec) -> Result< })?; match list { Value::List { items, .. } => { - let types: Result, _> = - items.into_iter().map(|v| decode_type_collecting(v, type_defs)).collect(); + let types: Result, _> = items + .into_iter() + .map(|v| decode_type_collecting(v, type_defs)) + .collect(); Ok(Type::tuple(types?)) } _ => Err(MetadataError::InvalidStructure( @@ -932,8 +945,7 @@ fn decode_type_collecting(value: Value, type_defs: &mut Vec) -> Result< fn decode_record_type(value: Value, type_defs: &mut Vec) -> Result { match value { Value::Record { - fields: rec_fields, - .. + fields: rec_fields, .. } => { let mut name = String::new(); let mut decoded_fields = Vec::new(); @@ -948,7 +960,10 @@ fn decode_record_type(value: Value, type_defs: &mut Vec) -> Result { if let Value::List { items, .. } = val { for item in items { - if let Value::Record { fields: field_rec, .. } = item { + if let Value::Record { + fields: field_rec, .. + } = item + { let mut field_name = String::new(); let mut field_type = Type::Value; for (fn_name, fn_val) in field_rec { @@ -959,7 +974,8 @@ fn decode_record_type(value: Value, type_defs: &mut Vec) -> Result { - field_type = decode_type_collecting(fn_val, type_defs)?; + field_type = + decode_type_collecting(fn_val, type_defs)?; } _ => {} } @@ -995,8 +1011,7 @@ fn decode_record_type(value: Value, type_defs: &mut Vec) -> Result) -> Result { match value { Value::Record { - fields: rec_fields, - .. + fields: rec_fields, .. } => { let mut name = String::new(); let mut decoded_cases = Vec::new(); @@ -1011,7 +1026,10 @@ fn decode_variant_type(value: Value, type_defs: &mut Vec) -> Result { if let Value::List { items, .. } = val { for item in items { - if let Value::Record { fields: case_rec, .. } = item { + if let Value::Record { + fields: case_rec, .. + } = item + { let mut case_name = String::new(); let mut case_payload = Type::Unit; for (cn, cv) in case_rec { @@ -1022,8 +1040,15 @@ fn decode_variant_type(value: Value, type_defs: &mut Vec) -> Result { - if let Value::Option { value: Some(payload_val), .. } = cv { - case_payload = decode_type_collecting(*payload_val, type_defs)?; + if let Value::Option { + value: Some(payload_val), + .. + } = cv + { + case_payload = decode_type_collecting( + *payload_val, + type_defs, + )?; } } _ => {} @@ -1039,13 +1064,14 @@ fn decode_variant_type(value: Value, type_defs: &mut Vec) -> Result Value { Value::Record { type_name: "FunctionSignature".to_string(), fields: vec![ - ("interface".to_string(), Value::String(interface.to_string())), + ( + "interface".to_string(), + Value::String(interface.to_string()), + ), ("name".to_string(), Value::String(func.name.clone())), ( "params".to_string(), @@ -1294,14 +1323,20 @@ pub fn encode_metadata_with_hashes(arena: &Arena) -> Result, MetadataErr "import-hashes".to_string(), Value::List { elem_type: crate::abi::ValueType::Record("InterfaceHash".to_string()), - items: import_hashes.iter().map(encode_interface_hash_value).collect(), + items: import_hashes + .iter() + .map(encode_interface_hash_value) + .collect(), }, ), ( "export-hashes".to_string(), Value::List { elem_type: crate::abi::ValueType::Record("InterfaceHash".to_string()), - items: export_hashes.iter().map(encode_interface_hash_value).collect(), + items: export_hashes + .iter() + .map(encode_interface_hash_value) + .collect(), }, ), ], @@ -1347,7 +1382,10 @@ pub enum TypeValidationError { /// Tuple or payload length mismatch. WrongArity { expected: usize, got: usize }, /// Error in a nested position, with context path. - Nested { context: String, inner: Box }, + Nested { + context: String, + inner: Box, + }, } impl std::fmt::Display for TypeValidationError { @@ -1461,11 +1499,12 @@ pub fn validate_value_in_type_space( Type::List(elem_type) => match value { Value::List { items, .. } => { for (i, item) in items.iter().enumerate() { - validate_value_in_type_space(item, elem_type, type_defs) - .map_err(|e| TypeValidationError::Nested { + validate_value_in_type_space(item, elem_type, type_defs).map_err(|e| { + TypeValidationError::Nested { context: format!("list[{}]", i), inner: Box::new(e), - })?; + } + })?; } Ok(()) } @@ -1475,11 +1514,12 @@ pub fn validate_value_in_type_space( Type::Option(inner_type) => match value { Value::Option { value: inner, .. } => { if let Some(v) = inner { - validate_value_in_type_space(v, inner_type, type_defs) - .map_err(|e| TypeValidationError::Nested { + validate_value_in_type_space(v, inner_type, type_defs).map_err(|e| { + TypeValidationError::Nested { context: "option::some".into(), inner: Box::new(e), - })?; + } + })?; } Ok(()) } @@ -1489,16 +1529,18 @@ pub fn validate_value_in_type_space( Type::Result { ok, err } => match value { Value::Result { value: result, .. } => { match result { - Ok(v) => validate_value_in_type_space(v, ok, type_defs) - .map_err(|e| TypeValidationError::Nested { + Ok(v) => validate_value_in_type_space(v, ok, type_defs).map_err(|e| { + TypeValidationError::Nested { context: "result::ok".into(), inner: Box::new(e), - })?, - Err(v) => validate_value_in_type_space(v, err, type_defs) - .map_err(|e| TypeValidationError::Nested { + } + })?, + Err(v) => validate_value_in_type_space(v, err, type_defs).map_err(|e| { + TypeValidationError::Nested { context: "result::err".into(), inner: Box::new(e), - })?, + } + })?, } Ok(()) } @@ -1514,11 +1556,12 @@ pub fn validate_value_in_type_space( }); } for (i, (ty, val)) in types.iter().zip(items.iter()).enumerate() { - validate_value_in_type_space(val, ty, type_defs) - .map_err(|e| TypeValidationError::Nested { + validate_value_in_type_space(val, ty, type_defs).map_err(|e| { + TypeValidationError::Nested { context: format!("tuple.{}", i), inner: Box::new(e), - })?; + } + })?; } Ok(()) } @@ -1531,9 +1574,15 @@ pub fn validate_value_in_type_space( let type_def = type_defs.iter().find(|td| td.name() == name); match type_def { - Some(TypeDef::Record { name, fields }) => validate_record(value, name, fields, type_defs), - Some(TypeDef::Variant { name, cases }) => validate_variant(value, name, cases, type_defs), - Some(TypeDef::Alias { ty, .. }) => validate_value_in_type_space(value, ty, type_defs), + Some(TypeDef::Record { name, fields }) => { + validate_record(value, name, fields, type_defs) + } + Some(TypeDef::Variant { name, cases }) => { + validate_variant(value, name, cases, type_defs) + } + Some(TypeDef::Alias { ty, .. }) => { + validate_value_in_type_space(value, ty, type_defs) + } Some(TypeDef::Enum { name, cases }) => validate_enum(value, name, cases), Some(TypeDef::Flags { .. }) => match value { Value::Flags(_) => Ok(()), @@ -1554,7 +1603,9 @@ fn validate_record( type_defs: &[TypeDef], ) -> Result<(), TypeValidationError> { match value { - Value::Record { fields: val_fields, .. } => { + Value::Record { + fields: val_fields, .. + } => { // Check for missing fields for expected in expected_fields { if !val_fields.iter().any(|(name, _)| name == &expected.name) { @@ -1576,11 +1627,12 @@ fn validate_record( // Validate each field's value for (name, val) in val_fields { if let Some(field_def) = expected_fields.iter().find(|f| &f.name == name) { - validate_value_in_type_space(val, &field_def.ty, type_defs) - .map_err(|e| TypeValidationError::Nested { + validate_value_in_type_space(val, &field_def.ty, type_defs).map_err(|e| { + TypeValidationError::Nested { context: format!("field '{}'", name), inner: Box::new(e), - })?; + } + })?; } } Ok(()) @@ -1599,7 +1651,9 @@ fn validate_variant( type_defs: &[TypeDef], ) -> Result<(), TypeValidationError> { match value { - Value::Variant { case_name, payload, .. } => { + Value::Variant { + case_name, payload, .. + } => { let case_def = expected_cases.iter().find(|c| &c.name == case_name); match case_def { Some(case) => { @@ -1613,13 +1667,11 @@ fn validate_variant( got: n, }), }), - (ty, 1) => { - validate_value_in_type_space(&payload[0], ty, type_defs) - .map_err(|e| TypeValidationError::Nested { - context: format!("case '{}'", case_name), - inner: Box::new(e), - }) - } + (ty, 1) => validate_value_in_type_space(&payload[0], ty, type_defs) + .map_err(|e| TypeValidationError::Nested { + context: format!("case '{}'", case_name), + inner: Box::new(e), + }), (_, n) => Err(TypeValidationError::Nested { context: format!("case '{}'", case_name), inner: Box::new(TypeValidationError::WrongArity { @@ -1648,7 +1700,9 @@ fn validate_enum( expected_cases: &[String], ) -> Result<(), TypeValidationError> { match value { - Value::Variant { case_name, payload, .. } => { + Value::Variant { + case_name, payload, .. + } => { if !expected_cases.iter().any(|c| c == case_name) { return Err(TypeValidationError::UnknownCase { variant: enum_name.into(), @@ -1699,7 +1753,11 @@ fn value_type_name(value: &Value) -> String { Value::Option { .. } => "option".into(), Value::Result { .. } => "result".into(), Value::Record { type_name, .. } => format!("record '{}'", type_name), - Value::Variant { type_name, case_name, .. } => format!("variant '{}' (case '{}')", type_name, case_name), + Value::Variant { + type_name, + case_name, + .. + } => format!("variant '{}' (case '{}')", type_name, case_name), Value::Tuple(items) => format!("tuple<{}>", items.len()), Value::Flags(_) => "flags".into(), } @@ -1842,8 +1900,16 @@ mod tests { assert_eq!(decoded.export_hashes[0].name, "theater:simple/actor"); // Verify hashes are non-zero - assert!(!decoded.import_hashes[0].hash.as_bytes().iter().all(|&b| b == 0)); - assert!(!decoded.export_hashes[0].hash.as_bytes().iter().all(|&b| b == 0)); + assert!(!decoded.import_hashes[0] + .hash + .as_bytes() + .iter() + .all(|&b| b == 0)); + assert!(!decoded.export_hashes[0] + .hash + .as_bytes() + .iter() + .all(|&b| b == 0)); } // ======================================================================== @@ -1855,7 +1921,9 @@ mod tests { let defs = vec![]; assert!(validate_value_in_type_space(&Value::Bool(true), &Type::Bool, &defs).is_ok()); assert!(validate_value_in_type_space(&Value::U32(42), &Type::U32, &defs).is_ok()); - assert!(validate_value_in_type_space(&Value::String("hi".into()), &Type::String, &defs).is_ok()); + assert!( + validate_value_in_type_space(&Value::String("hi".into()), &Type::String, &defs).is_ok() + ); // Mismatch assert!(validate_value_in_type_space(&Value::Bool(true), &Type::U32, &defs).is_err()); @@ -1874,8 +1942,15 @@ mod tests { let defs = vec![]; // Type::Value accepts anything assert!(validate_value_in_type_space(&Value::Bool(true), &Type::Value, &defs).is_ok()); - assert!(validate_value_in_type_space(&Value::String("x".into()), &Type::Value, &defs).is_ok()); - assert!(validate_value_in_type_space(&Value::Tuple(vec![Value::U8(1)]), &Type::Value, &defs).is_ok()); + assert!( + validate_value_in_type_space(&Value::String("x".into()), &Type::Value, &defs).is_ok() + ); + assert!(validate_value_in_type_space( + &Value::Tuple(vec![Value::U8(1)]), + &Type::Value, + &defs + ) + .is_ok()); } #[test] @@ -1896,15 +1971,13 @@ mod tests { #[test] fn test_validate_record() { - let defs = vec![ - TypeDef::Record { - name: "my-state".into(), - fields: vec![ - Field::new("count", Type::S32), - Field::new("name", Type::String), - ], - }, - ]; + let defs = vec![TypeDef::Record { + name: "my-state".into(), + fields: vec![ + Field::new("count", Type::S32), + Field::new("name", Type::String), + ], + }]; let ty = Type::Ref(TypePath::simple("my-state")); // Valid @@ -1948,15 +2021,13 @@ mod tests { #[test] fn test_validate_variant() { - let defs = vec![ - TypeDef::Variant { - name: "result".into(), - cases: vec![ - Case::new("ok", Type::String), - Case::new("err", Type::String), - ], - }, - ]; + let defs = vec![TypeDef::Variant { + name: "result".into(), + cases: vec![ + Case::new("ok", Type::String), + Case::new("err", Type::String), + ], + }]; let ty = Type::Ref(TypePath::simple("result")); // Valid case @@ -1989,12 +2060,10 @@ mod tests { #[test] fn test_validate_enum() { - let defs = vec![ - TypeDef::Enum { - name: "color".into(), - cases: vec!["red".into(), "green".into(), "blue".into()], - }, - ]; + let defs = vec![TypeDef::Enum { + name: "color".into(), + cases: vec!["red".into(), "green".into(), "blue".into()], + }]; let ty = Type::Ref(TypePath::simple("color")); let val = Value::Variant { @@ -2027,10 +2096,7 @@ mod tests { }, TypeDef::Variant { name: "status".into(), - cases: vec![ - Case::unit("pending"), - Case::new("active", Type::String), - ], + cases: vec![Case::unit("pending"), Case::new("active", Type::String)], }, ]; let ty = Type::Ref(TypePath::simple("state")); @@ -2039,12 +2105,15 @@ mod tests { let val = Value::Record { type_name: "state".into(), fields: vec![ - ("status".into(), Value::Variant { - type_name: "status".into(), - case_name: "active".into(), - tag: 1, - payload: vec![Value::String("running".into())], - }), + ( + "status".into(), + Value::Variant { + type_name: "status".into(), + case_name: "active".into(), + tag: 1, + payload: vec![Value::String("running".into())], + }, + ), ("count".into(), Value::U32(10)), ], }; @@ -2054,12 +2123,15 @@ mod tests { let val_bad = Value::Record { type_name: "state".into(), fields: vec![ - ("status".into(), Value::Variant { - type_name: "status".into(), - case_name: "active".into(), - tag: 1, - payload: vec![Value::U32(42)], // should be String - }), + ( + "status".into(), + Value::Variant { + type_name: "status".into(), + case_name: "active".into(), + tag: 1, + payload: vec![Value::U32(42)], // should be String + }, + ), ("count".into(), Value::U32(10)), ], }; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f38480a..843facf 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4,28 +4,26 @@ //! The module focuses on parsing; types are defined in `crate::types`. mod pact; -mod wit; mod validation; +mod wit; pub use pact::{ - parse_pact, parse_pact_dir, parse_pact_dir_with_registry, parse_pact_file, - InterfaceAlias, InterfaceTypes, Metadata, MetadataValue, PactExport, PactFileError, - PactImport, PactInterface, PactUse, ResolvedScope, ResolvedUse, TypeParam, TypeRegistry, + parse_pact, parse_pact_dir, parse_pact_dir_with_registry, parse_pact_file, InterfaceAlias, + InterfaceTypes, Metadata, MetadataValue, PactExport, PactFileError, PactImport, PactInterface, + PactUse, ResolvedScope, ResolvedUse, TypeParam, TypeRegistry, }; // WIT+ parser is deprecated - use parse_pact() instead. // Kept for internal tests only. -#[doc(hidden)] -#[allow(deprecated)] -pub use wit::{parse_interface, parse_world}; pub use validation::{ decode_with_schema, encode_with_schema, validate_graph_against_type, ValidationError, }; +#[doc(hidden)] +#[allow(deprecated)] +pub use wit::{parse_interface, parse_world}; // Re-export types from crate::types for convenience -pub use crate::types::{ - Arena, Case, Field, Function, Param, Type, TypeDef, TypePath, -}; +pub use crate::types::{Arena, Case, Field, Function, Param, Type, TypeDef, TypePath}; use std::collections::HashSet; @@ -124,7 +122,9 @@ impl World { } WorldItem::Function(func) => { // Standalone functions go in a "standalone" interface - let standalone = import_arena.children.iter_mut() + let standalone = import_arena + .children + .iter_mut() .find(|c| c.name == "standalone"); if let Some(child) = standalone { child.add_function(func.clone()); @@ -151,7 +151,9 @@ impl World { export_arena.add_child(child); } WorldItem::Function(func) => { - let standalone = export_arena.children.iter_mut() + let standalone = export_arena + .children + .iter_mut() .find(|c| c.name == "standalone"); if let Some(child) = standalone { child.add_function(func.clone()); @@ -422,9 +424,7 @@ fn validate_type_ref( allow_self_ref: bool, ) -> Result<(), ParseError> { match ty { - Type::List(inner) | Type::Option(inner) => { - validate_type_ref(inner, names, allow_self_ref) - } + Type::List(inner) | Type::Option(inner) => validate_type_ref(inner, names, allow_self_ref), Type::Result { ok, err } => { validate_type_ref(ok, names, allow_self_ref)?; validate_type_ref(err, names, allow_self_ref)?; diff --git a/src/parser/pact.rs b/src/parser/pact.rs index 035ff6c..1af52b7 100644 --- a/src/parser/pact.rs +++ b/src/parser/pact.rs @@ -259,7 +259,7 @@ fn tokenize(src: &str) -> Result, ParseError> { chars.next(); if matches!(chars.peek(), Some('/')) { // Line comment - while let Some(next) = chars.next() { + for next in chars.by_ref() { if next == '\n' { break; } @@ -310,7 +310,9 @@ fn tokenize(src: &str) -> Result, ParseError> { } // Numbers - if ch.is_ascii_digit() || (ch == '-' && matches!(chars.clone().nth(1), Some(c) if c.is_ascii_digit())) { + if ch.is_ascii_digit() + || (ch == '-' && matches!(chars.clone().nth(1), Some(c) if c.is_ascii_digit())) + { let mut num = String::new(); if ch == '-' { num.push(ch); @@ -435,11 +437,10 @@ impl Parser { } fn accept_ident(&mut self, expected: &str) -> bool { - matches!(self.peek(), Token::Ident(name) if name == expected) - && { - self.pos += 1; - true - } + matches!(self.peek(), Token::Ident(name) if name == expected) && { + self.pos += 1; + true + } } fn accept_at(&mut self) -> bool { @@ -503,11 +504,11 @@ pub fn parse_pact(src: &str) -> Result { /// The interface name will be derived from the filename (without .pact extension). pub fn parse_pact_file(path: impl AsRef) -> Result { let path = path.as_ref(); - let src = std::fs::read_to_string(path) - .map_err(|e| PactFileError::Io(path.to_path_buf(), e))?; + let src = + std::fs::read_to_string(path).map_err(|e| PactFileError::Io(path.to_path_buf(), e))?; - let mut interface = parse_pact(&src) - .map_err(|e| PactFileError::Parse(path.to_path_buf(), e))?; + let mut interface = + parse_pact(&src).map_err(|e| PactFileError::Parse(path.to_path_buf(), e))?; // If the interface was parsed as "root", use the filename as the name if interface.name == "root" { @@ -530,10 +531,7 @@ pub fn parse_pact_dir(path: impl AsRef) -> Result) -> Result Result<(), PactFileError> { - let entries = std::fs::read_dir(dir) - .map_err(|e| PactFileError::Io(dir.to_path_buf(), e))?; +fn parse_pact_dir_recursive(dir: &Path, parent: &mut PactInterface) -> Result<(), PactFileError> { + let entries = std::fs::read_dir(dir).map_err(|e| PactFileError::Io(dir.to_path_buf(), e))?; for entry in entries { let entry = entry.map_err(|e| PactFileError::Io(dir.to_path_buf(), e))?; @@ -563,8 +557,11 @@ fn parse_pact_dir_recursive( let mut child = PactInterface::new(subdir_name); parse_pact_dir_recursive(&path, &mut child)?; - if !child.children.is_empty() || !child.types.is_empty() - || !child.exports.is_empty() || !child.imports.is_empty() { + if !child.children.is_empty() + || !child.types.is_empty() + || !child.exports.is_empty() + || !child.imports.is_empty() + { parent.children.push(child); } } else if path.extension().and_then(|s| s.to_str()) == Some("pact") { @@ -652,12 +649,16 @@ impl TypeRegistry { /// Add an interface and all its children to the registry. pub fn add_interface(&mut self, interface: &PactInterface) { - let mut iface_types = InterfaceTypes::default(); - iface_types.interface = Some(interface.clone()); + let mut iface_types = InterfaceTypes { + interface: Some(interface.clone()), + ..Default::default() + }; // Add all types from this interface for typedef in &interface.types { - iface_types.types.insert(typedef.name().to_string(), typedef.clone()); + iface_types + .types + .insert(typedef.name().to_string(), typedef.clone()); } self.interfaces.insert(interface.name.clone(), iface_types); @@ -699,20 +700,28 @@ impl TypeRegistry { /// If `use_decl.items` is empty, returns all types from the interface. /// Otherwise, returns only the specified types. pub fn resolve_use(&self, use_decl: &PactUse) -> Result, String> { - let iface = self.interfaces + let iface = self + .interfaces .get(&use_decl.interface) .ok_or_else(|| format!("Unknown interface: {}", use_decl.interface))?; if use_decl.items.is_empty() { // Import all types - Ok(iface.types.iter().map(|(k, v)| (k.clone(), v.clone())).collect()) + Ok(iface + .types + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect()) } else { // Import specific types let mut result = Vec::new(); for item in &use_decl.items { - let typedef = iface.types - .get(item) - .ok_or_else(|| format!("Type {} not found in interface {}", item, use_decl.interface))?; + let typedef = iface.types.get(item).ok_or_else(|| { + format!( + "Type {} not found in interface {}", + item, use_decl.interface + ) + })?; result.push((item.clone(), typedef.clone())); } Ok(result) @@ -721,7 +730,10 @@ impl TypeRegistry { /// Create a resolved scope for an interface, including its own types /// and all types brought in via `use` statements. - pub fn resolve_scope(&self, interface: &PactInterface) -> Result, String> { + pub fn resolve_scope( + &self, + interface: &PactInterface, + ) -> Result, String> { let mut scope = HashMap::new(); // Add the interface's own types @@ -758,13 +770,17 @@ impl TypeRegistry { // Transform use declaration: use rpc(calculator) // interface field is the transform name, transform_args[0] is the base interface let transform_name = &use_decl.interface; - let base_interface_name = use_decl.transform_args.first() + let base_interface_name = use_decl + .transform_args + .first() .ok_or_else(|| "Transform requires at least one argument".to_string())?; - let transform = transforms.get(transform_name) + let transform = transforms + .get(transform_name) .ok_or_else(|| format!("Unknown transform: {}", transform_name))?; - let base_interface = self.get_interface(base_interface_name) + let base_interface = self + .get_interface(base_interface_name) .ok_or_else(|| format!("Unknown interface: {}", base_interface_name))?; let transformed = transform.transform(base_interface); @@ -776,9 +792,13 @@ impl TypeRegistry { // Return specific types from the transformed interface let mut types = Vec::new(); for item in &use_decl.items { - let typedef = transformed.types.iter() + let typedef = transformed + .types + .iter() .find(|t| t.name() == item) - .ok_or_else(|| format!("Type {} not found in transformed interface", item))?; + .ok_or_else(|| { + format!("Type {} not found in transformed interface", item) + })?; types.push((item.clone(), typedef.clone())); } Ok(ResolvedUse::Types(types)) @@ -798,7 +818,9 @@ impl TypeRegistry { // Add the interface's own types for typedef in &interface.types { - scope.types.insert(typedef.name().to_string(), typedef.clone()); + scope + .types + .insert(typedef.name().to_string(), typedef.clone()); } // Resolve each `use` and add those types/interfaces @@ -812,7 +834,9 @@ impl TypeRegistry { ResolvedUse::TransformedInterface(iface) => { // Add all types from the transformed interface for typedef in &iface.types { - scope.types.insert(typedef.name().to_string(), typedef.clone()); + scope + .types + .insert(typedef.name().to_string(), typedef.clone()); } // Store the transformed interface for function resolution scope.transformed_interfaces.push(iface); @@ -822,17 +846,22 @@ impl TypeRegistry { // Resolve interface aliases for alias in &interface.aliases { - let transform = transforms.get(&alias.transform) + let transform = transforms + .get(&alias.transform) .ok_or_else(|| format!("Unknown transform: {}", alias.transform))?; - let base_name = alias.args.first() - .ok_or_else(|| format!("Interface alias {} requires a base interface", alias.name))?; + let base_name = alias.args.first().ok_or_else(|| { + format!("Interface alias {} requires a base interface", alias.name) + })?; - let base_interface = self.get_interface(base_name) + let base_interface = self + .get_interface(base_name) .ok_or_else(|| format!("Unknown interface: {}", base_name))?; let transformed = transform.transform(base_interface); - scope.aliased_interfaces.insert(alias.name.clone(), transformed); + scope + .aliased_interfaces + .insert(alias.name.clone(), transformed); } Ok(scope) @@ -845,10 +874,12 @@ impl TypeRegistry { base_interface_name: &str, transforms: &crate::transform::TransformRegistry, ) -> Result { - let transform = transforms.get(transform_name) + let transform = transforms + .get(transform_name) .ok_or_else(|| format!("Unknown transform: {}", transform_name))?; - let base_interface = self.get_interface(base_interface_name) + let base_interface = self + .get_interface(base_interface_name) .ok_or_else(|| format!("Unknown interface: {}", base_interface_name))?; Ok(transform.transform(base_interface)) @@ -921,7 +952,10 @@ fn parse_interface_alias(parser: &mut Parser, name: String) -> Result Result<(), ParseError> { +fn parse_interface_body( + parser: &mut Parser, + interface: &mut PactInterface, +) -> Result<(), ParseError> { while !parser.is_eof() { // Skip semicolons if parser.accept_symbol(';') { @@ -1194,7 +1228,10 @@ fn parse_use(parser: &mut Parser) -> Result { }) } -fn parse_imports_block(parser: &mut Parser, interface: &mut PactInterface) -> Result<(), ParseError> { +fn parse_imports_block( + parser: &mut Parser, + interface: &mut PactInterface, +) -> Result<(), ParseError> { while !matches!(parser.peek(), Token::Symbol('}')) { if parser.accept_symbol(';') { continue; @@ -1229,7 +1266,10 @@ fn parse_imports_block(parser: &mut Parser, interface: &mut PactInterface) -> Re Ok(()) } -fn parse_exports_block(parser: &mut Parser, interface: &mut PactInterface) -> Result<(), ParseError> { +fn parse_exports_block( + parser: &mut Parser, + interface: &mut PactInterface, +) -> Result<(), ParseError> { while !matches!(parser.peek(), Token::Symbol('}')) { if parser.accept_symbol(';') { continue; @@ -1246,7 +1286,9 @@ fn parse_exports_block(parser: &mut Parser, interface: &mut PactInterface) -> Re } // Could be a type alias in exports let ty = parse_type(parser)?; - interface.exports.push(PactExport::Type(TypeDef::alias(name, ty))); + interface + .exports + .push(PactExport::Type(TypeDef::alias(name, ty))); continue; } @@ -1315,8 +1357,8 @@ fn parse_type(parser: &mut Parser) -> Result { "self" => Ok(Type::self_ref()), "value" => Ok(Type::Value), "_" => Ok(Type::Tuple(vec![])), // Unit type, used in result<_, E> for void ok type - "list" => parse_generic_type(parser, |t| Type::list(t)), - "option" => parse_generic_type(parser, |t| Type::option(t)), + "list" => parse_generic_type(parser, Type::list), + "option" => parse_generic_type(parser, Type::option), "tuple" => parse_tuple(parser), "result" => parse_result(parser), _ => Ok(Type::named(ident)), @@ -1430,7 +1472,10 @@ mod tests { let interface = parse_pact(src).expect("parse"); assert_eq!(interface.type_params.len(), 1); assert_eq!(interface.type_params[0].name, "T"); - assert_eq!(interface.type_params[0].constraint, Some("Serializable".to_string())); + assert_eq!( + interface.type_params[0].constraint, + Some("Serializable".to_string()) + ); } #[test] @@ -1793,14 +1838,18 @@ mod tests { // Verify runtime can resolve 'chain' let runtime = registry.get_interface("runtime").unwrap(); - let runtime_scope = registry.resolve_scope(runtime).expect("resolve runtime scope"); + let runtime_scope = registry + .resolve_scope(runtime) + .expect("resolve runtime scope"); assert!(runtime_scope.contains_key("chain")); let chain_type = runtime_scope.get("chain").unwrap(); assert!(matches!(chain_type, TypeDef::Record { .. })); // Verify message-server-client can resolve 'channel-accept' let client = registry.get_interface("message-server-client").unwrap(); - let client_scope = registry.resolve_scope(client).expect("resolve client scope"); + let client_scope = registry + .resolve_scope(client) + .expect("resolve client scope"); assert!(client_scope.contains_key("channel-accept")); let channel_accept = client_scope.get("channel-accept").unwrap(); assert!(matches!(channel_accept, TypeDef::Record { .. })); @@ -1836,7 +1885,8 @@ mod tests { // Resolve the caller's scope with transforms let caller = type_registry.get_interface("caller").unwrap(); - let scope = type_registry.resolve_scope_with_transforms(caller, &transform_registry) + let scope = type_registry + .resolve_scope_with_transforms(caller, &transform_registry) .expect("resolve scope"); // Should have rpc-error type from the transform @@ -1877,7 +1927,8 @@ mod tests { // Resolve the aliases interface scope with transforms let aliases = type_registry.get_interface("aliases").unwrap(); - let scope = type_registry.resolve_scope_with_transforms(aliases, &transform_registry) + let scope = type_registry + .resolve_scope_with_transforms(aliases, &transform_registry) .expect("resolve scope"); // Should have calc-client alias @@ -1910,11 +1961,9 @@ mod tests { let type_registry = TypeRegistry::from_interface(&root); let transform_registry = TransformRegistry::with_builtins(); - let transformed = type_registry.get_transformed_interface( - "rpc", - "calculator", - &transform_registry - ).expect("get transformed"); + let transformed = type_registry + .get_transformed_interface("rpc", "calculator", &transform_registry) + .expect("get transformed"); assert_eq!(transformed.name, "rpc(calculator)"); diff --git a/src/parser/validation.rs b/src/parser/validation.rs index a8a942c..c532439 100644 --- a/src/parser/validation.rs +++ b/src/parser/validation.rs @@ -21,16 +21,9 @@ pub enum ValidationError { actual: String, }, #[error("Variant tag out of range at node {node}: tag {tag}, max {max}")] - VariantTagOutOfRange { - node: u32, - tag: u32, - max: usize, - }, + VariantTagOutOfRange { node: u32, tag: u32, max: usize }, #[error("Variant payload mismatch at node {node} tag {tag}")] - VariantPayloadMismatch { - node: u32, - tag: u32, - }, + VariantPayloadMismatch { node: u32, tag: u32 }, #[error("Unsupported type: {0}")] UnsupportedType(String), } @@ -50,14 +43,7 @@ pub fn validate_graph_against_type( } let mut assigned: HashMap = HashMap::new(); - validate_type( - buffer, - buffer.root, - root_type, - None, - &map, - &mut assigned, - ) + validate_type(buffer, buffer.root, root_type, None, &map, &mut assigned) } pub fn decode_with_schema( @@ -514,7 +500,7 @@ fn validate_value_variant( let has_payload = !case.payload.is_unit(); match (has_payload, payload) { - (false, None) | (false, Some(_)) if payload.map_or(true, |_| false) => Ok(()), + (false, None) | (false, Some(_)) if payload.is_none() => Ok(()), (false, Some(_)) => Err(ValidationError::VariantPayloadMismatch { node: 0, tag: tag as u32, @@ -606,9 +592,14 @@ fn validate_variant( match (has_payload, children.first()) { (false, None) => Ok(()), - (true, Some(&child)) => { - validate_type(buffer, child, &case.payload, Some(variant_name), types, assigned) - } + (true, Some(&child)) => validate_type( + buffer, + child, + &case.payload, + Some(variant_name), + types, + assigned, + ), _ => Err(ValidationError::VariantPayloadMismatch { node: index, tag }), } } @@ -663,11 +654,7 @@ fn validate_flags( Ok(()) } -fn expect_kind( - node: u32, - actual: NodeKind, - expected: NodeKind, -) -> Result<(), ValidationError> { +fn expect_kind(node: u32, actual: NodeKind, expected: NodeKind) -> Result<(), ValidationError> { if actual == expected { Ok(()) } else { diff --git a/src/parser/wit.rs b/src/parser/wit.rs index ebf8c09..6d80963 100644 --- a/src/parser/wit.rs +++ b/src/parser/wit.rs @@ -8,8 +8,8 @@ //! Use `parse_pact()` instead of `parse_interface()` for new code. use super::{ - Case, Field, Function, Interface, InterfaceExport, InterfaceImport, InterfacePath, - Param, ParseError, Type, TypeDef, World, WorldItem, + Case, Field, Function, Interface, InterfaceExport, InterfaceImport, InterfacePath, Param, + ParseError, Type, TypeDef, World, WorldItem, }; #[derive(Debug, Clone, PartialEq)] @@ -211,11 +211,10 @@ impl Parser { } fn accept_ident(&mut self, expected: &str) -> bool { - matches!(self.peek(), Token::Ident(name) if name == expected) - && { - self.pos += 1; - true - } + matches!(self.peek(), Token::Ident(name) if name == expected) && { + self.pos += 1; + true + } } fn expect_ident_value(&mut self, expected: &str) -> Result<(), ParseError> { @@ -348,9 +347,11 @@ fn parse_flags(parser: &mut Parser) -> Result { } fn try_parse_named_func(parser: &mut Parser) -> Result, ParseError> { - let (Token::Ident(name), Token::Symbol(':'), Token::Ident(func_kw)) = - (parser.peek().clone(), parser.peek_n(1).clone(), parser.peek_n(2).clone()) - else { + let (Token::Ident(name), Token::Symbol(':'), Token::Ident(func_kw)) = ( + parser.peek().clone(), + parser.peek_n(1).clone(), + parser.peek_n(2).clone(), + ) else { return Ok(None); }; @@ -366,10 +367,7 @@ fn try_parse_named_func(parser: &mut Parser) -> Result, ParseEr Ok(Some(func)) } -fn parse_func( - parser: &mut Parser, - name_override: Option, -) -> Result { +fn parse_func(parser: &mut Parser, name_override: Option) -> Result { let name = match name_override { Some(name) => name, None => parser.expect_ident()?, @@ -554,7 +552,7 @@ fn tokenize(src: &str) -> Result, ParseError> { chars.next(); // Check for // line comment if matches!(chars.peek(), Some('/')) { - while let Some(next) = chars.next() { + for next in chars.by_ref() { if next == '\n' { break; } diff --git a/src/runtime/composition.rs b/src/runtime/composition.rs index 48afdbe..a1f1e0f 100644 --- a/src/runtime/composition.rs +++ b/src/runtime/composition.rs @@ -29,9 +29,7 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; use crate::abi::{decode, encode, Value}; -use crate::runtime::{ - RuntimeError, INPUT_BUFFER_OFFSET, RESULT_PTR_OFFSET, RESULT_LEN_OFFSET, -}; +use crate::runtime::{RuntimeError, INPUT_BUFFER_OFFSET, RESULT_LEN_OFFSET, RESULT_PTR_OFFSET}; use wasmtime::{Caller, Engine, Linker, Module, Store}; /// A host function that can be wired into compositions. @@ -346,6 +344,7 @@ impl CompositionBuilder { /// ``` /// /// Returns 0 on success (output ptr/len written to slots), -1 on error. +#[allow(clippy::too_many_arguments)] fn cross_package_call( caller: &mut Caller<'_, ComposedState>, registry: &Arc>, @@ -363,7 +362,10 @@ fn cross_package_call( }; let mut input_bytes = vec![0u8; in_len as usize]; - if memory.read(&caller, in_ptr as usize, &mut input_bytes).is_err() { + if memory + .read(&caller, in_ptr as usize, &mut input_bytes) + .is_err() + { return -1; } @@ -375,7 +377,7 @@ fn cross_package_call( Some(p) => p, None => return -1, }; - (Arc::clone(&source.store), source.instance.clone()) + (Arc::clone(&source.store), source.instance) }; // Registry lock released here @@ -441,10 +443,16 @@ fn cross_package_call( // Read output pointer and length from the slots let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - if store_guard.read_memory(&src_memory, RESULT_PTR_OFFSET, &mut ptr_bytes).is_err() { + if store_guard + .read_memory(&src_memory, RESULT_PTR_OFFSET, &mut ptr_bytes) + .is_err() + { return -1; } - if store_guard.read_memory(&src_memory, RESULT_LEN_OFFSET, &mut len_bytes).is_err() { + if store_guard + .read_memory(&src_memory, RESULT_LEN_OFFSET, &mut len_bytes) + .is_err() + { return -1; } @@ -480,7 +488,10 @@ fn cross_package_call( // Let's use the same RESULT region as a data buffer for now (after the ptr/len slots). const CROSS_CALL_BUFFER_OFFSET: usize = RESULT_LEN_OFFSET + 4; - if memory.write(&mut *caller, CROSS_CALL_BUFFER_OFFSET, &result).is_err() { + if memory + .write(&mut *caller, CROSS_CALL_BUFFER_OFFSET, &result) + .is_err() + { return -1; } @@ -488,10 +499,24 @@ fn cross_package_call( let result_ptr = CROSS_CALL_BUFFER_OFFSET as i32; let result_len = result.len() as i32; - if memory.write(&mut *caller, out_ptr_ptr as usize, &result_ptr.to_le_bytes()).is_err() { + if memory + .write( + &mut *caller, + out_ptr_ptr as usize, + &result_ptr.to_le_bytes(), + ) + .is_err() + { return -1; } - if memory.write(&mut *caller, out_len_ptr as usize, &result_len.to_le_bytes()).is_err() { + if memory + .write( + &mut *caller, + out_len_ptr as usize, + &result_len.to_le_bytes(), + ) + .is_err() + { return -1; } @@ -517,7 +542,10 @@ fn host_function_call( }; let mut input_bytes = vec![0u8; in_len as usize]; - if memory.read(&caller, in_ptr as usize, &mut input_bytes).is_err() { + if memory + .read(&caller, in_ptr as usize, &mut input_bytes) + .is_err() + { return -1; } @@ -531,7 +559,10 @@ fn host_function_call( // (same approach as cross_package_call) const HOST_CALL_BUFFER_OFFSET: usize = RESULT_LEN_OFFSET + 4; - if memory.write(&mut *caller, HOST_CALL_BUFFER_OFFSET, &result).is_err() { + if memory + .write(&mut *caller, HOST_CALL_BUFFER_OFFSET, &result) + .is_err() + { return -1; } @@ -539,10 +570,24 @@ fn host_function_call( let result_ptr = HOST_CALL_BUFFER_OFFSET as i32; let result_len = result.len() as i32; - if memory.write(&mut *caller, out_ptr_ptr as usize, &result_ptr.to_le_bytes()).is_err() { + if memory + .write( + &mut *caller, + out_ptr_ptr as usize, + &result_ptr.to_le_bytes(), + ) + .is_err() + { return -1; } - if memory.write(&mut *caller, out_len_ptr as usize, &result_len.to_le_bytes()).is_err() { + if memory + .write( + &mut *caller, + out_len_ptr as usize, + &result_len.to_le_bytes(), + ) + .is_err() + { return -1; } @@ -625,7 +670,9 @@ impl UntypedStore { ) -> Option> { match self { UntypedStore::Unit(store) => instance.get_typed_func(&mut *store, "__pack_free").ok(), - UntypedStore::Composed(store) => instance.get_typed_func(&mut *store, "__pack_free").ok(), + UntypedStore::Composed(store) => { + instance.get_typed_func(&mut *store, "__pack_free").ok() + } } } @@ -635,15 +682,13 @@ impl UntypedStore { ) -> Option> { match self { UntypedStore::Unit(store) => instance.get_typed_func(&mut *store, "__pack_alloc").ok(), - UntypedStore::Composed(store) => instance.get_typed_func(&mut *store, "__pack_alloc").ok(), + UntypedStore::Composed(store) => { + instance.get_typed_func(&mut *store, "__pack_alloc").ok() + } } } - fn call_alloc( - &mut self, - func: &wasmtime::TypedFunc, - size: i32, - ) -> Result { + fn call_alloc(&mut self, func: &wasmtime::TypedFunc, size: i32) -> Result { match self { UntypedStore::Unit(store) => func.call(&mut *store, size).map_err(|_| ()), UntypedStore::Composed(store) => func.call(&mut *store, size).map_err(|_| ()), @@ -661,11 +706,9 @@ impl UntypedStore { match self { UntypedStore::Unit(store) => func.call(&mut *store, (a, b, c, d)).map_err(|e| { eprintln!("[PACK DEBUG] call_func error: {:?}", e); - () }), UntypedStore::Composed(store) => func.call(&mut *store, (a, b, c, d)).map_err(|e| { eprintln!("[PACK DEBUG] call_func error: {:?}", e); - () }), } } @@ -687,9 +730,7 @@ impl UntypedStore { instance: &wasmtime::Instance, ) -> Option> { match self { - UntypedStore::Unit(store) => { - instance.get_typed_func(&mut *store, "__pack_types").ok() - } + UntypedStore::Unit(store) => instance.get_typed_func(&mut *store, "__pack_types").ok(), UntypedStore::Composed(store) => { instance.get_typed_func(&mut *store, "__pack_types").ok() } @@ -730,7 +771,7 @@ impl BuiltComposition { let pkg = reg.packages.get(package).ok_or_else(|| { RuntimeError::ModuleNotFound(format!("Package '{}' not found", package)) })?; - (Arc::clone(&pkg.store), pkg.instance.clone()) + (Arc::clone(&pkg.store), pkg.instance) }; // Registry lock released here @@ -740,18 +781,16 @@ impl BuiltComposition { let input_bytes = encode(input).map_err(|e| RuntimeError::AbiError(e.to_string()))?; // Get memory - let memory = store.get_memory(&instance).ok_or_else(|| { - RuntimeError::MemoryError("No memory export".into()) - })?; + let memory = store + .get_memory(&instance) + .ok_or_else(|| RuntimeError::MemoryError("No memory export".into()))?; // Try to allocate input buffer dynamically, fall back to fixed buffer let (in_ptr, dynamic_input) = match store.get_alloc_func(&instance) { - Some(alloc_func) => { - match store.call_alloc(&alloc_func, input_bytes.len() as i32) { - Ok(ptr) if ptr != 0 => (ptr, true), - _ => (INPUT_BUFFER_OFFSET as i32, false), - } - } + Some(alloc_func) => match store.call_alloc(&alloc_func, input_bytes.len() as i32) { + Ok(ptr) if ptr != 0 => (ptr, true), + _ => (INPUT_BUFFER_OFFSET as i32, false), + }, None => (INPUT_BUFFER_OFFSET as i32, false), }; @@ -761,12 +800,13 @@ impl BuiltComposition { .map_err(|_| RuntimeError::MemoryError("Failed to write input".into()))?; // Get and call the function - let func = store.get_typed_func(&instance, function).ok_or_else(|| { - RuntimeError::FunctionNotFound(function.to_string()) - })?; + let func = store + .get_typed_func(&instance, function) + .ok_or_else(|| RuntimeError::FunctionNotFound(function.to_string()))?; let status = store - .call_func(&func, + .call_func( + &func, in_ptr, input_bytes.len() as i32, RESULT_PTR_OFFSET as i32, @@ -832,7 +872,13 @@ impl BuiltComposition { /// List all packages in the composition. pub fn packages(&self) -> Vec { - self.registry.lock().unwrap().packages.keys().cloned().collect() + self.registry + .lock() + .unwrap() + .packages + .keys() + .cloned() + .collect() } /// Read embedded type metadata from a package in the composition. @@ -849,7 +895,7 @@ impl BuiltComposition { .packages .get(package) .ok_or(crate::metadata::MetadataError::NotFound)?; - (Arc::clone(&pkg.store), pkg.instance.clone()) + (Arc::clone(&pkg.store), pkg.instance) }; let mut store = store_arc.lock().unwrap(); @@ -864,9 +910,7 @@ impl BuiltComposition { RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32, ) - .map_err(|_| { - crate::metadata::MetadataError::CallFailed("call failed".into()) - })?; + .map_err(|_| crate::metadata::MetadataError::CallFailed("call failed".into()))?; if status != 0 { return Err(crate::metadata::MetadataError::CallFailed( @@ -874,21 +918,17 @@ impl BuiltComposition { )); } - let memory = store.get_memory(&instance).ok_or_else(|| { - crate::metadata::MetadataError::CallFailed("no memory".into()) - })?; + let memory = store + .get_memory(&instance) + .ok_or_else(|| crate::metadata::MetadataError::CallFailed("no memory".into()))?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; store .read_memory(&memory, RESULT_PTR_OFFSET, &mut ptr_bytes) - .map_err(|_| { - crate::metadata::MetadataError::CallFailed("read ptr failed".into()) - })?; + .map_err(|_| crate::metadata::MetadataError::CallFailed("read ptr failed".into()))?; store .read_memory(&memory, RESULT_LEN_OFFSET, &mut len_bytes) - .map_err(|_| { - crate::metadata::MetadataError::CallFailed("read len failed".into()) - })?; + .map_err(|_| crate::metadata::MetadataError::CallFailed("read len failed".into()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; let out_len = i32::from_le_bytes(len_bytes) as usize; diff --git a/src/runtime/host.rs b/src/runtime/host.rs index 5e432ce..31ec0b7 100644 --- a/src/runtime/host.rs +++ b/src/runtime/host.rs @@ -406,7 +406,6 @@ pub struct InterfaceBuilder<'a, 'b, T> { interceptor: Option>, } - impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { /// Register a raw host function with direct WASM-level parameters. /// @@ -513,10 +512,16 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { } }; - let memory = match ctx.caller.get_export("memory").and_then(|e| e.into_memory()) { + let memory = match ctx + .caller + .get_export("memory") + .and_then(|e| e.into_memory()) + { Some(m) => m, None => { - report(HostFunctionErrorKind::MemoryWrite("no memory export".to_string())); + report(HostFunctionErrorKind::MemoryWrite( + "no memory export".to_string(), + )); return -1; } }; @@ -527,11 +532,19 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { return -1; } - if let Err(e) = memory.write(&mut ctx.caller, out_ptr_ptr as usize, &(data_offset as i32).to_le_bytes()) { + if let Err(e) = memory.write( + &mut ctx.caller, + out_ptr_ptr as usize, + &(data_offset as i32).to_le_bytes(), + ) { report(HostFunctionErrorKind::MemoryWrite(e.to_string())); return -1; } - if let Err(e) = memory.write(&mut ctx.caller, out_len_ptr as usize, &(bytes.len() as i32).to_le_bytes()) { + if let Err(e) = memory.write( + &mut ctx.caller, + out_len_ptr as usize, + &(bytes.len() as i32).to_le_bytes(), + ) { report(HostFunctionErrorKind::MemoryWrite(e.to_string())); return -1; } @@ -553,14 +566,22 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { // Check interceptor for short-circuit (replay) if let Some(ref interceptor) = interceptor { - if let Some(recorded_output) = interceptor.before_import(&interface_name, &func_name, &input_value) { - interceptor.after_import(&interface_name, &func_name, &input_value, &recorded_output); + if let Some(recorded_output) = + interceptor.before_import(&interface_name, &func_name, &input_value) + { + interceptor.after_import( + &interface_name, + &func_name, + &input_value, + &recorded_output, + ); return write_output(&mut ctx, &recorded_output); } } // Clone input_value for after_import notification if interceptor exists - let input_value_for_interceptor = interceptor.as_ref().map(|_| input_value.clone()); + let input_value_for_interceptor = + interceptor.as_ref().map(|_| input_value.clone()); // Convert to user type let input: P = match P::try_from(input_value) { @@ -580,7 +601,12 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { // Notify interceptor of completed call if let Some(ref interceptor) = interceptor { if let Some(ref iv) = input_value_for_interceptor { - interceptor.after_import(&interface_name, &func_name, iv, &output_value); + interceptor.after_import( + &interface_name, + &func_name, + iv, + &output_value, + ); } } @@ -668,10 +694,16 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { } }; - let memory = match ctx.caller.get_export("memory").and_then(|e| e.into_memory()) { + let memory = match ctx + .caller + .get_export("memory") + .and_then(|e| e.into_memory()) + { Some(m) => m, None => { - report(HostFunctionErrorKind::MemoryWrite("no memory export".to_string())); + report(HostFunctionErrorKind::MemoryWrite( + "no memory export".to_string(), + )); return -1; } }; @@ -682,11 +714,19 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { return -1; } - if let Err(e) = memory.write(&mut ctx.caller, out_ptr_ptr as usize, &(data_offset as i32).to_le_bytes()) { + if let Err(e) = memory.write( + &mut ctx.caller, + out_ptr_ptr as usize, + &(data_offset as i32).to_le_bytes(), + ) { report(HostFunctionErrorKind::MemoryWrite(e.to_string())); return -1; } - if let Err(e) = memory.write(&mut ctx.caller, out_len_ptr as usize, &(bytes.len() as i32).to_le_bytes()) { + if let Err(e) = memory.write( + &mut ctx.caller, + out_len_ptr as usize, + &(bytes.len() as i32).to_le_bytes(), + ) { report(HostFunctionErrorKind::MemoryWrite(e.to_string())); return -1; } @@ -707,14 +747,22 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { // Check interceptor for short-circuit (replay) if let Some(ref interceptor) = interceptor { - if let Some(recorded_output) = interceptor.before_import(&interface_name, &func_name, &input_value) { - interceptor.after_import(&interface_name, &func_name, &input_value, &recorded_output); + if let Some(recorded_output) = + interceptor.before_import(&interface_name, &func_name, &input_value) + { + interceptor.after_import( + &interface_name, + &func_name, + &input_value, + &recorded_output, + ); return write_output(&mut ctx, &recorded_output); } } // Clone input_value for after_import notification if interceptor exists - let input_value_for_interceptor = interceptor.as_ref().map(|_| input_value.clone()); + let input_value_for_interceptor = + interceptor.as_ref().map(|_| input_value.clone()); // Convert to user type let input: P = match P::try_from(input_value) { @@ -746,7 +794,12 @@ impl<'a, 'b, T: 'static> InterfaceBuilder<'a, 'b, T> { // Notify interceptor of completed call if let Some(ref interceptor) = interceptor { if let Some(ref iv) = input_value_for_interceptor { - interceptor.after_import(&interface_name, &func_name, iv, &output_value); + interceptor.after_import( + &interface_name, + &func_name, + iv, + &output_value, + ); } } @@ -1263,17 +1316,14 @@ impl HostFunctionProvider for DefaultHostProvider { } }, )? - .func_raw( - "alloc", - |caller: Caller<'_, HostState>, size: i32| -> i32 { - let mut offset = caller.data().alloc_offset.lock().unwrap(); - let ptr = *offset; - *offset += size as usize; - // Align to 8 bytes - *offset = (*offset + 7) & !7; - ptr as i32 - }, - )?; + .func_raw("alloc", |caller: Caller<'_, HostState>, size: i32| -> i32 { + let mut offset = caller.data().alloc_offset.lock().unwrap(); + let ptr = *offset; + *offset += size as usize; + // Align to 8 bytes + *offset = (*offset + 7) & !7; + ptr as i32 + })?; Ok(()) } diff --git a/src/runtime/interface_check.rs b/src/runtime/interface_check.rs index ada54bd..991ac4f 100644 --- a/src/runtime/interface_check.rs +++ b/src/runtime/interface_check.rs @@ -161,11 +161,8 @@ mod tests { #[test] fn expected_signature_graph_abi_for_params() { - let func = Function::with_signature( - "process", - vec![Param::new("x", Type::S64)], - vec![Type::S64], - ); + let func = + Function::with_signature("process", vec![Param::new("x", Type::S64)], vec![Type::S64]); assert_eq!(expected_signature_for(&func), ExpectedSignature::GraphAbi); } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 2d4e7c6..e896ce5 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -10,9 +10,8 @@ mod interface_check; pub use composition::{BuiltComposition, CompositionBuilder, HostFn}; pub use host::{ AsyncCtx, Ctx, DefaultHostProvider, ErrorHandler, HostFunctionError, HostFunctionErrorKind, - HostFunctionProvider, HostLinkerBuilder, InterfaceBuilder, LinkerError, - INPUT_BUFFER_OFFSET, OUTPUT_BUFFER_CAPACITY, OUTPUT_BUFFER_OFFSET, - RESULT_PTR_OFFSET, RESULT_LEN_OFFSET, + HostFunctionProvider, HostLinkerBuilder, InterfaceBuilder, LinkerError, INPUT_BUFFER_OFFSET, + OUTPUT_BUFFER_CAPACITY, OUTPUT_BUFFER_OFFSET, RESULT_LEN_OFFSET, RESULT_PTR_OFFSET, }; pub use interceptor::CallInterceptor; pub use interface_check::{ @@ -250,7 +249,11 @@ impl<'a> AsyncCompiledModule<'a> { .await .map_err(|e| RuntimeError::WasmError(e.to_string()))?; - Ok(AsyncInstance { store, instance, interceptor: None }) + Ok(AsyncInstance { + store, + instance, + interceptor: None, + }) } /// Instantiate the module with a builder function for configuring host functions (async). @@ -310,7 +313,11 @@ impl<'a> AsyncCompiledModule<'a> { .await .map_err(|e| RuntimeError::WasmError(e.to_string()))?; - Ok(AsyncInstance { store, instance, interceptor }) + Ok(AsyncInstance { + store, + instance, + interceptor, + }) } /// Get a reference to the engine. @@ -417,7 +424,8 @@ impl AsyncInstance { // Write input to buffer let memory = self.get_memory()?; - memory.write(&mut self.store, in_ptr, &input_bytes) + memory + .write(&mut self.store, in_ptr, &input_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to write input".into()))?; // Call the function @@ -441,16 +449,20 @@ impl AsyncInstance { // Free the input buffer if dynamically allocated if dynamic_input { - self.call_pack_free_async(in_ptr, input_bytes.len()).await.ok(); + self.call_pack_free_async(in_ptr, input_bytes.len()) + .await + .ok(); } // Read result ptr/len from slots let memory = self.get_memory()?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read result ptr".into()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read result len".into()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -460,7 +472,8 @@ impl AsyncInstance { if status != 0 { // Read error message let mut err_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut err_bytes) + memory + .read(&self.store, out_ptr, &mut err_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read error".into()))?; // Free the error buffer @@ -508,7 +521,10 @@ impl AsyncInstance { /// Call __pack_free to free a guest-allocated buffer (async). async fn call_pack_free_async(&mut self, ptr: usize, len: usize) -> Result<(), RuntimeError> { - if let Ok(free_func) = self.instance.get_typed_func::<(i32, i32), ()>(&mut self.store, "__pack_free") { + if let Ok(free_func) = self + .instance + .get_typed_func::<(i32, i32), ()>(&mut self.store, "__pack_free") + { free_func .call_async(&mut self.store, (ptr as i32, len as i32)) .await @@ -539,14 +555,19 @@ impl AsyncInstance { /// Calls the `__pack_types` export to get CGRF-encoded metadata describing /// the package's imports and exports. Returns `Err(MetadataError::NotFound)` /// if the package doesn't export `__pack_types`. - pub async fn types(&mut self) -> Result { + pub async fn types( + &mut self, + ) -> Result { let types_func = self .instance .get_typed_func::<(i32, i32), i32>(&mut self.store, "__pack_types") .map_err(|_| crate::metadata::MetadataError::NotFound)?; let status = types_func - .call_async(&mut self.store, (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32)) + .call_async( + &mut self.store, + (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32), + ) .await .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; @@ -556,13 +577,16 @@ impl AsyncInstance { )); } - let memory = self.get_memory() + let memory = self + .get_memory() .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -570,7 +594,8 @@ impl AsyncInstance { // Read metadata bytes (static data, no __pack_free needed) let mut metadata_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut metadata_bytes) + memory + .read(&self.store, out_ptr, &mut metadata_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; crate::metadata::decode_metadata(&metadata_bytes) @@ -579,14 +604,19 @@ impl AsyncInstance { /// Read embedded type metadata with interface hashes from the package (async). /// /// Like `types()`, but also decodes interface hashes for compatibility checking. - pub async fn types_with_hashes(&mut self) -> Result { + pub async fn types_with_hashes( + &mut self, + ) -> Result { let types_func = self .instance .get_typed_func::<(i32, i32), i32>(&mut self.store, "__pack_types") .map_err(|_| crate::metadata::MetadataError::NotFound)?; let status = types_func - .call_async(&mut self.store, (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32)) + .call_async( + &mut self.store, + (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32), + ) .await .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; @@ -596,13 +626,16 @@ impl AsyncInstance { )); } - let memory = self.get_memory() + let memory = self + .get_memory() .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -610,7 +643,8 @@ impl AsyncInstance { // Read metadata bytes (static data, no __pack_free needed) let mut metadata_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut metadata_bytes) + memory + .read(&self.store, out_ptr, &mut metadata_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; crate::metadata::decode_metadata_with_hashes(&metadata_bytes) @@ -854,11 +888,7 @@ impl InstanceWithHost { /// /// The WASM function signature is `(in_ptr, in_len, out_ptr_ptr, out_len_ptr) -> status`: /// - Returns: 0 on success, -1 on error (error message in ptr/len) - pub fn call_with_value( - &mut self, - name: &str, - input: &Value, - ) -> Result { + pub fn call_with_value(&mut self, name: &str, input: &Value) -> Result { // Encode input let input_bytes = encode(input).map_err(|e| RuntimeError::AbiError(e.to_string()))?; @@ -870,7 +900,8 @@ impl InstanceWithHost { // Write input to buffer let memory = self.get_memory()?; - memory.write(&mut self.store, in_ptr, &input_bytes) + memory + .write(&mut self.store, in_ptr, &input_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to write input".into()))?; // Call the function @@ -900,9 +931,11 @@ impl InstanceWithHost { let memory = self.get_memory()?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read result ptr".into()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read result len".into()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -912,7 +945,8 @@ impl InstanceWithHost { if status != 0 { // Read error message let mut err_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut err_bytes) + memory + .read(&self.store, out_ptr, &mut err_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read error".into()))?; // Free the error buffer @@ -954,7 +988,10 @@ impl InstanceWithHost { /// Call __pack_free to free a guest-allocated buffer. fn call_pack_free(&mut self, ptr: usize, len: usize) -> Result<(), RuntimeError> { - if let Ok(free_func) = self.instance.get_typed_func::<(i32, i32), ()>(&mut self.store, "__pack_free") { + if let Ok(free_func) = self + .instance + .get_typed_func::<(i32, i32), ()>(&mut self.store, "__pack_free") + { free_func .call(&mut self.store, (ptr as i32, len as i32)) .map_err(|e| RuntimeError::WasmError(e.to_string()))?; @@ -967,14 +1004,19 @@ impl InstanceWithHost { /// Calls the `__pack_types` export to get CGRF-encoded metadata describing /// the package's imports and exports. Returns `Err(MetadataError::NotFound)` /// if the package doesn't export `__pack_types`. - pub fn types(&mut self) -> Result { + pub fn types( + &mut self, + ) -> Result { let types_func = self .instance .get_typed_func::<(i32, i32), i32>(&mut self.store, "__pack_types") .map_err(|_| crate::metadata::MetadataError::NotFound)?; let status = types_func - .call(&mut self.store, (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32)) + .call( + &mut self.store, + (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32), + ) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; if status != 0 { @@ -983,13 +1025,16 @@ impl InstanceWithHost { )); } - let memory = self.get_memory() + let memory = self + .get_memory() .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -997,7 +1042,8 @@ impl InstanceWithHost { // Read metadata bytes (static data, no __pack_free needed) let mut metadata_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut metadata_bytes) + memory + .read(&self.store, out_ptr, &mut metadata_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; crate::metadata::decode_metadata(&metadata_bytes) @@ -1006,14 +1052,19 @@ impl InstanceWithHost { /// Read embedded type metadata with interface hashes from the package. /// /// Like `types()`, but also decodes interface hashes for compatibility checking. - pub fn types_with_hashes(&mut self) -> Result { + pub fn types_with_hashes( + &mut self, + ) -> Result { let types_func = self .instance .get_typed_func::<(i32, i32), i32>(&mut self.store, "__pack_types") .map_err(|_| crate::metadata::MetadataError::NotFound)?; let status = types_func - .call(&mut self.store, (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32)) + .call( + &mut self.store, + (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32), + ) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; if status != 0 { @@ -1022,13 +1073,16 @@ impl InstanceWithHost { )); } - let memory = self.get_memory() + let memory = self + .get_memory() .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -1036,7 +1090,8 @@ impl InstanceWithHost { // Read metadata bytes (static data, no __pack_free needed) let mut metadata_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut metadata_bytes) + memory + .read(&self.store, out_ptr, &mut metadata_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; crate::metadata::decode_metadata_with_hashes(&metadata_bytes) @@ -1141,11 +1196,7 @@ impl Instance { /// /// The WASM function signature is `(in_ptr, in_len, out_ptr_ptr, out_len_ptr) -> status`: /// - Returns: 0 on success, -1 on error (error message in ptr/len) - pub fn call_with_value( - &mut self, - name: &str, - input: &Value, - ) -> Result { + pub fn call_with_value(&mut self, name: &str, input: &Value) -> Result { // Encode input let input_bytes = encode(input).map_err(|e| RuntimeError::AbiError(e.to_string()))?; @@ -1157,7 +1208,8 @@ impl Instance { // Write input to buffer let memory = self.get_memory()?; - memory.write(&mut self.store, in_ptr, &input_bytes) + memory + .write(&mut self.store, in_ptr, &input_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to write input".into()))?; // Call the function @@ -1187,9 +1239,11 @@ impl Instance { let memory = self.get_memory()?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read result ptr".into()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read result len".into()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -1199,7 +1253,8 @@ impl Instance { if status != 0 { // Read error message let mut err_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut err_bytes) + memory + .read(&self.store, out_ptr, &mut err_bytes) .map_err(|_| RuntimeError::MemoryError("Failed to read error".into()))?; // Free the error buffer @@ -1241,7 +1296,10 @@ impl Instance { /// Call __pack_free to free a guest-allocated buffer. fn call_pack_free(&mut self, ptr: usize, len: usize) -> Result<(), RuntimeError> { - if let Ok(free_func) = self.instance.get_typed_func::<(i32, i32), ()>(&mut self.store, "__pack_free") { + if let Ok(free_func) = self + .instance + .get_typed_func::<(i32, i32), ()>(&mut self.store, "__pack_free") + { free_func .call(&mut self.store, (ptr as i32, len as i32)) .map_err(|e| RuntimeError::WasmError(e.to_string()))?; @@ -1254,14 +1312,19 @@ impl Instance { /// Calls the `__pack_types` export to get CGRF-encoded metadata describing /// the package's imports and exports. Returns `Err(MetadataError::NotFound)` /// if the package doesn't export `__pack_types`. - pub fn types(&mut self) -> Result { + pub fn types( + &mut self, + ) -> Result { let types_func = self .instance .get_typed_func::<(i32, i32), i32>(&mut self.store, "__pack_types") .map_err(|_| crate::metadata::MetadataError::NotFound)?; let status = types_func - .call(&mut self.store, (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32)) + .call( + &mut self.store, + (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32), + ) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; if status != 0 { @@ -1270,13 +1333,16 @@ impl Instance { )); } - let memory = self.get_memory() + let memory = self + .get_memory() .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -1284,7 +1350,8 @@ impl Instance { // Read metadata bytes (static data, no __pack_free needed) let mut metadata_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut metadata_bytes) + memory + .read(&self.store, out_ptr, &mut metadata_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; crate::metadata::decode_metadata(&metadata_bytes) @@ -1293,14 +1360,19 @@ impl Instance { /// Read embedded type metadata with interface hashes from the package. /// /// Like `types()`, but also decodes interface hashes for compatibility checking. - pub fn types_with_hashes(&mut self) -> Result { + pub fn types_with_hashes( + &mut self, + ) -> Result { let types_func = self .instance .get_typed_func::<(i32, i32), i32>(&mut self.store, "__pack_types") .map_err(|_| crate::metadata::MetadataError::NotFound)?; let status = types_func - .call(&mut self.store, (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32)) + .call( + &mut self.store, + (RESULT_PTR_OFFSET as i32, RESULT_LEN_OFFSET as i32), + ) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; if status != 0 { @@ -1309,13 +1381,16 @@ impl Instance { )); } - let memory = self.get_memory() + let memory = self + .get_memory() .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let mut ptr_bytes = [0u8; 4]; let mut len_bytes = [0u8; 4]; - memory.read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) + memory + .read(&self.store, RESULT_PTR_OFFSET, &mut ptr_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; - memory.read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) + memory + .read(&self.store, RESULT_LEN_OFFSET, &mut len_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; let out_ptr = i32::from_le_bytes(ptr_bytes) as usize; @@ -1323,7 +1398,8 @@ impl Instance { // Read metadata bytes (static data, no __pack_free needed) let mut metadata_bytes = vec![0u8; out_len]; - memory.read(&self.store, out_ptr, &mut metadata_bytes) + memory + .read(&self.store, out_ptr, &mut metadata_bytes) .map_err(|e| crate::metadata::MetadataError::CallFailed(e.to_string()))?; crate::metadata::decode_metadata_with_hashes(&metadata_bytes) @@ -1400,11 +1476,7 @@ mod tests { fields: vec![("wrong".to_string(), Value::String("x".to_string()))], }; let err = runtime - .encode_result_with_schema( - &interface.types, - &value, - &Type::named("config".to_string()), - ) + .encode_result_with_schema(&interface.types, &value, &Type::named("config".to_string())) .expect_err("expected error"); match err { diff --git a/src/transform.rs b/src/transform.rs index d75f72f..3b3eedf 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -306,7 +306,10 @@ mod tests { Type::Result { ok, err } => { // ok should be result match ok.as_ref() { - Type::Result { ok: inner_ok, err: inner_err } => { + Type::Result { + ok: inner_ok, + err: inner_err, + } => { assert_eq!(**inner_ok, Type::S32); assert_eq!(**inner_err, Type::String); } diff --git a/src/types.rs b/src/types.rs index 8eb0362..2e1fe85 100644 --- a/src/types.rs +++ b/src/types.rs @@ -175,11 +175,7 @@ impl Function { } /// Create a function with parameters and results. - pub fn with_signature( - name: impl Into, - params: Vec, - results: Vec, - ) -> Self { + pub fn with_signature(name: impl Into, params: Vec, results: Vec) -> Self { Self { name: name.into(), interface: String::new(), diff --git a/tests/abi_roundtrip.rs b/tests/abi_roundtrip.rs index 0381efa..aad95d6 100644 --- a/tests/abi_roundtrip.rs +++ b/tests/abi_roundtrip.rs @@ -118,7 +118,11 @@ fn roundtrip_array_s32() { fn roundtrip_array_f64() { let value = Value::List { elem_type: ValueType::F64, - items: vec![Value::F64(3.14), Value::F64(-0.0), Value::F64(f64::INFINITY)], + items: vec![ + Value::F64(3.14), + Value::F64(-0.0), + Value::F64(f64::INFINITY), + ], }; let bytes = encode(&value).expect("encode"); @@ -158,7 +162,11 @@ fn roundtrip_large_byte_array() { let bytes = encode(&value).expect("encode"); // 100KB of data should encode to ~100KB, not megabytes - assert!(bytes.len() < 200_000, "Array encoding should be near 1:1, got {}", bytes.len()); + assert!( + bytes.len() < 200_000, + "Array encoding should be near 1:1, got {}", + bytes.len() + ); let decoded = decode(&bytes).expect("decode"); assert_eq!(decoded, value); diff --git a/tests/composition.rs b/tests/composition.rs index 4a45a3a..d8f7f05 100644 --- a/tests/composition.rs +++ b/tests/composition.rs @@ -90,10 +90,10 @@ fn composition_multiple_calls() { // Test with various inputs let test_cases = vec![ - (0, 1), // 0 * 2 + 1 = 1 - (1, 3), // 1 * 2 + 1 = 3 - (5, 11), // 5 * 2 + 1 = 11 - (10, 21), // 10 * 2 + 1 = 21 + (0, 1), // 0 * 2 + 1 = 1 + (1, 3), // 1 * 2 + 1 = 3 + (5, 11), // 5 * 2 + 1 = 11 + (10, 21), // 10 * 2 + 1 = 21 (100, 201), // 100 * 2 + 1 = 201 ]; @@ -101,7 +101,13 @@ fn composition_multiple_calls() { let result = composition .call("adder", "process", &Value::S64(input)) .expect(&format!("failed to call process with {}", input)); - assert_eq!(result, Value::S64(expected), "process({}) should be {}", input, expected); + assert_eq!( + result, + Value::S64(expected), + "process({}) should be {}", + input, + expected + ); } } diff --git a/tests/host_functions.rs b/tests/host_functions.rs index 78b8df6..d0fac89 100644 --- a/tests/host_functions.rs +++ b/tests/host_functions.rs @@ -41,20 +41,20 @@ fn test_namespaced_interface_registration() { let mut instance = module .instantiate_with_host(state, |builder| { - builder - .interface("theater:simple/runtime")? - .func_raw( - "add_offset", - |caller: Caller<'_, TestState>, a: i32, b: i32| -> i32 { - a + b + caller.data().actor_id - }, - )?; + builder.interface("theater:simple/runtime")?.func_raw( + "add_offset", + |caller: Caller<'_, TestState>, a: i32, b: i32| -> i32 { + a + b + caller.data().actor_id + }, + )?; Ok(()) }) .expect("instantiate"); // compute(5, 10) should return 5 + 10 + 100 = 115 - let result = instance.call_i32_i32_to_i32("compute", 5, 10).expect("call"); + let result = instance + .call_i32_i32_to_i32("compute", 5, 10) + .expect("call"); assert_eq!(result, 115); } @@ -176,7 +176,9 @@ fn test_multiple_interfaces() { }) .expect("instantiate"); - let result = instance.call_i32_i32_to_i32("compute", 5, 10).expect("call"); + let result = instance + .call_i32_i32_to_i32("compute", 5, 10) + .expect("call"); assert_eq!(result, 30); // double(add(5, 10)) = double(15) = 30 } @@ -187,10 +189,7 @@ fn test_provider_pattern() { struct MathProvider; impl HostFunctionProvider<()> for MathProvider { - fn register( - &self, - builder: &mut HostLinkerBuilder<'_, ()>, - ) -> Result<(), LinkerError> { + fn register(&self, builder: &mut HostLinkerBuilder<'_, ()>) -> Result<(), LinkerError> { builder .interface("math")? .func_raw("add", |_: Caller<'_, ()>, a: i32, b: i32| a + b)? @@ -637,10 +636,16 @@ fn test_func_typed_result_encodes_as_value_result_ok() { // Verify output is Value::Result with Ok variant, NOT Value::Variant match output { - Value::Result { value: Ok(inner), .. } => { + Value::Result { + value: Ok(inner), .. + } => { assert_eq!(*inner, Value::S64(42)); // 21 * 2 = 42 } - Value::Variant { type_name, case_name, .. } => { + Value::Variant { + type_name, + case_name, + .. + } => { panic!( "Expected Value::Result but got Value::Variant(type_name={}, case_name={})", type_name, case_name @@ -705,7 +710,9 @@ fn test_func_typed_result_encodes_as_value_result_err() { |_ctx: &mut pack::Ctx<'_, ()>, input: Value| -> Result { // Return Err for negative numbers match input { - Value::S64(n) if n < 0 => Err(Value::String("negative not allowed".to_string())), + Value::S64(n) if n < 0 => { + Err(Value::String("negative not allowed".to_string())) + } Value::S64(n) => Ok(Value::S64(n * 2)), other => Ok(other), } @@ -723,10 +730,16 @@ fn test_func_typed_result_encodes_as_value_result_err() { // Verify output is Value::Result with Err variant, NOT Value::Variant match output { - Value::Result { value: Err(inner), .. } => { + Value::Result { + value: Err(inner), .. + } => { assert_eq!(*inner, Value::String("negative not allowed".to_string())); } - Value::Variant { type_name, case_name, .. } => { + Value::Variant { + type_name, + case_name, + .. + } => { panic!( "Expected Value::Result but got Value::Variant(type_name={}, case_name={})", type_name, case_name @@ -812,10 +825,16 @@ async fn test_func_async_result_encodes_as_value_result_ok() { // Verify output is Value::Result with Ok variant, NOT Value::Variant match output { - Value::Result { value: Ok(inner), .. } => { + Value::Result { + value: Ok(inner), .. + } => { assert_eq!(*inner, Value::S64(42)); // 21 * 2 = 42 } - Value::Variant { type_name, case_name, .. } => { + Value::Variant { + type_name, + case_name, + .. + } => { panic!( "Expected Value::Result but got Value::Variant(type_name={}, case_name={})", type_name, case_name @@ -882,7 +901,9 @@ async fn test_func_async_result_encodes_as_value_result_err() { |_ctx: pack::AsyncCtx<()>, input: Value| async move { // Return Err for negative numbers let result: Result = match input { - Value::S64(n) if n < 0 => Err(Value::String("negative not allowed".to_string())), + Value::S64(n) if n < 0 => { + Err(Value::String("negative not allowed".to_string())) + } Value::S64(n) => Ok(Value::S64(n * 2)), other => Ok(other), }; @@ -903,10 +924,16 @@ async fn test_func_async_result_encodes_as_value_result_err() { // Verify output is Value::Result with Err variant, NOT Value::Variant match output { - Value::Result { value: Err(inner), .. } => { + Value::Result { + value: Err(inner), .. + } => { assert_eq!(*inner, Value::String("negative not allowed".to_string())); } - Value::Variant { type_name, case_name, .. } => { + Value::Variant { + type_name, + case_name, + .. + } => { panic!( "Expected Value::Result but got Value::Variant(type_name={}, case_name={})", type_name, case_name @@ -995,10 +1022,18 @@ fn test_func_typed_result_preserves_ok_type_on_err() { // Verify output has correct types even though we returned Err match output { - Value::Result { ok_type, err_type, value: Err(inner) } => { + Value::Result { + ok_type, + err_type, + value: Err(inner), + } => { // ok_type should be List, NOT String (the bug was defaulting to String) - assert_eq!(ok_type, ValueType::List(Box::new(ValueType::U8)), - "ok_type should be List, not {:?}", ok_type); + assert_eq!( + ok_type, + ValueType::List(Box::new(ValueType::U8)), + "ok_type should be List, not {:?}", + ok_type + ); assert_eq!(err_type, ValueType::String); assert_eq!(*inner, Value::String("Resource not found".to_string())); } @@ -1078,11 +1113,19 @@ fn test_func_typed_result_preserves_err_type_on_ok() { // Verify output has correct types even though we returned Ok match output { - Value::Result { ok_type, err_type, value: Ok(inner) } => { + Value::Result { + ok_type, + err_type, + value: Ok(inner), + } => { assert_eq!(ok_type, ValueType::String); // err_type should be List, NOT String (the bug was defaulting to String) - assert_eq!(err_type, ValueType::List(Box::new(ValueType::U8)), - "err_type should be List, not {:?}", err_type); + assert_eq!( + err_type, + ValueType::List(Box::new(ValueType::U8)), + "err_type should be List, not {:?}", + err_type + ); assert_eq!(*inner, Value::String("Success!".to_string())); } other => { diff --git a/tests/schema_validation.rs b/tests/schema_validation.rs index 2c37f67..8639702 100644 --- a/tests/schema_validation.rs +++ b/tests/schema_validation.rs @@ -143,8 +143,7 @@ fn decode_with_schema_roundtrip() { }; let bytes = encode(&value).expect("encode"); let decoded = - decode_with_schema(&interface.types, &bytes, &Type::named("node"), None) - .expect("decode"); + decode_with_schema(&interface.types, &bytes, &Type::named("node"), None).expect("decode"); assert_eq!(decoded, value); } @@ -161,13 +160,8 @@ fn decode_with_schema_rejects_mismatch() { let value = Value::String("bad".to_string()); let bytes = encode(&value).expect("encode"); - let err = decode_with_schema( - &interface.types, - &bytes, - &Type::named("node"), - None, - ) - .expect_err("expected validation error"); + let err = decode_with_schema(&interface.types, &bytes, &Type::named("node"), None) + .expect_err("expected validation error"); match err { ValidationError::TypeMismatch { .. } => {} @@ -188,12 +182,8 @@ fn validate_flags_mask() { let bytes = encode(&value).expect("encode"); let buffer = GraphBuffer::from_bytes(&bytes).expect("from_bytes"); - validate_graph_against_type( - &interface.types, - &buffer, - &Type::named("mode"), - ) - .expect("schema validate"); + validate_graph_against_type(&interface.types, &buffer, &Type::named("mode")) + .expect("schema validate"); } #[test] diff --git a/tests/type_metadata.rs b/tests/type_metadata.rs index 3716533..b28530c 100644 --- a/tests/type_metadata.rs +++ b/tests/type_metadata.rs @@ -1,8 +1,8 @@ //! Integration tests for embedded type metadata. +use pack::abi::{encode, Value, ValueType}; use pack::metadata::{decode_metadata, MetadataError, TypeDesc}; use pack::runtime::{CompositionBuilder, Runtime}; -use pack::abi::{encode, Value, ValueType}; fn load_wasm(name: &str) -> Vec { let manifest_dir = env!("CARGO_MANIFEST_DIR"); @@ -28,14 +28,22 @@ fn test_echo_metadata() { // Echo has 2 exports: echo and transform assert_eq!(metadata.exports().len(), 2, "echo should have 2 exports"); - let echo_fn = metadata.exports().into_iter().find(|f| f.name == "echo").expect("echo export"); + let echo_fn = metadata + .exports() + .into_iter() + .find(|f| f.name == "echo") + .expect("echo export"); assert_eq!(echo_fn.params.len(), 1); assert_eq!(echo_fn.params[0].name, "input"); assert_eq!(echo_fn.params[0].ty, TypeDesc::Value); assert_eq!(echo_fn.results.len(), 1); assert_eq!(echo_fn.results[0], TypeDesc::Value); - let transform_fn = metadata.exports().into_iter().find(|f| f.name == "transform").expect("transform export"); + let transform_fn = metadata + .exports() + .into_iter() + .find(|f| f.name == "transform") + .expect("transform export"); assert_eq!(transform_fn.params.len(), 1); assert_eq!(transform_fn.params[0].name, "input"); assert_eq!(transform_fn.params[0].ty, TypeDesc::Value); @@ -46,7 +54,9 @@ fn test_echo_metadata() { #[test] fn test_doubler_metadata() { let runtime = Runtime::new(); - let module = runtime.load_module(&load_wasm("doubler")).expect("load doubler"); + let module = runtime + .load_module(&load_wasm("doubler")) + .expect("load doubler"); let mut instance = module.instantiate().expect("instantiate doubler"); let metadata = instance.types().expect("should have metadata"); diff --git a/tests/wasm_execution.rs b/tests/wasm_execution.rs index 638fb7c..e1840f6 100644 --- a/tests/wasm_execution.rs +++ b/tests/wasm_execution.rs @@ -25,13 +25,17 @@ fn run_add_module() { // Create the runtime and load the module let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); // Instantiate and run let mut instance = module.instantiate().expect("failed to instantiate"); // Call the add function - let result = instance.call_i32_i32_to_i32("add", 2, 3).expect("failed to call add"); + let result = instance + .call_i32_i32_to_i32("add", 2, 3) + .expect("failed to call add"); assert_eq!(result, 5); // Try a few more values @@ -57,7 +61,9 @@ fn run_add64_module() { let wasm_bytes = wat::parse_str(ADD64_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); let result = instance @@ -72,7 +78,9 @@ fn call_missing_function() { let wasm_bytes = wat::parse_str(ADD_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); let err = instance.call_i32_i32_to_i32("nonexistent", 1, 2); @@ -126,12 +134,16 @@ fn memory_read_write() { let wasm_bytes = wat::parse_str(SUM_BYTES_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); // Write some bytes to memory let data = [1u8, 2, 3, 4, 5]; - instance.write_memory(0, &data).expect("failed to write memory"); + instance + .write_memory(0, &data) + .expect("failed to write memory"); // Call sum_bytes(0, 5) - should return 1+2+3+4+5 = 15 let result = instance @@ -149,7 +161,9 @@ fn memory_string_roundtrip() { let wasm_bytes = wat::parse_str(SUM_BYTES_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); // Write a string to memory @@ -215,7 +229,9 @@ fn memory_reverse_string() { let wasm_bytes = wat::parse_str(REVERSE_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); // Write a string @@ -272,7 +288,9 @@ fn graph_abi_echo_roundtrip() { let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); // Test with a simple integer @@ -288,7 +306,9 @@ fn graph_abi_echo_string() { let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); let input = Value::String("Hello, Graph ABI!".to_string()); @@ -303,16 +323,14 @@ fn graph_abi_echo_list() { let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); let input = Value::List { elem_type: ValueType::S64, - items: vec![ - Value::S64(1), - Value::S64(2), - Value::S64(3), - ], + items: vec![Value::S64(1), Value::S64(2), Value::S64(3)], }; let output = instance .call_with_value("echo", &input) @@ -325,7 +343,9 @@ fn graph_abi_echo_variant() { let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); // This is like a simple S-expression node @@ -346,7 +366,9 @@ fn graph_abi_echo_nested() { let wasm_bytes = wat::parse_str(ECHO_MODULE).expect("failed to parse WAT"); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load module"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load module"); let mut instance = module.instantiate().expect("failed to instantiate"); // A nested structure like (list (sym "a") (num 42)) @@ -406,7 +428,9 @@ fn rust_package_echo_roundtrip() { let wasm_bytes = load_rust_echo_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load Rust package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load Rust package"); let mut instance = module.instantiate().expect("failed to instantiate"); // Test with a simple integer @@ -422,7 +446,9 @@ fn rust_package_echo_complex() { let wasm_bytes = load_rust_echo_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load Rust package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load Rust package"); let mut instance = module.instantiate().expect("failed to instantiate"); // Test with a nested structure @@ -436,11 +462,7 @@ fn rust_package_echo_complex() { tag: 2, payload: vec![Value::List { elem_type: ValueType::S64, - items: vec![ - Value::S64(1), - Value::S64(2), - Value::S64(3), - ], + items: vec![Value::S64(1), Value::S64(2), Value::S64(3)], }], }, Value::Option { @@ -461,7 +483,9 @@ fn rust_package_transform_doubles_s64() { let wasm_bytes = load_rust_echo_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load Rust package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load Rust package"); let mut instance = module.instantiate().expect("failed to instantiate"); // Test transform doubles S64 @@ -477,7 +501,9 @@ fn rust_package_transform_nested() { let wasm_bytes = load_rust_echo_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load Rust package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load Rust package"); let mut instance = module.instantiate().expect("failed to instantiate"); // Test transform on nested structure - doubles all S64 values @@ -498,13 +524,13 @@ fn rust_package_transform_nested() { let expected = Value::List { elem_type: ValueType::Variant("node".to_string()), items: vec![ - Value::S64(20), // 10 * 2 - Value::S64(40), // 20 * 2 + Value::S64(20), // 10 * 2 + Value::S64(40), // 20 * 2 Value::Variant { type_name: "node".to_string(), case_name: "num".to_string(), tag: 1, - payload: vec![Value::S64(100)], // 50 * 2 + payload: vec![Value::S64(100)], // 50 * 2 }, ], }; @@ -520,7 +546,9 @@ fn rust_package_transform_preserves_strings() { let wasm_bytes = load_rust_echo_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load Rust package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load Rust package"); let mut instance = module.instantiate().expect("failed to instantiate"); // Strings should pass through unchanged @@ -555,7 +583,9 @@ fn host_imports_logging() { let wasm_bytes = load_rust_logger_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load logger package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load logger package"); // Instantiate with host imports let imports = HostImports::new(); @@ -577,9 +607,18 @@ fn host_imports_logging() { assert!(!logs.is_empty(), "Expected log messages"); // Verify specific log messages - assert!(logs.iter().any(|m| m.contains("starting")), "Expected 'starting' log"); - assert!(logs.iter().any(|m| m.contains("got S64")), "Expected S64 type log"); - assert!(logs.iter().any(|m| m.contains("done")), "Expected 'done' log"); + assert!( + logs.iter().any(|m| m.contains("starting")), + "Expected 'starting' log" + ); + assert!( + logs.iter().any(|m| m.contains("got S64")), + "Expected S64 type log" + ); + assert!( + logs.iter().any(|m| m.contains("done")), + "Expected 'done' log" + ); } #[test] @@ -587,7 +626,9 @@ fn host_imports_logging_with_string() { let wasm_bytes = load_rust_logger_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load logger package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load logger package"); let imports = HostImports::new(); let mut instance = module @@ -604,8 +645,14 @@ fn host_imports_logging_with_string() { assert_eq!(output, input); let logs = instance.get_logs(); - assert!(logs.iter().any(|m| m.contains("got String")), "Expected String type log"); - assert!(logs.iter().any(|m| m.contains("test message")), "Expected the actual string in logs"); + assert!( + logs.iter().any(|m| m.contains("got String")), + "Expected String type log" + ); + assert!( + logs.iter().any(|m| m.contains("test message")), + "Expected the actual string in logs" + ); } #[test] @@ -613,7 +660,9 @@ fn host_imports_transform_nested() { let wasm_bytes = load_rust_logger_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load logger package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load logger package"); let imports = HostImports::new(); let mut instance = module @@ -623,20 +672,12 @@ fn host_imports_transform_nested() { // Test with a list of S64 values let input = Value::List { elem_type: ValueType::S64, - items: vec![ - Value::S64(10), - Value::S64(20), - Value::S64(30), - ], + items: vec![Value::S64(10), Value::S64(20), Value::S64(30)], }; let expected = Value::List { elem_type: ValueType::S64, - items: vec![ - Value::S64(20), - Value::S64(40), - Value::S64(60), - ], + items: vec![Value::S64(20), Value::S64(40), Value::S64(60)], }; let output = instance @@ -647,7 +688,10 @@ fn host_imports_transform_nested() { // Verify logging happened let logs = instance.get_logs(); - assert!(logs.iter().any(|m| m.contains("got List")), "Expected List type log"); + assert!( + logs.iter().any(|m| m.contains("got List")), + "Expected List type log" + ); } #[test] @@ -655,7 +699,9 @@ fn host_imports_clear_logs() { let wasm_bytes = load_rust_logger_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load logger package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load logger package"); let imports = HostImports::new(); let mut instance = module @@ -679,7 +725,10 @@ fn host_imports_clear_logs() { assert!(!logs2.is_empty()); // Should only have logs from second call - assert!(logs2.len() < logs1.len() * 2, "Should only have logs from second call"); + assert!( + logs2.len() < logs1.len() * 2, + "Should only have logs from second call" + ); } // ============================================================================ @@ -781,7 +830,9 @@ fn sexpr_eval_simple_addition() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (+ 1 2 3) => 6 @@ -804,7 +855,9 @@ fn sexpr_eval_nested_arithmetic() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (* (+ 2 3) (- 10 4)) => 5 * 6 = 30 @@ -826,15 +879,13 @@ fn sexpr_eval_comparison() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (< 5 10) => true - let input = sexpr::list(vec![ - sexpr::sym("<"), - sexpr::num(5), - sexpr::num(10), - ]); + let input = sexpr::list(vec![sexpr::sym("<"), sexpr::num(5), sexpr::num(10)]); let output = instance .call_with_value("evaluate", &input) @@ -848,7 +899,9 @@ fn sexpr_eval_if_true() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (if (> 10 5) 42 0) => 42 @@ -871,7 +924,9 @@ fn sexpr_eval_if_false() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (if (< 10 5) 42 0) => 0 @@ -894,7 +949,9 @@ fn sexpr_eval_list_operations() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (car (list 1 2 3)) => 1 @@ -920,7 +977,9 @@ fn sexpr_eval_length() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (length (list 1 2 3 4 5)) => 5 @@ -948,7 +1007,9 @@ fn sexpr_eval_complex_expression() { let wasm_bytes = load_rust_sexpr_package(); let runtime = Runtime::new(); - let module = runtime.load_module(&wasm_bytes).expect("failed to load sexpr package"); + let module = runtime + .load_module(&wasm_bytes) + .expect("failed to load sexpr package"); let mut instance = module.instantiate().expect("failed to instantiate"); // (if (and (> 10 5) (< 3 7))