redmine_api/api/
issue_statuses.rs1use derive_builder::Builder;
8use reqwest::Method;
9use std::borrow::Cow;
10
11use crate::api::{Endpoint, NoPagination, ReturnsJsonResponse};
12
13#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16pub struct IssueStatusEssentials {
17 pub id: u64,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub is_closed: Option<bool>,
22 pub name: String,
24}
25
26impl From<IssueStatus> for IssueStatusEssentials {
27 fn from(v: IssueStatus) -> Self {
28 Self {
29 id: v.id,
30 is_closed: Some(v.is_closed),
31 name: v.name,
32 }
33 }
34}
35
36impl From<&IssueStatus> for IssueStatusEssentials {
37 fn from(v: &IssueStatus) -> Self {
38 Self {
39 id: v.id,
40 is_closed: Some(v.is_closed),
41 name: v.name.to_owned(),
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50pub struct IssueStatus {
51 pub id: u64,
53 pub name: String,
55 pub description: Option<String>,
57 pub is_closed: bool,
59}
60
61#[derive(Debug, Clone, Builder)]
63#[builder(setter(strip_option))]
64#[expect(
65 clippy::empty_structs_with_brackets,
66 reason = "derive_builder requires named-field syntax"
67)]
68pub struct ListIssueStatuses {}
69
70impl ReturnsJsonResponse for ListIssueStatuses {}
71impl NoPagination for ListIssueStatuses {}
72
73impl ListIssueStatuses {
74 #[must_use]
76 pub fn builder() -> ListIssueStatusesBuilder {
77 ListIssueStatusesBuilder::default()
78 }
79}
80
81impl Endpoint for ListIssueStatuses {
82 fn method(&self) -> Method {
83 Method::GET
84 }
85
86 fn endpoint(&self) -> Cow<'static, str> {
87 "issue_statuses.json".into()
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
93pub struct IssueStatusesWrapper<T> {
94 pub issue_statuses: Vec<T>,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
101pub struct IssueStatusWrapper<T> {
102 pub issue_status: T,
104}
105
106#[cfg(test)]
107mod test {
108 use super::*;
109 use pretty_assertions::assert_eq;
110 use std::error::Error;
111 use tracing_test::traced_test;
112
113 #[traced_test]
114 #[test]
115 fn test_list_issue_statuses_no_pagination() -> Result<(), Box<dyn Error>> {
116 dotenvy::dotenv()?;
117 let redmine = crate::api::Redmine::from_env(
118 reqwest::blocking::Client::builder()
119 .tls_backend_rustls()
120 .build()?,
121 )?;
122 let endpoint = ListIssueStatuses::builder().build()?;
123 redmine.json_response_body::<_, IssueStatusesWrapper<IssueStatus>>(&endpoint)?;
124 Ok(())
125 }
126
127 #[traced_test]
132 #[test]
133 fn test_completeness_issue_status_type() -> Result<(), Box<dyn Error>> {
134 dotenvy::dotenv()?;
135 let redmine = crate::api::Redmine::from_env(
136 reqwest::blocking::Client::builder()
137 .tls_backend_rustls()
138 .build()?,
139 )?;
140 let endpoint = ListIssueStatuses::builder().build()?;
141 let IssueStatusesWrapper {
142 issue_statuses: values,
143 } = redmine.json_response_body::<_, IssueStatusesWrapper<serde_json::Value>>(&endpoint)?;
144 for value in values {
145 let o: IssueStatus = serde_json::from_value(value.clone())?;
146 let reserialized = serde_json::to_value(o)?;
147 assert_eq!(value, reserialized);
148 }
149 Ok(())
150 }
151
152 #[test]
153 fn test_issue_status_essentials_from_issue_status() {
154 let issue_status = IssueStatus {
155 id: 1,
156 name: "New".to_string(),
157 description: Some("new issue".to_string()),
158 is_closed: false,
159 };
160 let issue_status_essentials: IssueStatusEssentials = issue_status.into();
161 assert_eq!(issue_status_essentials.id, 1);
162 assert_eq!(issue_status_essentials.name, "New");
163 assert_eq!(issue_status_essentials.is_closed, Some(false));
164 }
165}