perf(metadata): move normalized Rust values into response envelopes

Move normalized maps and track arrays into response envelopes instead of serializing them into another Value tree. Cover JSON parity, Unicode, track numbering and cancellation.
This commit is contained in:
zarzet
2026-09-20 17:21:01 +07:00
parent 3b662cc114
commit cb5ce3df62
2 changed files with 55 additions and 6 deletions
@@ -392,10 +392,17 @@ fn response(kind: &str, value: &Value, check: &Check<'_>) -> Result<Value, Resol
check().map_err(ResolverError::Cancelled)?;
let cover = text(value, "cover_url");
Ok(match kind {
"track" => json!({"track":track(value, "", 0)}),
"album" => {
json!({"album_info":album(value, true),"track_list":tracks(array(value,"tracks"),cover,check)?})
}
// Move normalized values into the envelope. json! serializes borrowed
// expressions, duplicating every field of an already-built track list.
"track" => Map::from_iter([("track".into(), track(value, "", 0))]).into(),
"album" => Map::from_iter([
("album_info".into(), album(value, true)),
(
"track_list".into(),
tracks(array(value, "tracks"), cover, check)?,
),
])
.into(),
"playlist" => {
let mut info = strings(
value,
@@ -413,7 +420,14 @@ fn response(kind: &str, value: &Value, check: &Check<'_>) -> Result<Value, Resol
"owner".into(),
json!({"name":text(value,"artists"),"images":cover}),
);
json!({"playlist_info":info,"track_list":tracks(array(value,"tracks"),cover,check)?})
Map::from_iter([
("playlist_info".into(), info.into()),
(
"track_list".into(),
tracks(array(value, "tracks"), cover, check)?,
),
])
.into()
}
"artist" => {
let cover = text(value, "image_url");
@@ -439,7 +453,7 @@ fn response(kind: &str, value: &Value, check: &Check<'_>) -> Result<Value, Resol
if value["listeners"].as_i64().unwrap_or_default() > 0 {
info.insert("listeners".into(), value["listeners"].clone());
}
let mut result = json!({"artist_info":info});
let mut result = Value::Object(Map::from_iter([("artist_info".into(), info.into())]));
for key in ["albums", "releases"] {
let values = array(value, key);
if key == "albums" || !values.is_empty() {
@@ -49,6 +49,41 @@ fn fixture() -> (tempfile::TempDir, Backend) {
(root, backend)
}
#[test]
fn response_envelopes_preserve_json_and_cancellation() {
let value = json!({
"id": "collection", "name": "音楽 🎵", "artists": "Artist Café",
"cover_url": "https://example.invalid/cover.jpg",
"tracks": [
{"id": "first", "name": "One", "external_links": {"example": "link"}},
{"id": "second", "name": "Two", "explicit": true, "track_number": 9},
],
});
let check = || Ok(());
let cover = text(&value, "cover_url");
let expected = json!({
"album_info": album(&value, true),
"track_list": tracks(array(&value, "tracks"), cover, &check).unwrap(),
});
let actual = response("album", &value, &check).unwrap();
assert_eq!(actual.to_string(), expected.to_string());
let playlist = response("playlist", &value, &check).unwrap();
assert_eq!(playlist["track_list"], expected["track_list"]);
assert_eq!(playlist["playlist_info"]["owner"]["name"], "Artist Café");
assert_eq!(playlist["track_list"][0]["track_number"], 1);
assert_eq!(playlist["track_list"][1]["track_number"], 9);
assert_eq!(
response("track", &value, &check).unwrap().to_string(),
json!({"track": track(&value, "", 0)}).to_string(),
);
for kind in ["album", "playlist", "artist", "track"] {
assert_eq!(
response(kind, &value, &|| Err("cancelled".into())),
Err(ResolverError::Cancelled("cancelled".into())),
);
}
}
#[test]
fn artist_metadata_preserves_logo_separately_from_portrait() {
let (_root, backend) = fixture();