mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-21 15:02:21 +02:00
Add rust_backend/README.md: crate map and dependencies, vendored patches, toolchain, host and Android/iOS build commands (all pass --locked; none pass --offline), NDK version and ABIs, UniFFI binding outputs and consumers, and the test layout. Derived from rust_backend/Cargo.toml, rust-toolchain.toml, crate Cargo.toml files, uniffi.toml, the scripts/ build and check scripts, android/app/build.gradle.kts, ios/Podfile and .github/workflows/ci.yml.
155 lines
7.5 KiB
Markdown
155 lines
7.5 KiB
Markdown
# Rust Backend
|
|
|
|
The Rust workspace is the app's only backend on Android and iOS. Flutter
|
|
reaches it through the native channel (see `android/README.md`); Kotlin and
|
|
Swift call it through UniFFI bindings generated from `spotiflac-mobile`.
|
|
|
|
The toolchain is pinned in `rust_backend/rust-toolchain.toml` (Rust 1.99.0
|
|
with rustfmt and clippy). The workspace uses edition 2024, resolver 3, and
|
|
denies `unsafe_code` as a workspace lint, with a few audited local
|
|
`#[allow(unsafe_code)]` exceptions in the `extensions` and `mobile` crates.
|
|
|
|
Comments that mention "Go" describe the retired Go backend. Its observable
|
|
behavior (wire formats, parsing quirks, file layouts) remains a compatibility
|
|
contract.
|
|
|
|
## Crate Map
|
|
|
|
Dependencies point downward: `core` and `network` depend on no other
|
|
workspace crate.
|
|
|
|
| Crate | Package | Depends on |
|
|
| --- | --- | --- |
|
|
| `crates/core` | `spotiflac-core` | none |
|
|
| `crates/network` | `spotiflac-network` | none |
|
|
| `crates/providers` | `spotiflac-providers` | core, network |
|
|
| `crates/extensions` | `spotiflac-extensions` | core, network, providers |
|
|
| `crates/mobile` | `spotiflac-mobile` | core, network, extensions |
|
|
| `crates/bindgen` | `spotiflac-bindgen` | `uniffi` CLI only |
|
|
|
|
**core.** Shared backend logic, independent of mobile bindings and UI
|
|
runtimes. Modules cover filenames, tags and metadata, covers and palette
|
|
extraction, CUE sheets, lyrics, ISRC, matching, download progress state,
|
|
Hi-Res analysis and JSON or XML helpers. It uses `image`, `claxon` (FLAC
|
|
decoding for the Hi-Res check), `roxmltree` and pinned Unicode tables.
|
|
|
|
**network.** Shared, cancellable HTTP transport for the embedded backend: the
|
|
client, cookies, DNS lookup, TLS roots, request policy, query and URL
|
|
handling. It is built on `hyper-util`, `hyper-rustls` and `tokio`; Apple
|
|
targets verify certificates with `rustls-platform-verifier`, other targets
|
|
with `rustls-native-certs`.
|
|
|
|
**providers.** Built-in provider clients and shared lookup services for the
|
|
native app owners: Deezer, MusicBrainz, lyrics, the resolver, and a request
|
|
coalescer.
|
|
|
|
**extensions.** Extension JavaScript execution and host services, isolated
|
|
from mobile UI code. It embeds QuickJS through `rquickjs` and provides the
|
|
host APIs (network, files, crypto, FFmpeg, auth, sessions, storage), the
|
|
manifest and package format, the repository, signed sessions and the
|
|
download backend. Its main types are `ExtensionRuntime` and
|
|
`ExtensionServices`.
|
|
|
|
**mobile.** The UniFFI mobile API (`uniffi::setup_scaffolding!`). This crate
|
|
is the only backend and routes the app on both platforms. It is built as
|
|
`lib`, `cdylib` (Android `libspotiflac_mobile.so`) and `staticlib` (iOS).
|
|
It adds native data jobs, the native library, cache, collections, playlists
|
|
and backup, lyrics, ID3 and network tags, `rusqlite`, and USB audio
|
|
(`libusb1-sys`, Android only).
|
|
|
|
**bindgen.** The UniFFI bindgen command line (`uniffi_bindgen_main`), used
|
|
only to generate the Kotlin and Swift bindings. It enables the `cli` and
|
|
`cargo-metadata` features of `uniffi`; the build script builds the library
|
|
in a separate cargo call so those features do not reach the shipped library.
|
|
|
|
### Vendored Patches
|
|
|
|
`Cargo.toml` patches two crates.io packages with copies in
|
|
`rust_backend/vendor/`: `rustix` 1.1.5 (Android `statx` fallback) and
|
|
`libusb1-sys` 0.7.0 (libusb 1.0.30 sources). The reasons and the upgrade
|
|
checks are in `rust_backend/vendor/README.md`. Third-party notices are in
|
|
`rust_backend/NOTICE`.
|
|
|
|
## Building
|
|
|
|
| Command | Result |
|
|
| --- | --- |
|
|
| `bash scripts/build_rust_backend.sh host` | Release host library and Kotlin and Swift bindings |
|
|
| `bash scripts/build_rust_backend.sh android` | Host step, then `libspotiflac_mobile.so` for each ABI |
|
|
| `bash scripts/build_rust_backend.sh ios` | Host step, then the XCFramework and CocoaPods staging |
|
|
| `bash scripts/check_rust_backend.sh` | `cargo fmt --check`, `cargo clippy -D warnings`, `cargo test` |
|
|
|
|
Every `cargo build`, `cargo run`, `cargo clippy` and `cargo test` call in
|
|
both scripts passes `--locked`, so `Cargo.lock` must be up to date. The
|
|
scripts do not pass `--offline`; dependencies come from crates.io except the
|
|
vendored patches.
|
|
|
|
The host step always runs first. It builds `spotiflac-mobile` in release
|
|
mode, then runs `spotiflac-bindgen generate` against the host library for
|
|
Kotlin and Swift. Output goes to `rust_backend/target/`:
|
|
|
|
- `target/release/`: host library (`.dylib` on macOS, `.so` on Linux)
|
|
- `target/bindings/kotlin/`, `target/bindings/swift/`: generated bindings
|
|
- `target/android/jniLibs/<abi>/`: Android libraries
|
|
- `target/ios/`: the XCFramework, Swift binding and podspec for CocoaPods
|
|
|
|
### Android
|
|
|
|
Android Gradle runs `scripts/build_rust_backend.sh android` in its
|
|
`buildRustBackend` task before `preBuild`, so a normal Flutter build is
|
|
enough. For a manual build:
|
|
|
|
- Set `ANDROID_NDK_HOME` to NDK 30.0.16248370 (the version CI installs in
|
|
`.github/workflows/ci.yml` and `android/app/build.gradle.kts` uses).
|
|
- `SPOTIFLAC_RUST_ANDROID_ABIS` selects `arm64-v8a`, `armeabi-v7a` or both
|
|
(comma-separated; the default is both).
|
|
- Libraries link against API 24 with 16 KB page alignment.
|
|
|
|
`bash scripts/check_android_statx.sh arm64-v8a` checks the vendored Rustix
|
|
fallback on a connected device (see `rust_backend/vendor/README.md`).
|
|
|
|
### iOS
|
|
|
|
On macOS, `bash scripts/build_rust_backend.sh ios` builds
|
|
`aarch64-apple-ios`, `aarch64-apple-ios-sim` and `x86_64-apple-ios` (iOS 16.0
|
|
deployment target), merges the two simulator slices, and stages
|
|
`target/ios/` with `rust_backend/SpotiFLACBackend.podspec`. Run
|
|
`(cd ios && pod install)` afterwards; `ios/Podfile` refuses to install
|
|
without the staged files.
|
|
|
|
## Where the Bindings Are Used
|
|
|
|
The binding names come from `rust_backend/crates/mobile/uniffi.toml`.
|
|
|
|
| Platform | Generated | Consumed by |
|
|
| --- | --- | --- |
|
|
| Android | Kotlin package `com.spotiflac.backend`, loading `spotiflac_mobile` through JNA | `android/app/build.gradle.kts` adds `target/bindings/kotlin` and `target/android/jniLibs` as source dirs; used by `android/app/src/rust/kotlin/com/zarz/spotiflac/SelectedCoreBackend.kt` and its neighbors |
|
|
| iOS | Swift module `SpotiFLACBackend` with FFI module `SpotiFLACBackendFFI` | `ios/Podfile` pod `SpotiFLACBackend` at `../rust_backend/target/ios`; imported by `ios/Runner/CoreBackend.swift` |
|
|
|
|
The generated files are build output under `target/` and are not tracked.
|
|
After changing an exported function, rebuild for the platform you test; the
|
|
Kotlin and Swift call sites must match the new signatures.
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
bash scripts/check_rust_backend.sh
|
|
(cd rust_backend && cargo test --locked -p spotiflac-core)
|
|
(cd rust_backend && cargo test --locked -p spotiflac-mobile network_tags --lib)
|
|
```
|
|
|
|
- Unit tests sit in `#[cfg(test)] mod tests` blocks. Larger modules keep them
|
|
in a sibling `tests.rs` declared with `mod tests;`, for example
|
|
`rust_backend/crates/mobile/src/native_library/tests.rs` and
|
|
`rust_backend/crates/extensions/src/manager/tests.rs`.
|
|
- Cargo integration tests live in `rust_backend/crates/core/tests/` and
|
|
`rust_backend/crates/extensions/tests/`.
|
|
- `rust_backend/crates/core/src/metadata/musicbrainz/case_data.rs` is a frozen
|
|
generated table (Unicode 15.0.0 ranges and case mappings for MusicBrainz
|
|
title casing, kept for parity with the Go backend). It is marked
|
|
`DO NOT EDIT`; the generator is no longer in the repository, and it must not
|
|
be regenerated from newer tables.
|
|
- CI runs `scripts/check_rust_backend.sh` on Linux, the iOS build on macOS,
|
|
and the Android build with Kotlin unit tests (`gradle -p android
|
|
:app:assembleDebug :app:testDebugUnitTest`).
|