e79e289743
Fix all 33 rustdoc warnings across the workspace: - Unresolved intra-doc links: rewrite [`name`] → either backtick text (when not actually a link) or fully-qualified `[Type::method]` / `[crate::module::name]` form. Affected: mxaccess-codec (asb_variant, item_control, metadata_query, observed_write_template, reference_handle, write_message), mxaccess-rpc (pdu), mxaccess-nmx (client), mxaccess-asb-nettcp (nmf), mxaccess-callback (exporter), mxaccess (asb_session, session, lib). - Bracket-text being interpreted as link refs (e.g. `body[17]` → `` `body[17]` ``). - Private-item references in public docs (CALLBACK_BROADCAST_CAPACITY, recover_connection_core, mxvalue_to_writevalue) reduced to backtick-text since they aren't part of the public API. `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps` now exits clean. Workspace 759 tests pass; clippy clean. Defers `#![warn(missing_docs)]` lint to a future pass — the cleanup target is the broken-link warnings, which are signal; missing-docs would surface hundreds of low-priority public-item gaps that are out of scope for this F-number. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
193 lines
8.9 KiB
Rust
193 lines
8.9 KiB
Rust
//! `NmxMetadataQueryMessage` — observed pre-advise metadata-query body.
|
|
//!
|
|
//! Direct port of `src/MxNativeCodec/NmxMetadataQueryMessage.cs`. The .NET
|
|
//! reference exposes a single static helper, `encode_observed_pre_advise`,
|
|
//! which returns a fixed observed body with a 16-byte item-correlation GUID
|
|
//! patched in at offset `0x8a`.
|
|
//!
|
|
//! The body is a captured constant — both segments of the hex literal in
|
|
//! `NmxMetadataQueryMessage.cs:10-11` are reproduced byte-for-byte below.
|
|
//! It encodes two metadata queries against `$DevPlatform.GR.TimeOfLastDeploy`
|
|
//! and `$DevPlatform.GR.TimeOfLastConfigChange`. The Rust port preserves
|
|
//! every byte; the only mutation is the GUID at offset `0x8a`.
|
|
|
|
// Direct byte indexing — see reference_handle.rs for rationale.
|
|
#![allow(clippy::indexing_slicing)]
|
|
|
|
/// Offset of the 16-byte item-correlation GUID inside the observed body
|
|
/// (`NmxMetadataQueryMessage.cs:5`).
|
|
pub const PRE_ADVISE_CORRELATION_OFFSET: usize = 0x8a;
|
|
|
|
/// Length of the first hex segment in bytes — `NmxMetadataQueryMessage.cs:10`.
|
|
const SEGMENT_1_LEN: usize = 160;
|
|
|
|
/// Length of the second hex segment in bytes — `NmxMetadataQueryMessage.cs:11`.
|
|
const SEGMENT_2_LEN: usize = 154;
|
|
|
|
/// Length of the observed body in bytes (160 + 154 = 314).
|
|
pub const PRE_ADVISE_BODY_LEN: usize = SEGMENT_1_LEN + SEGMENT_2_LEN;
|
|
|
|
/// First hex segment from `NmxMetadataQueryMessage.cs:10`. Decoded byte-for-byte
|
|
/// from `Convert.FromHexString(...)` of the literal in the .NET source.
|
|
const SEGMENT_1: [u8; SEGMENT_1_LEN] = [
|
|
0x17, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x71, 0x00, 0x0a, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x6a, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x81, 0x44, 0x00, 0x65,
|
|
0x00, 0x76, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x74, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72,
|
|
0x00, 0x6d, 0x00, 0x2e, 0x00, 0x47, 0x00, 0x52, 0x00, 0x2e, 0x00, 0x54, 0x00, 0x69, 0x00, 0x6d,
|
|
0x00, 0x65, 0x00, 0x4f, 0x00, 0x66, 0x00, 0x4c, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x44,
|
|
0x00, 0x65, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x79, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x01, 0xd0, 0xfc, 0x40, 0x09, 0x1f, 0x01, 0x00, 0xc0, 0xca, 0x9c, 0xcd, 0x32, 0x65,
|
|
0xb0, 0x46, 0xa5, 0x85, 0xa5, 0x83, 0xb2, 0xe7, 0x7a, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
];
|
|
|
|
/// Second hex segment from `NmxMetadataQueryMessage.cs:11`. Decoded
|
|
/// byte-for-byte from `Convert.FromHexString(...)` of the literal in the
|
|
/// .NET source.
|
|
const SEGMENT_2: [u8; SEGMENT_2_LEN] = [
|
|
0x17, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x71, 0x00, 0x0a, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x76, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x81, 0x44, 0x00, 0x65,
|
|
0x00, 0x76, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x74, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72,
|
|
0x00, 0x6d, 0x00, 0x2e, 0x00, 0x47, 0x00, 0x52, 0x00, 0x2e, 0x00, 0x54, 0x00, 0x69, 0x00, 0x6d,
|
|
0x00, 0x65, 0x00, 0x4f, 0x00, 0x66, 0x00, 0x4c, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x43,
|
|
0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x43, 0x00, 0x68, 0x00, 0x61,
|
|
0x00, 0x6e, 0x00, 0x67, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00,
|
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x50,
|
|
0x03, 0x41, 0x09, 0x20, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
|
|
];
|
|
|
|
/// Concatenation of `SEGMENT_1 || SEGMENT_2`. Equivalent to the result of
|
|
/// `Convert.FromHexString` on the joined hex literal at
|
|
/// `NmxMetadataQueryMessage.cs:10-11`.
|
|
const OBSERVED_PRE_ADVISE_BODY: [u8; PRE_ADVISE_BODY_LEN] = {
|
|
let mut out = [0u8; PRE_ADVISE_BODY_LEN];
|
|
let mut i = 0;
|
|
while i < SEGMENT_1_LEN {
|
|
out[i] = SEGMENT_1[i];
|
|
i += 1;
|
|
}
|
|
let mut j = 0;
|
|
while j < SEGMENT_2_LEN {
|
|
out[SEGMENT_1_LEN + j] = SEGMENT_2[j];
|
|
j += 1;
|
|
}
|
|
out
|
|
};
|
|
|
|
/// Stateless helpers around the observed metadata-query body.
|
|
///
|
|
/// Mirrors the static class `NmxMetadataQueryMessage`
|
|
/// (`NmxMetadataQueryMessage.cs:3-15`).
|
|
pub struct NmxMetadataQueryMessage;
|
|
|
|
impl NmxMetadataQueryMessage {
|
|
/// Encode the observed pre-advise body, patching the supplied 16-byte
|
|
/// GUID into offset `0x8a` (`NmxMetadataQueryMessage.cs:7-14`).
|
|
///
|
|
/// `item_correlation_id` is the raw 16-byte little-endian Guid layout —
|
|
/// the same byte order .NET's `Guid.TryWriteBytes` emits. Callers
|
|
/// constructing a Guid from Rust types are responsible for using the
|
|
/// same wire layout (e.g. `windows::core::GUID::to_u128_le().to_le_bytes()`
|
|
/// or equivalent).
|
|
pub fn encode_observed_pre_advise(item_correlation_id: [u8; 16]) -> Vec<u8> {
|
|
let mut body = OBSERVED_PRE_ADVISE_BODY.to_vec();
|
|
body[PRE_ADVISE_CORRELATION_OFFSET..PRE_ADVISE_CORRELATION_OFFSET + 16]
|
|
.copy_from_slice(&item_correlation_id);
|
|
body
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn body_length_is_314() {
|
|
// 160 + 154 = 314 bytes — derived from the two hex segments at
|
|
// `NmxMetadataQueryMessage.cs:10-11`.
|
|
assert_eq!(SEGMENT_1_LEN, 160);
|
|
assert_eq!(SEGMENT_2_LEN, 154);
|
|
assert_eq!(PRE_ADVISE_BODY_LEN, 314);
|
|
assert_eq!(OBSERVED_PRE_ADVISE_BODY.len(), 314);
|
|
}
|
|
|
|
// Compile-time bounds checks: clippy denies `assert!(<const expr>)` at
|
|
// runtime, so anchor these as `const _: () = assert!(...)` instead. They
|
|
// still fail the build if the constants drift — at compile time, before
|
|
// the test runner even spins up.
|
|
const _: () = assert!(PRE_ADVISE_CORRELATION_OFFSET + 16 <= PRE_ADVISE_BODY_LEN);
|
|
const _: () = assert!(PRE_ADVISE_CORRELATION_OFFSET + 16 <= SEGMENT_1_LEN);
|
|
|
|
#[test]
|
|
fn correlation_offset_is_0x8a() {
|
|
assert_eq!(PRE_ADVISE_CORRELATION_OFFSET, 0x8a);
|
|
// 0x8a (138) + 16 = 154, which is inside the first 160-byte segment.
|
|
// Anchor checks are above as `const _: () = assert!(...)`.
|
|
}
|
|
|
|
#[test]
|
|
fn observed_guid_in_template_matches_dotnet_capture() {
|
|
// The captured GUID at offset 0x8a in the literal body
|
|
// (`NmxMetadataQueryMessage.cs:10` — after the `0xc0` byte at offset 138).
|
|
let expected = [
|
|
0xc0, 0xca, 0x9c, 0xcd, 0x32, 0x65, 0xb0, 0x46, 0xa5, 0x85, 0xa5, 0x83, 0xb2, 0xe7,
|
|
0x7a, 0x5d,
|
|
];
|
|
assert_eq!(
|
|
&OBSERVED_PRE_ADVISE_BODY[0x8a..0x8a + 16],
|
|
&expected,
|
|
"the embedded GUID must match the .NET literal byte-for-byte"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn guid_is_patched_at_0x8a() {
|
|
let guid = [
|
|
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
|
|
0xff, 0x00,
|
|
];
|
|
let body = NmxMetadataQueryMessage::encode_observed_pre_advise(guid);
|
|
assert_eq!(body.len(), PRE_ADVISE_BODY_LEN);
|
|
assert_eq!(&body[0x8a..0x8a + 16], &guid);
|
|
}
|
|
|
|
#[test]
|
|
fn bytes_outside_correlation_window_are_unchanged() {
|
|
// Encode with an all-zero GUID and an all-0xff GUID, compare every
|
|
// byte outside the patch window — they must be identical.
|
|
let body_a = NmxMetadataQueryMessage::encode_observed_pre_advise([0u8; 16]);
|
|
let body_b = NmxMetadataQueryMessage::encode_observed_pre_advise([0xffu8; 16]);
|
|
for i in 0..PRE_ADVISE_BODY_LEN {
|
|
if (PRE_ADVISE_CORRELATION_OFFSET..PRE_ADVISE_CORRELATION_OFFSET + 16).contains(&i) {
|
|
continue;
|
|
}
|
|
assert_eq!(body_a[i], body_b[i], "byte {i} should be unchanged");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn encoded_body_matches_observed_template_at_known_offsets() {
|
|
// Spot-check anchor bytes from the .NET hex string. Offsets 0..10
|
|
// are the `17 01 00 01 01 00 01 00 00 00` header
|
|
// (`NmxMetadataQueryMessage.cs:10`); offset 160 starts the second
|
|
// segment with the same 10-byte preamble (`NmxMetadataQueryMessage.cs:11`).
|
|
let body = NmxMetadataQueryMessage::encode_observed_pre_advise([0u8; 16]);
|
|
let preamble = [0x17, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00];
|
|
assert_eq!(&body[0..10], &preamble);
|
|
assert_eq!(&body[SEGMENT_1_LEN..SEGMENT_1_LEN + 10], &preamble);
|
|
}
|
|
|
|
#[test]
|
|
fn fresh_call_does_not_mutate_template() {
|
|
// Each call must return an independent buffer — patching the result
|
|
// of one call must not affect a subsequent call.
|
|
let mut a = NmxMetadataQueryMessage::encode_observed_pre_advise([0u8; 16]);
|
|
a[0] = 0x99;
|
|
let b = NmxMetadataQueryMessage::encode_observed_pre_advise([0u8; 16]);
|
|
assert_eq!(b[0], 0x17, "second call must not see mutation of first");
|
|
}
|
|
}
|