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,79 +13,64 @@ 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 {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
match value { if self.value.is_empty() {
let mut empty = s.serialize_struct("subsonic-response", 2)?;
empty.serialize_field("status", &self.status)?;
empty.serialize_field("version", &self.version)?;
return empty.end();
}
let mut s = s.serialize_struct("subsonic-response", 3)?;
s.serialize_field("status", &self.status)?;
s.serialize_field("version", &self.version)?;
match self.value.as_ref() {
SubResponseType::MusicFolders { music_folders } => { SubResponseType::MusicFolders { music_folders } => {
s.serialize_field("musicFolders", music_folders)?;
}
SubResponseType::Error(error) => {
s.serialize_field("error", error)?;
}
SubResponseType::License { valid } => {
#[derive(Serialize)] #[derive(Serialize)]
struct MF { struct License {
id: String, #[serde(rename = "valid")]
name: String, valid: bool,
} }
impl From<MusicFolder> for MF { s.serialize_field("license", &License { valid: *valid })?;
fn from(value: MusicFolder) -> Self {
Self {
id: value.id,
name: value.name,
} }
}
}
#[derive(Serialize)]
struct MusicFolders {
#[serde(rename = "musicFolders")]
music_folders: Vec<MF>,
}
MusicFolders {
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 } => { SubResponseType::AlbumList { albums } => {
#[derive(Serialize)] #[derive(Serialize)]
struct AlbumList { struct AlbumList<'a> {
#[serde(rename = "album")] #[serde(rename = "album")]
albums: Vec<Album>, albums: &'a Vec<Album>,
} }
AlbumList { s.serialize_field("albumList", &AlbumList { albums })?;
albums: albums.clone(),
}
.serialize(s)
} }
SubResponseType::AlbumList2 { albums } => { SubResponseType::AlbumList2 { albums } => {
#[derive(Serialize)] #[derive(Serialize)]
struct AlbumList2 { struct AlbumList2<'a> {
#[serde(rename = "album")] #[serde(rename = "album")]
albums: Vec<Album>, albums: &'a Vec<Album>,
} }
AlbumList2 { s.serialize_field("albumList2", &AlbumList2 { albums })?;
albums: albums.clone(),
}
.serialize(s)
} }
SubResponseType::Album { album, songs } => { SubResponseType::Album { album, songs } => {
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename = "album")]
struct AlbumWithSongs<'a> { struct AlbumWithSongs<'a> {
#[serde(flatten)] #[serde(flatten)]
album: &'a Album, album: &'a Album,
@ -92,23 +78,22 @@ where
songs: &'a Vec<Child>, songs: &'a Vec<Child>,
} }
AlbumWithSongs { album, songs }.serialize(s) s.serialize_field("album", &AlbumWithSongs { album, songs })?;
} }
SubResponseType::ScanStatus { scanning, count } => { SubResponseType::ScanStatus { scanning, count } => {
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename = "scanStatus")]
struct ScanStatus { struct ScanStatus {
#[serde(rename = "scanning")]
scanning: bool, scanning: bool,
#[serde(rename = "count")]
count: u64, count: u64,
} }
ScanStatus { s.serialize_field(
"scanStatus",
&ScanStatus {
scanning: *scanning, scanning: *scanning,
count: *count, count: *count,
} },
.serialize(s) )?;
} }
SubResponseType::SearchResult3 { SubResponseType::SearchResult3 {
artists, artists,
@ -116,7 +101,6 @@ where
songs, songs,
} => { } => {
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename = "searchResult3")]
struct SearchResult3<'a> { struct SearchResult3<'a> {
#[serde(rename = "artist")] #[serde(rename = "artist")]
artists: &'a Vec<Artist>, artists: &'a Vec<Artist>,
@ -126,16 +110,29 @@ where
songs: &'a Vec<Child>, songs: &'a Vec<Child>,
} }
SearchResult3 { s.serialize_field(
"searchResult3",
&SearchResult3 {
artists, artists,
albums, albums,
songs, songs,
},
)?;
} }
.serialize(s) SubResponseType::Artists { artists } => {
#[derive(Serialize)]
struct Artists<'a> {
#[serde(rename = "artist")]
artists: &'a Vec<Artist>,
} }
SubResponseType::Artists { artists } => artists.serialize(s),
SubResponseType::Artist { artist } => artist.serialize(s), s.serialize_field("artists", &Artists { artists })?;
SubResponseType::Empty => s.serialize_none(), }
SubResponseType::Artist { artist } => s.serialize_field("artist", artist)?,
SubResponseType::Empty => unreachable!("Empty response should be handled above"),
}
s.end()
} }
} }

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,
} }