Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/endpoint/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ impl Qbit {
&self,
last_known_id: impl Into<Option<i64>> + Send + Sync,
) -> Result<Vec<PeerLog>> {
#[derive(Serialize)]
#[skip_serializing_none]
#[derive(Serialize)]
struct Arg {
last_known_id: Option<i64>,
}
Expand Down
4 changes: 2 additions & 2 deletions src/endpoint/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ impl Qbit {
&self,
id: impl Into<Option<i64>> + Send + Sync,
) -> Result<Vec<SearchJobStatus>> {
#[derive(Serialize)]
#[skip_serializing_none]
#[derive(Serialize)]
struct Arg {
id: Option<i64>,
}
Expand Down Expand Up @@ -129,8 +129,8 @@ impl Qbit {
limit: impl Into<Option<i64>> + Send + Sync,
offset: impl Into<Option<i64>> + Send + Sync,
) -> Result<SearchResults> {
#[derive(Serialize)]
#[skip_serializing_none]
#[derive(Serialize)]
struct Arg {
id: i64,
limit: Option<i64>,
Expand Down
2 changes: 1 addition & 1 deletion src/endpoint/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::{Qbit, Result, ext::*, model::*};
impl Qbit {
/// Return main-data changes since the supplied response ID.
pub async fn sync(&self, rid: impl Into<Option<i64>> + Send + Sync) -> Result<SyncData> {
#[derive(Serialize)]
#[skip_serializing_none]
#[derive(Serialize)]
struct Arg {
rid: Option<i64>,
}
Expand Down
4 changes: 2 additions & 2 deletions src/endpoint/torrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ impl Qbit {
hashes: impl Into<Hashes> + Send + Sync,
delete_files: impl Into<Option<bool>> + Send + Sync,
) -> Result<()> {
#[derive(Serialize)]
#[skip_serializing_none]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Arg {
hashes: Hashes,
Expand Down Expand Up @@ -840,8 +840,8 @@ impl Qbit {
hashes: impl Into<Hashes> + Send + Sync,
tags: Option<impl Into<Sep<String, ','>> + Send>,
) -> Result<()> {
#[derive(Serialize)]
#[skip_serializing_none]
#[derive(Serialize)]
struct Arg {
hashes: String,
tags: Option<String>,
Expand Down
20 changes: 19 additions & 1 deletion src/model/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub struct ProcessInfo {
pub launch_time: i64,
}

#[skip_serializing_none]
#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
#[cfg_attr(
feature = "builder",
Expand All @@ -37,7 +38,6 @@ pub struct ProcessInfo {
///
/// Optional fields allow callers to update only selected settings.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq)]
#[skip_serializing_none]
pub struct Preferences {
/// Currently selected language (e.g. en_GB for English)
pub locale: Option<String>,
Expand Down Expand Up @@ -465,3 +465,21 @@ pub struct SetCookieArg {
/// Cookie expiration date (seconds since epoch)
pub expiration_date: Option<i64>,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn preferences_skips_none_fields() {
let prefs = Preferences {
save_path: Some("/data/torrents".to_string()),
auto_tmm_enabled: Some(true),
..Default::default()
};
let json = serde_json::to_string(&prefs).unwrap();
assert!(json.contains("save_path"));
assert!(json.contains("auto_tmm_enabled"));
assert!(!json.contains("null"), "None fields should be skipped, got: {json}");
}
}
23 changes: 22 additions & 1 deletion src/model/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ pub enum LogLevel {
feature = "builder",
builder(field_defaults(default, setter(strip_option)))
)]
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
#[skip_serializing_none]
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
pub struct GetLogsArg {
/// Include normal messages (default: `true`)
pub normal: Option<bool>,
Expand All @@ -76,3 +76,24 @@ pub struct GetLogsArg {
/// Exclude messages with "message id" <= `last_known_id` (default: `-1`)
pub last_known_id: Option<i64>,
}

#[cfg(test)]
mod tests {
use super::GetLogsArg;

#[test]
fn get_logs_arg_skips_none_fields() {
let arg = GetLogsArg {
normal: None,
info: None,
warning: None,
critical: None,
last_known_id: None,
};
let json = serde_json::to_string(&arg).unwrap();
assert!(
!json.contains("null"),
"None fields should be skipped, got: {json}"
);
}
}
34 changes: 32 additions & 2 deletions src/model/torrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,8 @@ impl Display for Hashes {
feature = "builder",
builder(field_defaults(default, setter(strip_option)))
)]
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize)]
#[skip_serializing_none]
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize)]
pub struct GetTorrentListArg {
/// Filter torrent list by state. Allowed state filters: `all`,
/// `downloading`, `seeding`, `completed`, `paused`, `active`, `inactive`,
Expand Down Expand Up @@ -471,8 +471,8 @@ fn is_torrent_files(source: &TorrentSource) -> bool {
feature = "builder",
builder(field_defaults(default, setter(strip_option)))
)]
#[derive(Debug, Clone, PartialEq, serde::Serialize, Default)]
#[skip_serializing_none]
#[derive(Debug, Clone, PartialEq, serde::Serialize, Default)]
pub struct AddTorrentArg {
/// URLs or torrent files to add.
#[serde(flatten)]
Expand Down Expand Up @@ -638,6 +638,36 @@ mod torrent_source_tests {
}
}

#[cfg(test)]
mod skip_serializing_none_tests {
use super::{AddTorrentArg, GetTorrentListArg, TorrentSource};

#[test]
fn get_torrent_list_arg_skips_none_fields() {
let arg = GetTorrentListArg::default();
let json = serde_json::to_string(&arg).unwrap();
assert!(
!json.contains("null"),
"None fields should be skipped, got: {json}"
);
}

#[test]
fn add_torrent_arg_skips_none_fields() {
let arg = AddTorrentArg {
source: TorrentSource::Urls {
urls: super::Sep::from(vec![]),
},
..Default::default()
};
let json = serde_json::to_string(&arg).unwrap();
assert!(
!json.contains("null"),
"None fields should be skipped, got: {json}"
);
}
}

/// Per-torrent share limits.
#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
#[derive(Debug, Clone, PartialEq, serde::Serialize)]
Expand Down
Loading