feat: Rust bindings (sys crate + safe wrapper) over the native C ABI (LEO-325) (#19)
* feat: Rust bindings (sys crate + safe wrapper) over the native C ABI (LEO-325) Add idiomatic Rust bindings over the sw_* C ABI as a two-crate workspace under rust/, mirroring the shipped Python (cffi) and C++ (header-only) bindings. sensorwatch-sys (raw FFI): - build.rs compiles the C core straight into the crate via cc with -DSW_STATIC (single artifact, no separate DLL — sidesteps the SECURITY.md 2.1 search-order risk). Compiled on all platforms, so the safe crate links everywhere and returns UnsupportedPlatform off Windows rather than failing to link. - FFI declarations are pre-generated with bindgen and checked in (src/bindings.rs), so building needs only a C compiler, never libclang. A CI bindgen-drift job regenerates them (pinned bindgen 0.72.1 + libclang 18.1.1) and git-diffs the result, so header drift fails CI. sensorwatch (safe wrapper): - Move-only Session/Snapshot freed by Drop (ownership makes close/free exactly-once automatic, no double-free). Reading value type; ReadingType enum folding unrecognized categories to Unknown; Error enum carrying the sw_error_t code + sw_error_string text, with every non-SW_OK surfaced as Result::Err. Session::new() verifies the ABI version, then opens the source. CI: a rust job (build/clippy/fmt/test on ubuntu + windows — ubuntu asserts UnsupportedPlatform, windows runs a live-snapshot test that self-skips when HWiNFO is down) plus the bindgen-drift job. Docs: README Rust section, C_ABI.md "Binding Notes -> Rust" flipped from sketch to shipped, SECURITY.md mitigation register + header comment updated. crates.io publishing is split into a follow-up (LEO-326). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: address PR #19 review round 1 (Send/Sync, NaN equality, MSRV CI) - Reading: NaN-reflexive PartialEq (manual impl + scalar_eq), so a Reading carrying a NaN scalar equals itself -- mirrors the C++ binding and de-flakes the live-snapshot assert_eq! checks (kimi #2). - Snapshot: `unsafe impl Send + Sync` -- the snapshot owns an immutable copy and the ABI documents concurrent queries on a live snapshot as safe, so it can be shared across threads. Session: `unsafe impl Send` (kept !Sync -- same-session use must be synchronized, snapshot() is &mut self). Both were conservatively !Send/!Sync before (a raw pointer is unconditionally !Send/!Sync regardless of pointee); these restore the thread-safety the ABI actually guarantees. Docs and a compile-time marker test updated to match; the misleading "!Send Snapshot" test comment fixed (Copilot inline, kimi #1/#3, claude). - query_string: treat a length-query needed==0 (an ABI contract violation) as CorruptData instead of re-calling with a zero-length buffer (kimi #6). - CI: new rust-msrv job (cargo check on 1.82, the declared floor) so the MSRV is verified not just declared (kimi #5, claude); cache bindgen-cli in the bindgen-drift job to skip the ~2-3 min rebuild on cache hits (kimi #4). Verified locally: fmt/clippy (-D warnings) clean, 7 integration tests + doctest pass, and `cargo +1.82.0 check --workspace --all-targets --locked` passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Leonard Janke <lcjanke2020@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
A
Agent for Leonard Janke committed
774cfcfaa9cd3523c1b6694aac740c89488b1f1f
Parent: 521dc89
Committed by GitHub <noreply@github.com>
on 7/1/2026, 6:27:40 PM