redmine_api/api/
trackers.rs1use derive_builder::Builder;
8use reqwest::Method;
9use std::borrow::Cow;
10
11use crate::api::issue_statuses::IssueStatusEssentials;
12use crate::api::{Endpoint, NoPagination, ReturnsJsonResponse};
13
14#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17pub struct TrackerEssentials {
18 pub id: u64,
20 pub name: String,
22}
23
24impl From<Tracker> for TrackerEssentials {
25 fn from(v: Tracker) -> Self {
26 Self {
27 id: v.id,
28 name: v.name,
29 }
30 }
31}
32
33impl From<&Tracker> for TrackerEssentials {
34 fn from(v: &Tracker) -> Self {
35 Self {
36 id: v.id,
37 name: v.name.to_owned(),
38 }
39 }
40}
41
42#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
46pub struct Tracker {
47 pub id: u64,
49 pub name: String,
51 pub default_status: IssueStatusEssentials,
53 pub description: Option<String>,
55 pub enabled_standard_fields: Vec<String>,
57}
58
59#[derive(Debug, Clone, Builder)]
61#[builder(setter(strip_option))]
62#[expect(
63 clippy::empty_structs_with_brackets,
64 reason = "derive_builder requires named-field syntax"
65)]
66pub struct ListTrackers {}
67
68impl ReturnsJsonResponse for ListTrackers {}
69impl NoPagination for ListTrackers {}
70
71impl ListTrackers {
72 #[must_use]
74 pub fn builder() -> ListTrackersBuilder {
75 ListTrackersBuilder::default()
76 }
77}
78
79impl Endpoint for ListTrackers {
80 fn method(&self) -> Method {
81 Method::GET
82 }
83
84 fn endpoint(&self) -> Cow<'static, str> {
85 "trackers.json".into()
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
91pub struct TrackersWrapper<T> {
92 pub trackers: Vec<T>,
94}
95
96#[cfg(test)]
97mod test {
98 use super::*;
99 use pretty_assertions::assert_eq;
100 use std::error::Error;
101 use tracing_test::traced_test;
102
103 #[traced_test]
104 #[test]
105 fn test_list_trackers_no_pagination() -> Result<(), Box<dyn Error>> {
106 dotenvy::dotenv()?;
107 let redmine = crate::api::Redmine::from_env(
108 reqwest::blocking::Client::builder()
109 .tls_backend_rustls()
110 .build()?,
111 )?;
112 let endpoint = ListTrackers::builder().build()?;
113 redmine.json_response_body::<_, TrackersWrapper<Tracker>>(&endpoint)?;
114 Ok(())
115 }
116
117 #[traced_test]
122 #[test]
123 fn test_completeness_tracker_type() -> Result<(), Box<dyn Error>> {
124 dotenvy::dotenv()?;
125 let redmine = crate::api::Redmine::from_env(
126 reqwest::blocking::Client::builder()
127 .tls_backend_rustls()
128 .build()?,
129 )?;
130 let endpoint = ListTrackers::builder().build()?;
131 let TrackersWrapper { trackers: values } =
132 redmine.json_response_body::<_, TrackersWrapper<serde_json::Value>>(&endpoint)?;
133 for value in values {
134 let o: Tracker = serde_json::from_value(value.clone())?;
135 let reserialized = serde_json::to_value(o)?;
136 assert_eq!(value, reserialized);
137 }
138 Ok(())
139 }
140
141 #[test]
142 fn test_tracker_essentials_from_tracker() {
143 let tracker = Tracker {
144 id: 1,
145 name: "Bug".to_string(),
146 default_status: IssueStatusEssentials {
147 id: 1,
148 name: "New".to_string(),
149 is_closed: Some(false),
150 },
151 description: Some("A bug".to_string()),
152 enabled_standard_fields: vec![],
153 };
154 let tracker_essentials: TrackerEssentials = tracker.into();
155 assert_eq!(tracker_essentials.id, 1);
156 assert_eq!(tracker_essentials.name, "Bug");
157 }
158}