Files
vnidrop/crates/vnidrop/AGENTS.md

125 lines
3.8 KiB
Markdown

# AGENTS.md — `crates/vnidrop` (Rust core)
Nearest guide when editing under `crates/vnidrop/`. Root [`AGENTS.md`](../../AGENTS.md)
still applies; this file wins for Rust-specific commands and conventions.
---
## Purpose
This crate is the transfer backend exposed to Kotlin via UniFFI (`VnidropCore`).
It owns Iroh, blobs, SQLite history, tickets, approval handshake, and file streaming.
Read [`CORE_FLOW.md`](CORE_FLOW.md) before changing send/receive/export/cancel.
---
## Commands (run from repo root)
Always prefer workspace commands so lockfile/fmt stay consistent:
```bash
make format
make test-rust
make check-rust
```
Focused integration suites:
```bash
make test-rust-transfer
make test-rust-approval
make test-rust-lifecycle
make test-rust-output-sink
```
Docs (CI uses `-D warnings`):
```bash
RUSTDOCFLAGS='-D warnings' cargo doc -p vnidrop --no-deps
```
Run `make format` after finishing Rust edits without asking.
---
## Module layout
```
src/
runtime/
mod.rs # CoreInner, startup recovery, emit helpers
facade.rs # UniFFI surface, block_on, cancel entry
share.rs # share / import
receive.rs # receive, download, export, OutputSinkFile
lifecycle.rs # cancel share, delete, status, access mode, shutdown
provider.rs # provider messages, per-peer transfer progress
filesystem.rs # collect sources, atomic publish, path rules
repository.rs # SQLite
approval.rs / handshake.rs / ticket.rs / access_policy.rs / event_hub.rs
api.rs # UniFFI records/enums
tests/ # crate-private unit tests
tests/ # public-API integration tests + support/
```
**Do not** reassemble a single huge `runtime.rs`. Prefer new focused modules if a
file approaches ~800 LoC of non-test code.
---
## Hard constraints
1. **Public API stability:** UniFFI surface changes break Kotlin. Prefer additive
changes; update shared Kotlin call sites in the same change when required.
2. **Streaming stays in Rust.** Platform passes paths or FDs; core does not pull
whole files into Kotlin.
3. **Android FDs are files only.** `SourceKind::FileDescriptor` with
`is_directory=true` must fail; directories are expanded on the platform side.
4. **Cancel:** signal active-transfer oneshot **synchronously** before async DB
work. Use existing `take_active_transfer` / facade cancel path. Do not reintroduce
nested exclusive `Runtime::block_on` deadlocks.
5. **No lock across await:** Clippy `await_holding_lock` fails CI.
6. **ReceiveOutputSink:** after successful `start_file`, exactly one of
`finish_file` or `abort_file` (see `OutputSinkFile` Drop).
7. **No-overwrite publish** for path receives (temp + hard link / exclusive rename).
8. Integration tests must use the **public** API + `tests/support/` only.
---
## Code style (Rust)
- Clippy clean with `-D warnings`.
- Prefer exhaustive `match`; avoid catch-all arms that hide new enum variants.
- Prefer comparing whole objects in tests when practical.
- Comment only non-obvious why (concurrency, durability, platform FS quirks).
- Do not add one-off private helpers used once if inline is clearer.
- Prefer private modules; export deliberately via `lib.rs` / UniFFI.
---
## Testing
- Unit / private: `src/tests/` (see crate `tests.rs` paths).
- Integration: `tests/*.rs` + `tests/support/mod.rs` (`TestNode`, `MemoryOutputSink`,
`CoreGuard`, etc.).
- Bug fixes need a regression test.
- Prefer gates/latches over multi-second sleeps (see output_sink cancel test).
- Failure tests: durable status and/or events when applicable.
- Recovery tests: shut down core, reopen same data dir.
Details: [`tests/README.md`](tests/README.md).
---
## PR / verify checklist for this crate
```bash
make check-rust
```
If you touched cancel, export, or sinks, also:
```bash
make test-rust-output-sink
```