fix: fix json serialization
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
513997cd79
commit
79e2f730cc
5 changed files with 140 additions and 143 deletions
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue