fix: fix json serialization
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Lys 2023-11-28 05:39:38 +02:00
parent 513997cd79
commit 79e2f730cc
Signed by: lyssieth
GPG key ID: C9CF3D614FAA3940
5 changed files with 140 additions and 143 deletions

View file

@ -1,4 +1,5 @@
use poem::{http::StatusCode, IntoResponse, Response}; use poem::{http::StatusCode, IntoResponse, Response};
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use crate::authentication::VersionTriple; use crate::authentication::VersionTriple;
@ -12,130 +13,126 @@ pub use types::artist::Artist;
pub use types::child::Child; pub use types::child::Child;
pub use types::music_folder::MusicFolder; pub use types::music_folder::MusicFolder;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone)]
pub struct SubsonicResponse { pub struct SubsonicResponse {
pub status: ResponseStatus, pub status: ResponseStatus,
pub version: VersionTriple, pub version: VersionTriple,
#[serde(
skip_serializing_if = "SubResponseType::is_empty",
serialize_with = "serialize_without_root",
flatten
)]
pub value: Box<SubResponseType>, pub value: Box<SubResponseType>,
} }
fn serialize_without_root<S>(value: &SubResponseType, s: S) -> Result<S::Ok, S::Error> impl Serialize for SubsonicResponse {
where fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
S: Serializer, where
{ S: Serializer,
match value { {
SubResponseType::MusicFolders { music_folders } => { if self.value.is_empty() {
#[derive(Serialize)] let mut empty = s.serialize_struct("subsonic-response", 2)?;
struct MF { empty.serialize_field("status", &self.status)?;
id: String, empty.serialize_field("version", &self.version)?;
name: String, return empty.end();
} }
impl From<MusicFolder> for MF { let mut s = s.serialize_struct("subsonic-response", 3)?;
fn from(value: MusicFolder) -> Self { s.serialize_field("status", &self.status)?;
Self { s.serialize_field("version", &self.version)?;
id: value.id, match self.value.as_ref() {
name: value.name, SubResponseType::MusicFolders { music_folders } => {
} s.serialize_field("musicFolders", music_folders)?;
}
SubResponseType::Error(error) => {
s.serialize_field("error", error)?;
}
SubResponseType::License { valid } => {
#[derive(Serialize)]
struct License {
#[serde(rename = "valid")]
valid: bool,
} }
}
#[derive(Serialize)] s.serialize_field("license", &License { valid: *valid })?;
struct MusicFolders {
#[serde(rename = "musicFolders")]
music_folders: Vec<MF>,
} }
SubResponseType::AlbumList { albums } => {
#[derive(Serialize)]
struct AlbumList<'a> {
#[serde(rename = "album")]
albums: &'a Vec<Album>,
}
MusicFolders { s.serialize_field("albumList", &AlbumList { albums })?;
music_folders: music_folders.iter().map(|v| v.clone().into()).collect(),
}
.serialize(s)
}
SubResponseType::Error(error) => error.serialize(s),
SubResponseType::License { valid } => valid.serialize(s),
SubResponseType::AlbumList { albums } => {
#[derive(Serialize)]
struct AlbumList {
#[serde(rename = "album")]
albums: Vec<Album>,
} }
SubResponseType::AlbumList2 { albums } => {
#[derive(Serialize)]
struct AlbumList2<'a> {
#[serde(rename = "album")]
albums: &'a Vec<Album>,
}
AlbumList { s.serialize_field("albumList2", &AlbumList2 { albums })?;
albums: albums.clone(),
}
.serialize(s)
}
SubResponseType::AlbumList2 { albums } => {
#[derive(Serialize)]
struct AlbumList2 {
#[serde(rename = "album")]
albums: Vec<Album>,
} }
SubResponseType::Album { album, songs } => {
#[derive(Serialize)]
struct AlbumWithSongs<'a> {
#[serde(flatten)]
album: &'a Album,
#[serde(rename = "song")]
songs: &'a Vec<Child>,
}
AlbumList2 { s.serialize_field("album", &AlbumWithSongs { album, songs })?;
albums: albums.clone(),
}
.serialize(s)
}
SubResponseType::Album { album, songs } => {
#[derive(Serialize)]
#[serde(rename = "album")]
struct AlbumWithSongs<'a> {
#[serde(flatten)]
album: &'a Album,
#[serde(rename = "song")]
songs: &'a Vec<Child>,
} }
SubResponseType::ScanStatus { scanning, count } => {
#[derive(Serialize)]
struct ScanStatus {
scanning: bool,
count: u64,
}
AlbumWithSongs { album, songs }.serialize(s) s.serialize_field(
} "scanStatus",
SubResponseType::ScanStatus { scanning, count } => { &ScanStatus {
#[derive(Serialize)] scanning: *scanning,
#[serde(rename = "scanStatus")] count: *count,
struct ScanStatus { },
#[serde(rename = "scanning")] )?;
scanning: bool,
#[serde(rename = "count")]
count: u64,
} }
SubResponseType::SearchResult3 {
ScanStatus {
scanning: *scanning,
count: *count,
}
.serialize(s)
}
SubResponseType::SearchResult3 {
artists,
albums,
songs,
} => {
#[derive(Serialize)]
#[serde(rename = "searchResult3")]
struct SearchResult3<'a> {
#[serde(rename = "artist")]
artists: &'a Vec<Artist>,
#[serde(rename = "album")]
albums: &'a Vec<Album>,
#[serde(rename = "song")]
songs: &'a Vec<Child>,
}
SearchResult3 {
artists, artists,
albums, albums,
songs, songs,
} => {
#[derive(Serialize)]
struct SearchResult3<'a> {
#[serde(rename = "artist")]
artists: &'a Vec<Artist>,
#[serde(rename = "album")]
albums: &'a Vec<Album>,
#[serde(rename = "song")]
songs: &'a Vec<Child>,
}
s.serialize_field(
"searchResult3",
&SearchResult3 {
artists,
albums,
songs,
},
)?;
} }
.serialize(s) SubResponseType::Artists { artists } => {
#[derive(Serialize)]
struct Artists<'a> {
#[serde(rename = "artist")]
artists: &'a Vec<Artist>,
}
s.serialize_field("artists", &Artists { artists })?;
}
SubResponseType::Artist { artist } => s.serialize_field("artist", artist)?,
SubResponseType::Empty => unreachable!("Empty response should be handled above"),
} }
SubResponseType::Artists { artists } => artists.serialize(s),
SubResponseType::Artist { artist } => artist.serialize(s), s.end()
SubResponseType::Empty => s.serialize_none(),
} }
} }

View file

@ -4,29 +4,29 @@ use time::format_description::well_known::Iso8601;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct Album { pub struct Album {
#[serde(rename = "@id")] #[serde(rename = "id")]
pub id: String, pub id: String,
#[serde(rename = "@name")] #[serde(rename = "name")]
pub name: String, pub name: String,
#[serde(rename = "@artist", skip_serializing_if = "Option::is_none")] #[serde(rename = "artist", skip_serializing_if = "Option::is_none")]
pub artist: Option<String>, pub artist: Option<String>,
#[serde(rename = "@artistId", skip_serializing_if = "Option::is_none")] #[serde(rename = "artistId", skip_serializing_if = "Option::is_none")]
pub artist_id: Option<String>, pub artist_id: Option<String>,
#[serde(rename = "@coverArt", skip_serializing_if = "Option::is_none")] #[serde(rename = "coverArt", skip_serializing_if = "Option::is_none")]
pub cover_art_id: Option<String>, pub cover_art_id: Option<String>,
#[serde(rename = "@songCount")] #[serde(rename = "songCount")]
pub song_count: u64, pub song_count: u64,
#[serde(rename = "@duration")] #[serde(rename = "duration")]
pub duration: u64, pub duration: u64,
#[serde(rename = "@playCount")] #[serde(rename = "playCount")]
pub play_count: u64, pub play_count: u64,
#[serde(rename = "@created")] #[serde(rename = "created")]
pub created: String, pub created: String,
#[serde(rename = "@starred", skip_serializing_if = "Option::is_none")] #[serde(rename = "starred", skip_serializing_if = "Option::is_none")]
pub starred: Option<String>, pub starred: Option<String>,
#[serde(rename = "@year", skip_serializing_if = "Option::is_none")] #[serde(rename = "year", skip_serializing_if = "Option::is_none")]
pub year: Option<u32>, pub year: Option<u32>,
#[serde(rename = "@genre", skip_serializing_if = "Option::is_none")] #[serde(rename = "genre", skip_serializing_if = "Option::is_none")]
pub genre: Option<String>, pub genre: Option<String>,
} }

View file

@ -4,17 +4,17 @@ use time::format_description::well_known::Iso8601;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct Artist { pub struct Artist {
#[serde(rename = "@id")] #[serde(rename = "id")]
pub id: String, pub id: String,
#[serde(rename = "@name")] #[serde(rename = "name")]
pub name: String, pub name: String,
#[serde(rename = "@coverArtId")] #[serde(rename = "coverArtId")]
pub cover_art: Option<String>, pub cover_art: Option<String>,
#[serde(rename = "@artistImageUrl")] #[serde(rename = "artistImageUrl")]
pub artist_image_url: Option<String>, pub artist_image_url: Option<String>,
#[serde(rename = "@albumCount")] #[serde(rename = "albumCount")]
pub album_count: u64, pub album_count: u64,
#[serde(rename = "@starred")] #[serde(rename = "starred")]
pub starred: Option<String>, pub starred: Option<String>,
} }

View file

@ -6,49 +6,49 @@ use crate::subsonic::{Album, Artist};
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct Child { pub struct Child {
#[serde(rename = "@id")] #[serde(rename = "id")]
pub id: String, pub id: String,
#[serde(rename = "@isDir")] #[serde(rename = "isDir")]
pub is_dir: bool, pub is_dir: bool,
#[serde(rename = "@title")] #[serde(rename = "title")]
pub title: String, pub title: String,
#[serde(rename = "@album", skip_serializing_if = "Option::is_none")] #[serde(rename = "album", skip_serializing_if = "Option::is_none")]
pub album: Option<String>, pub album: Option<String>,
#[serde(rename = "@track", skip_serializing_if = "Option::is_none")] #[serde(rename = "track", skip_serializing_if = "Option::is_none")]
pub track: Option<u32>, pub track: Option<u32>,
#[serde(rename = "@year", skip_serializing_if = "Option::is_none")] #[serde(rename = "year", skip_serializing_if = "Option::is_none")]
pub year: Option<u32>, pub year: Option<u32>,
#[serde(rename = "@coverArtId", skip_serializing_if = "Option::is_none")] #[serde(rename = "coverArtId", skip_serializing_if = "Option::is_none")]
pub cover_art_id: Option<String>, pub cover_art_id: Option<String>,
#[serde(rename = "@size")] #[serde(rename = "size")]
pub size: u64, pub size: u64,
#[serde(rename = "@contentType")] #[serde(rename = "contentType")]
pub content_type: String, pub content_type: String,
#[serde(rename = "@suffix")] #[serde(rename = "suffix")]
pub suffix: String, pub suffix: String,
#[serde(rename = "@starred", skip_serializing_if = "Option::is_none")] #[serde(rename = "starred", skip_serializing_if = "Option::is_none")]
pub starred: Option<String>, pub starred: Option<String>,
#[serde(rename = "@duration")] #[serde(rename = "duration")]
pub duration: u64, pub duration: u64,
#[serde(rename = "@bitRate", skip_serializing_if = "Option::is_none")] #[serde(rename = "bitRate", skip_serializing_if = "Option::is_none")]
pub bit_rate: Option<u64>, pub bit_rate: Option<u64>,
#[serde(rename = "@path")] #[serde(rename = "path")]
pub path: String, pub path: String,
#[serde(rename = "@playCount")] #[serde(rename = "playCount")]
pub play_count: u64, pub play_count: u64,
#[serde(rename = "@discNumber")] #[serde(rename = "discNumber")]
pub disc_number: u32, pub disc_number: u32,
#[serde(rename = "@created")] #[serde(rename = "created")]
pub created: String, pub created: String,
#[serde(rename = "@albumId", skip_serializing_if = "Option::is_none")] #[serde(rename = "albumId", skip_serializing_if = "Option::is_none")]
pub album_id: Option<String>, pub album_id: Option<String>,
#[serde(rename = "@artistId", skip_serializing_if = "Option::is_none")] #[serde(rename = "artistId", skip_serializing_if = "Option::is_none")]
pub artist_id: Option<String>, pub artist_id: Option<String>,
#[serde(rename = "@artist")] #[serde(rename = "artist")]
pub artist: String, pub artist: String,
#[serde(rename = "@type")] #[serde(rename = "type")]
pub child_type: String, pub child_type: String,
#[serde(rename = "@isVideo")] #[serde(rename = "isVideo")]
pub is_video: bool, pub is_video: bool,
} }

View file

@ -3,9 +3,9 @@ use serde::Serialize;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct MusicFolder { pub struct MusicFolder {
#[serde(rename = "@id")] #[serde(rename = "id")]
pub id: String, pub id: String,
#[serde(rename = "@name")] #[serde(rename = "name")]
pub name: String, pub name: String,
} }