The NAHPU app uses Rust through Flutter Rust Bridge. The app’s rust/ crate is the rust_lib_nahpu wrapper crate: it exposes bridge-facing functions and types, while reusable implementations live in the NAHPU API workspace.
Use Data import and export to see which workflows cross this boundary and which remain Dart-owned.
What the API layer means
Section titled “What the API layer means”In the app repository:
rust/src/api/*.rsdefines bridge-facing wrapper modules.lib/src/rust/contains generated Dart bindings.rust/Cargo.tomldeclares the API crates linked into the app.flutter_rust_bridge_codegen generateregenerates bridge code after wrapper API changes.cargo checkandcargo clippyverify the Rust wrapper and its integration.
The reusable Rust logic comes from these NAHPU API crates:
| Crate | Used for |
|---|---|
nahpu_archive | ZIP, tar.gz, and gzip archive creation and extraction |
nahpu_configs | redb storage and export/import for user configs, record export presets, template presets, and document layouts |
nahpu_db | Drift-derived Rust record models plus CSV, TSV, Excel, and JSON import/export helpers. Carries the mirrored tables.drift, so a schema change bumps it |
nahpu_dp | Reproducible NAHPU Data Package planning, validation, and writing |
nahpu_dwc | Darwin Core mappings, Darwin Core Archive, and Darwin Core Data Package bundles |
nahpu_export | Markdown/Typst document rendering and PDF compilation |
nahpu_gis | Coordinate conversion, GIS import/export, and vector-layer normalization |
Building against an unpublished API
Section titled “Building against an unpublished API”A schema or mapper change usually lands in nahpu_api before the affected crates are published to crates.io. Until they are, rust/Cargo.toml resolves them from a sibling checkout through [patch.crates-io]:
[patch.crates-io]nahpu_db = { path = "../../../Rust/nahpu_api/crates/nahpu_db" }nahpu_dwc = { path = "../../../Rust/nahpu_api/crates/nahpu_dwc" }nahpu_export = { path = "../../../Rust/nahpu_api/crates/nahpu_export" }The path is relative to rust/Cargo.toml, so the nahpu_api checkout must sit beside the Flutter repository as Rust/nahpu_api. A contributor without that layout cannot build the app while the section is active.
Three rules keep the patch working:
- Raise the version requirement in
[dependencies]as well. Cargo only applies a patch whose local version satisfies the requirement, so a requirement left on the published version silently ignores the patch. - Patch every crate that shares a patched type, not only the one you changed. Patching
nahpu_dbalone whilenahpu_gisstill comes from the registry links two copies ofnahpu_archive, and types stop matching across the boundary. - Bump the API crate before patching it. The patched version must differ from the published one, or the local build and a released build resolve to different code under the same number.
Re-comment each entry as its crate is published, and remove the section once none are left. Cargo.lock records a path dependency with no source field, which is the quickest way to confirm which crates are local.
Data ownership
Section titled “Data ownership”The API does not own every kind of persisted app data. Project records remain in the Flutter app’s Drift-managed SQLite database, while configuration, installation-local state, and files follow separate ownership boundaries. See the canonical Persistence data page for the complete storage model and the NAHPU-to-Darwin Core mapping.
Wrapper modules
Section titled “Wrapper modules”The app exposes Rust behavior through rust/src/api/:
| Module | Purpose |
|---|---|
archive.rs | ZIP and tar.gz writer/extractor wrappers |
common.rs | Bridge initialization and shared sanity helpers |
config.rs | nahpu_configs models and configuration/preset operations |
document.rs | Markdown-to-Typst conversion and Typst-to-PDF compilation |
dwc.rs | Darwin Core header mapping and bundle plan/validate/write operations |
export.rs | General record and tabular export wrappers |
gis.rs | Coordinate conversion, coordinate exchange, and vector-layer wrappers |
import.rs | Delimited and Excel record-reading wrappers |
nahpu_dp.rs | NAHPU Data Package plan/validate/write operations |
Generated files under lib/src/rust/ and rust/src/frb_generated.rs must not be hand-edited. Change the wrapper API and regenerate the bindings instead.
When to edit the app wrapper
Section titled “When to edit the app wrapper”Edit rust/src/api/ when you need to:
- expose an existing API-crate function to Dart;
- convert Dart-friendly inputs into crate-friendly Rust inputs;
- map Rust errors into bridge-friendly result types;
- add simple structs or enums that Flutter Rust Bridge can serialize.
Do not put durable domain logic in the wrapper. If behavior should be reused outside the Flutter app, or is a core parser, exporter, validator, converter, or package writer, implement it in the appropriate NAHPU API crate first.
Boundary rules
Section titled “Boundary rules”- Keep wrapper functions and conversions small.
- Prefer
Result<T, String>or another bridge-friendly result over panics. - Avoid
.unwrap()unless the invariant is local and documented. - Prefer primitive values, strings, byte lists, and simple structs at the Dart boundary.
- Keep generated code out of manual edits.
- Regenerate bindings after changing any bridge-facing Rust signature.
Adding a bridge API
Section titled “Adding a bridge API”Implement or update reusable behavior in the relevant
nahpu_apicrate.Add a wrapper function or type in the appropriate
rust/src/api/*.rsfile.Export a new wrapper module from
rust/src/api/mod.rswhen necessary.Regenerate Flutter Rust Bridge bindings.
Terminal window flutter_rust_bridge_codegen generateUse the generated binding from the relevant Dart service.
Add focused tests for the Rust behavior, Dart service, or integration path.
Verify the change.
Terminal window flutter analyzeflutter testcargo checkcargo clippy