8.5 KiB
8.5 KiB
Plan — build features
Executes architecture/build-features.md against quality/build-features.md.
Ordered by dependency; each task names how it is verified. Three commits:
(A) the mount-registry refactor (no feature change), (B) the features
themselves, (C) packaging + docs.
Build commands run through devenv with the session-local target dir:
devenv shell -- bash -lc 'CARGO_TARGET_DIR="$(pwd)/target-claude" cargo … '
A — Mount registry (behaviour-preserving refactor)
- A1 —
Mount+ registry types. Inprovider.rs:struct Mount { root: &'static str, name: &'static str, client: Arc<dyn ProviderClient> },Mount::new(root, name, Arc<impl ProviderClient>) -> Mount(theArccoerces at the call site), andMount::owns(&self, path) -> boolmatchingpath == root || path.starts_with("<root>/")— so/fsxis not/fs. Verifies:mount_owns_its_root_and_children_only(G10). - A2 —
ProviderOrchestratorholdsmounts: Vec<Mount>. Replace the nine*_clientfields (keepcrabidy_store). Addfrom_mounts(Vec<Mount>) -> Self(sorts the mounts crabidy-first / orphans-last / rest alphabetical, and creates the bounded channel), used bybuildand the tests. Verifies:root_lists_mounted_providers_in_order(G12). - A3 — One dispatch helper.
owner(&self, path) -> Option<&dyn ProviderClient>plusowner_or(&self, path, err)that warns with the same message and returns the typed error. Delete the nine*_ownsfunctions and the nine*_provideraccessors. Verifies:unowned_paths_are_typed_errors(G11). - A4 — Rewrite the eight dispatch methods (
is_track_path,get_urls_for_track,get_metadata_for_track,get_lib_node,create_lib_node,rename_lib_node,delete_lib_node,resolve_tracks_into) as a single lookup each, preserving: the synthetic root short-circuit andannotate_capturedinget_lib_node,MalformedPathfor lookups,NotSupportedfor mutations, and thewarn!s. Verifies:dispatch_reaches_the_owning_mount,overridden_resolve_tracks_into_is_dispatched,no_mounts_serves_an_empty_root(G10, G11, G13). - A5 —
get_lib_rootmaps the sorted mounts toLibraryNodeChild::new(no per-provider block, no re-sort). Verifies:root_lists_mounted_providers_in_order(G12). - A6 —
build()registers mounts. Each provider's init block stays as is; on success it pushes aMountinstead of assigning a field./crabidyand/orphanspush after the store exists. Verifies: full test suite +cargo clippy -- -D warnings(G2). - A7 — Commit A.
cargo test -p crabidy-server, clippy, fmt all clean; noCargo.tomlchange in this commit (G1/G2 hold trivially).
B — The features
- B1 — Manifests (done in api-design; re-verify):
crabidy-server(default = all-providers + opus + spectrum + web-ui, gated deps optional),audio-player(opus),cbd-tui(notifications),cbd(pass-through of all of them). Verifies: G4, G5;cargo tree -p crabidy-serverat defaults matches the pre-change tree (G1). - B2 —
settings:BUILT_IN_PROVIDERS,provider_enabledbounded by it,unavailable_providers(),ensure_defaultwriting only built-ins. Replace thetodo!(). Verifies:an_absent_providers_key_enables_every_built_in_provider,unavailable_providers_reports_built_out_and_unknown_names,ensure_default_lists_only_built_in_providers,a_built_out_provider_cannot_be_enabled(G4, G25, G26). - B3 — Per-provider
#[cfg]inbuild()only. Wrap each provider's init+registration block in#[cfg(feature = "…")]; theenabled.*toggle read stays inside the block. Provideruse/imports move to the same gate. Verifies: the matrix (B12) compiles each provider alone; G9 by reading. - B4 —
fs: gate the store half.#[cfg(feature = "fs")]on thecrabidy_store,capture, andorphansmodules (lib.rs), onProviderOrchestrator::crabidy_store/its field, onPlayback's store field andrestore_currentand the persister, and on the/fs,/crabidy,/orphansregistrations.Playback::newkeeps its arity — the store parameter becomes#[cfg]-free by threadingOption<Arc<CrabidyStore>>only under the feature (adjust the two call sites inlib.rsand the playback tests). Verifies: G14, G17;--no-default-featuresbuild (B12). - B5 —
fs: RPC degradation. Withoutfs,CaptureLibraryNodeandSaveQueuereturnStatus::unimplementednaming the feature; withfs, unchanged. No panic, no hang. Verifies: G15 by reading + a--no-default-featuressmoke run. - B6 —
fs: thescancommand. GateAUDIO_EXTENSIONS,is_audio_file,scan,scan_dir,scan_fileand their tests behindfs; without it the dispatcher incrabidy-server/src/main.rsandcbd/src/main.rsprints "this binary was built without thefsfeature" and exits non-zero. The clap surface does not change. Verifies: G16, G9. - B7 —
opusinaudio-player. Gatemod opus_sourceand theis_ogg_opussniff inplayer_engine::build_source; without the feature an Ogg-Opus header producesErrnaming the missing feature (the existing "failed to decode" path), never a panic. Verifies: G18, G19 (read + theopus-off matrix entry);opus_source's own tests stay under the feature. - B8 —
opusinscan.AUDIO_EXTENSIONSincludes"opus"only under the feature. Verifies:opus_is_scannable_only_with_the_decoder,scan_walks_past_opus_files_it_cannot_play(G20). - B9 —
spectrum. Gatemod spectrum,spawn_spectrum_task, and its call;realfftoptional.Player::spectrum_tap()unchanged. Verifies: G21; thespectrum-off matrix entry. - B10 —
notificationsincbd-tui. Gate thenotify_rustimport and the notify call inapp/now_playing.rs; the config key stays accepted so an existingcbd-tui.tomlstill parses. Verifies: G23;cbd-tui --no-default-featuresbuild +cargo test -p cbd-tui. - B11 —
featurescommand.cli::build_features()+cli::features()(replace thetodo!()s), wired intoServerCommand::FeaturesandCbdCommand::Features; plus oneinfo!line at startup listing the build, and onewarn!perunavailable_providers()entry. Verifies:build_features_lists_providers_then_extras(G24, G25). - B12 — The build matrix as a devenv script.
check-featuresrunscargo clippy -- -D warnings(andcargo testwhere meaningful) over: defaults ·--no-default-features· each of the seven providers alone · defaults-minus-opus· minus-spectrum· minus-web-ui·-p cbd-tui --no-default-features·-p audio-player --no-default-features. Addcargo-hackto devenv for ad-hoc deeper sweeps. Verifies: G6, G7. - B13 — Commit B once the whole matrix is green and no
todo!()/unimplemented!()from the stub stage remains (G28).
C — Packaging and docs
- C1 —
flake.nix. Native build: replace the bare--no-default-featureswith an explicit--no-default-features --features all-providers,opus,spectrum; leave the aarch64 cross build on defaults (web-uion). Verifies: G8;nix build .#crabidyif the sandbox allows, otherwise read + the equivalent cargo invocation. - C2 —
docs/src/build-features.md— the feature table, what each one costs to lose, the two worked examples (local-files appliance:--no-default-features --features fs,opus; streaming box:--no-default-features --features tidal,web-ui,opus,spectrum), the compile-time vs runtime distinction, and thefeaturescommand. Linked fromSUMMARY.md;docs/src/config.mdcross-references it from theproviderskey. Verifies: G27 +mdbook build. - C3 —
README.mdgains a short "tailored builds" pointer. Verifies: G27. - C4 —
plan/summary.mdentry describing what shipped, the decisions taken autonomously, and what stayed out (auth, HLS, spectrum tap — with the reason). Verifies: dev-flow convention. - C5 — Commit C.
Deferred / not done (recorded, not silently dropped)
- Gating
[auth]/argon2— refused (fail-open security risk, D9). - Gating
hls.rs,windowed_http.rs,spectrum_tap.rs— no dependency payoff (D7, D8). - A
minimalconvenience feature — users compose their own set (D1). - CI enforcement of the matrix — there is no CI yet;
check-featuresis the manual gate.