From 955c7ea5b42eb7d09aa9ccb28af0bb910aa98ed8 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 21 Jul 2026 02:05:54 +0200 Subject: [PATCH] Rename the track-file extension to .cbd-track.toml .track.toml was too generic for files that only crabidy understands; the cbd- prefix makes them unmistakable. Co-Authored-By: Claude Fable 5 --- architecture/fs-provider.md | 12 ++++----- fsdy/src/lib.rs | 52 ++++++++++++++++++------------------- plan/summary.md | 5 +++- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/architecture/fs-provider.md b/architecture/fs-provider.md index 318ef15..4227780 100644 --- a/architecture/fs-provider.md +++ b/architecture/fs-provider.md @@ -81,9 +81,9 @@ Consequences, accepted deliberately: resolution. **Link chains are structurally impossible** — see D3's "no links into `/fs`" rule. -### D3 — On-disk schema: TOML, extension `.track.toml`, exactly one playable +### D3 — On-disk schema: TOML, extension `.cbd-track.toml`, exactly one playable -TOML per project convention. A file named `.track.toml` inside +TOML per project convention. A file named `.cbd-track.toml` inside the configured root is a track node; everything else (other files, hidden entries) is ignored. Schema: @@ -174,7 +174,7 @@ direction: right disk: Local disk { shape: cylinder - tree: "root dir: dirs, *.track.toml" + tree: "root dir: dirs, *.cbd-track.toml" } server: crabidy-server { @@ -185,7 +185,7 @@ server: crabidy-server { } fsdy: fsdy::Client { - parse: "parse + validate .track.toml" + parse: "parse + validate .cbd-track.toml" map: "path <-> root-relative file (encoded segments)" } @@ -218,7 +218,7 @@ pb -> orch: ResolveTracks("/fs/mix", chunk_tx) orch -> fs: resolve_tracks_into (spawned) fs -> fs: "list dir, parse 3 track files" fs -> pb: "chunk of 3 Tracks (paths below)" {style.bold: true} -pb -> orch: "GetTrackUrls(/fs/mix/a.track.toml)" +pb -> orch: "GetTrackUrls(/fs/mix/a.cbd-track.toml)" orch -> fs: get_urls_for_track fs -> pb: "[/home/u/Music/a.flac]" pb -> orch: "GetTrackUrls(/tidal/...) # link track, rewritten path" @@ -241,4 +241,4 @@ path.) - **Metadata drift** on link tracks (file says X, target now titled Y): accepted; the file is the user's curated metadata. - Open (future): tag-reading for bare audio files; multiple roots; a - `%`-style creator that writes a `.track.toml` from inside the TUI. + `%`-style creator that writes a `.cbd-track.toml` from inside the TUI. diff --git a/fsdy/src/lib.rs b/fsdy/src/lib.rs index 5b348f4..9623c9a 100644 --- a/fsdy/src/lib.rs +++ b/fsdy/src/lib.rs @@ -28,7 +28,7 @@ pub const PROVIDER_ROOT: &str = "/fs"; /// Files with this suffix are serialized track nodes; everything else in /// the tree is ignored. -pub const TRACK_FILE_SUFFIX: &str = ".track.toml"; +pub const TRACK_FILE_SUFFIX: &str = ".cbd-track.toml"; /// Provider settings, persisted as `fsdy.toml` next to the other crabidy /// config files. @@ -60,7 +60,7 @@ pub enum TrackFileError { LinkIntoFs(String), } -/// The on-disk schema of a `*.track.toml` file. See +/// The on-disk schema of a `*.cbd-track.toml` file. See /// `architecture/fs-provider.md` (D3) for the format documentation. #[derive(Debug, Deserialize)] pub struct TrackFile { @@ -214,7 +214,7 @@ impl Client { } /// Lists a directory as a library node: subdirectories become queueable - /// child nodes, `*.track.toml` files become tracks, each list sorted + /// child nodes, `*.cbd-track.toml` files become tracks, each list sorted /// case-insensitively by file name. /// /// Symlinks, hidden entries (dot-prefixed), non-UTF-8 names, and track @@ -552,7 +552,7 @@ mod tests { "relative link", ), ( - "title = \"x\"\n[playable]\nlink = \"/fs/other.track.toml\"\n", + "title = \"x\"\n[playable]\nlink = \"/fs/other.cbd-track.toml\"\n", "link into /fs", ), ("title = \"x\"\nnot toml at all [", "invalid toml"), @@ -569,7 +569,7 @@ mod tests { #[test] fn to_track_rewrites_the_path_only_for_links() { - let lib_path = "/fs/mix/song.track.toml"; + let lib_path = "/fs/mix/song.cbd-track.toml"; let linked = TrackFile::parse( "title = \"t\"\nartist = \"a\"\n[playable]\nlink = \"/tidal/artists/1/2\"\n", ) @@ -590,9 +590,9 @@ mod tests { #[tokio::test] async fn track_paths_need_the_suffix_and_the_provider_prefix() { let (client, _dir) = client_with_root().await; - assert!(client.is_track_path("/fs/mix/song.track.toml")); + assert!(client.is_track_path("/fs/mix/song.cbd-track.toml")); assert!(!client.is_track_path("/fs/mix")); - assert!(!client.is_track_path("/tidal/song.track.toml")); + assert!(!client.is_track_path("/tidal/song.cbd-track.toml")); assert!(!client.is_track_path("/fs")); } @@ -622,10 +622,10 @@ mod tests { fs::create_dir(dir.path().join("b-dir")).expect("mkdir"); fs::create_dir(dir.path().join("A-dir")).expect("mkdir"); fs::create_dir(dir.path().join(".hidden-dir")).expect("mkdir"); - write_track(dir.path(), "b song.track.toml", &url_track("b")); - write_track(dir.path(), "A song.track.toml", &url_track("a")); - write_track(dir.path(), "broken.track.toml", "not [ valid"); - write_track(dir.path(), ".hidden.track.toml", &url_track("h")); + write_track(dir.path(), "b song.cbd-track.toml", &url_track("b")); + write_track(dir.path(), "A song.cbd-track.toml", &url_track("a")); + write_track(dir.path(), "broken.cbd-track.toml", "not [ valid"); + write_track(dir.path(), ".hidden.cbd-track.toml", &url_track("h")); fs::write(dir.path().join("cover.jpg"), b"jpg").expect("write"); #[cfg(unix)] std::os::unix::fs::symlink(dir.path().join("b-dir"), dir.path().join("z-link")) @@ -641,7 +641,7 @@ mod tests { let tracks: Vec<&str> = node.tracks.iter().map(|t| t.title.as_str()).collect(); assert_eq!(tracks, vec!["a", "b"]); // Track paths are encoded segments under the node's path. - assert_eq!(node.tracks[0].path, "/fs/A%20song.track.toml"); + assert_eq!(node.tracks[0].path, "/fs/A%20song.cbd-track.toml"); } #[tokio::test] @@ -658,7 +658,7 @@ mod tests { async fn nodes_link_back_to_their_parent() { let (client, dir) = client_with_root().await; fs::create_dir_all(dir.path().join("a/b")).expect("mkdir"); - write_track(&dir.path().join("a/b"), "t.track.toml", &url_track("t")); + write_track(&dir.path().join("a/b"), "t.cbd-track.toml", &url_track("t")); let node = client.get_lib_node("/fs/a/b").await.expect("node"); assert_eq!(node.parent.as_deref(), Some("/fs/a")); assert_eq!(node.title, "b"); @@ -675,28 +675,28 @@ mod tests { fs::create_dir(&sub).expect("mkdir"); write_track( &sub, - "rel.track.toml", + "rel.cbd-track.toml", "title = \"r\"\n[playable]\nfile = \"a.flac\"\n", ); let abs_target = dir.path().join("elsewhere.mp3"); write_track( &sub, - "abs.track.toml", + "abs.cbd-track.toml", &format!( "title = \"a\"\n[playable]\nfile = {:?}\n", abs_target.to_str().expect("utf8") ), ); - write_track(&sub, "web.track.toml", &url_track("w")); + write_track(&sub, "web.cbd-track.toml", &url_track("w")); write_track( &sub, - "linked.track.toml", + "linked.cbd-track.toml", "title = \"l\"\n[playable]\nlink = \"/tidal/artists/1/2\"\n", ); // Relative files resolve against the track file's directory. let urls = client - .get_urls_for_track("/fs/mix/rel.track.toml") + .get_urls_for_track("/fs/mix/rel.cbd-track.toml") .await .expect("relative file"); assert_eq!( @@ -705,20 +705,20 @@ mod tests { ); // Absolute files pass through. let urls = client - .get_urls_for_track("/fs/mix/abs.track.toml") + .get_urls_for_track("/fs/mix/abs.cbd-track.toml") .await .expect("absolute file"); assert_eq!(urls, vec![abs_target.to_str().expect("utf8").to_string()]); // URLs pass through. let urls = client - .get_urls_for_track("/fs/mix/web.track.toml") + .get_urls_for_track("/fs/mix/web.cbd-track.toml") .await .expect("url"); assert_eq!(urls, vec!["https://example.org/s.mp3".to_string()]); // Link playables never resolve here: their tracks route to the // target provider, so landing here means a malformed request. let err = client - .get_urls_for_track("/fs/mix/linked.track.toml") + .get_urls_for_track("/fs/mix/linked.cbd-track.toml") .await .expect_err("link"); assert_eq!(err, ProviderError::MalformedPath); @@ -729,11 +729,11 @@ mod tests { let (client, dir) = client_with_root().await; write_track( dir.path(), - "linked.track.toml", + "linked.cbd-track.toml", "title = \"t\"\nartist = \"a\"\n[playable]\nlink = \"/tidal/artists/1/2\"\n", ); let track = client - .get_metadata_for_track("/fs/linked.track.toml") + .get_metadata_for_track("/fs/linked.cbd-track.toml") .await .expect("metadata"); assert_eq!(track.path, "/tidal/artists/1/2"); @@ -750,9 +750,9 @@ mod tests { let al2 = dir.path().join("artist/album2"); fs::create_dir_all(&al1).expect("mkdir"); fs::create_dir_all(&al2).expect("mkdir"); - write_track(&al1, "01.track.toml", &url_track("one")); - write_track(&al1, "02.track.toml", &url_track("two")); - write_track(&al2, "01.track.toml", &url_track("three")); + write_track(&al1, "01.cbd-track.toml", &url_track("one")); + write_track(&al1, "02.cbd-track.toml", &url_track("two")); + write_track(&al2, "01.cbd-track.toml", &url_track("three")); let (chunk_tx, chunk_rx) = flume::bounded(8); client diff --git a/plan/summary.md b/plan/summary.md index 7ade29b..5de0b2b 100644 --- a/plan/summary.md +++ b/plan/summary.md @@ -4,7 +4,7 @@ Built per `plan/fs-provider.md`: a second media provider (crate `fsdy`, `/fs`) that walks one configured root directory and treats -`*.track.toml` files as serialized track nodes — metadata plus exactly +`*.cbd-track.toml` files as serialized track nodes — metadata plus exactly one playable reference: a local audio file (absolute or relative to the track file), an http(s) URL, or a crabidy-internal link. The wire types are unchanged (architecture D1): the only new datastructure is the @@ -30,6 +30,9 @@ are recorded in `architecture/fs-provider.md` (options + rationale). ### Deviations from plan / architecture (fs-provider) +- **Extension renamed to `.cbd-track.toml`** (user request, follow-up + commit): the original `.track.toml` was too generic; the `cbd-` prefix + makes the files unmistakably crabidy's. - **`TrackFileError::UrlScheme` carries only the scheme**, not the URL: the parse error ends up in skip-warnings, and a private stream URL may embed a token (quality gate "no file contents in logs"). The