1use derive_builder::Builder;
9use reqwest::Method;
10use std::borrow::Cow;
11
12use crate::api::{Endpoint, NoPagination, ReturnsJsonResponse};
13use serde_repr::{Deserialize_repr, Serialize_repr};
14
15#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19pub struct RoleEssentials {
20 pub id: u64,
22 pub name: String,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub inherited: Option<bool>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
31pub enum IssuesVisibility {
32 #[serde(rename = "all")]
34 All,
35 #[serde(rename = "default")]
37 AllNonPrivate,
38 #[serde(rename = "own")]
40 Own,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45pub enum TimeEntriesVisibility {
46 #[serde(rename = "all")]
48 All,
49 #[serde(rename = "own")]
51 Own,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
56pub enum UsersVisibility {
57 #[serde(rename = "all")]
59 All,
60 #[serde(rename = "members_of_visible_projects")]
62 MembersOfVisibleProjects,
63}
64
65#[derive(
67 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize_repr, Deserialize_repr,
68)]
69#[repr(u8)]
70pub enum BuiltinRole {
71 Custom = 0,
73 NonMember = 1,
75 Anonymous = 2,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
83pub struct Role {
84 pub id: u64,
86 pub name: String,
88 pub assignable: bool,
90 pub issues_visibility: IssuesVisibility,
92 pub time_entries_visibility: TimeEntriesVisibility,
94 pub users_visibility: UsersVisibility,
96 pub permissions: Vec<String>,
99 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub position: Option<u64>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub builtin: Option<BuiltinRole>,
105 #[serde(default, skip_serializing_if = "Option::is_none")]
107 pub settings: Option<serde_json::Value>,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub default_time_entry_activity_id: Option<u64>,
111}
112
113#[derive(Debug, Clone, Builder)]
118#[builder(setter(strip_option))]
119pub struct ListRoles {
120 #[builder(default)]
122 givable: Option<bool>,
123}
124
125impl ReturnsJsonResponse for ListRoles {}
126impl NoPagination for ListRoles {}
127
128impl ListRoles {
129 #[must_use]
131 pub fn builder() -> ListRolesBuilder {
132 ListRolesBuilder::default()
133 }
134}
135
136impl Endpoint for ListRoles {
137 fn method(&self) -> Method {
138 Method::GET
139 }
140
141 fn endpoint(&self) -> Cow<'static, str> {
142 "roles.json".into()
143 }
144
145 fn parameters(&self) -> crate::api::QueryParams<'_> {
146 let mut params = crate::api::QueryParams::default();
147 params.push_opt("givable", self.givable);
148 params
149 }
150}
151
152#[derive(Debug, Clone, Builder)]
154#[builder(setter(strip_option))]
155pub struct GetRole {
156 id: u64,
158}
159
160impl ReturnsJsonResponse for GetRole {}
161impl NoPagination for GetRole {}
162
163impl GetRole {
164 #[must_use]
166 pub fn builder() -> GetRoleBuilder {
167 GetRoleBuilder::default()
168 }
169}
170
171impl Endpoint for GetRole {
172 fn method(&self) -> Method {
173 Method::GET
174 }
175
176 fn endpoint(&self) -> Cow<'static, str> {
177 format!("roles/{}.json", self.id).into()
178 }
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
183pub struct RolesWrapper<T> {
184 pub roles: Vec<T>,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
191pub struct RoleWrapper<T> {
192 pub role: T,
194}
195
196#[cfg(test)]
197mod test {
198 use super::*;
199 use pretty_assertions::assert_eq;
200 use std::error::Error;
201 use tracing_test::traced_test;
202
203 #[traced_test]
204 #[test]
205 fn test_list_roles_no_pagination() -> Result<(), Box<dyn Error>> {
206 dotenvy::dotenv()?;
207 let redmine = crate::api::Redmine::from_env(
208 reqwest::blocking::Client::builder()
209 .use_rustls_tls()
210 .build()?,
211 )?;
212 let endpoint = ListRoles::builder().build()?;
213 redmine.json_response_body::<_, RolesWrapper<RoleEssentials>>(&endpoint)?;
214 Ok(())
215 }
216
217 #[traced_test]
218 #[test]
219 fn test_list_roles_givable_filter() -> Result<(), Box<dyn Error>> {
220 dotenvy::dotenv()?;
221 let redmine = crate::api::Redmine::from_env(
222 reqwest::blocking::Client::builder()
223 .use_rustls_tls()
224 .build()?,
225 )?;
226 let endpoint = ListRoles::builder().givable(true).build()?;
227 redmine.json_response_body::<_, RolesWrapper<RoleEssentials>>(&endpoint)?;
228 Ok(())
229 }
230
231 #[test]
232 fn test_get_role() -> Result<(), Box<dyn Error>> {
233 dotenvy::dotenv()?;
234 let redmine = crate::api::Redmine::from_env(
235 reqwest::blocking::Client::builder()
236 .use_rustls_tls()
237 .build()?,
238 )?;
239 let endpoint = GetRole::builder().id(8).build()?;
240 redmine.json_response_body::<_, RoleWrapper<Role>>(&endpoint)?;
241 Ok(())
242 }
243
244 #[traced_test]
249 #[test]
250 fn test_completeness_role_type() -> Result<(), Box<dyn Error>> {
251 dotenvy::dotenv()?;
252 let redmine = crate::api::Redmine::from_env(
253 reqwest::blocking::Client::builder()
254 .use_rustls_tls()
255 .build()?,
256 )?;
257 let list_endpoint = ListRoles::builder().build()?;
258 let RolesWrapper { roles } =
259 redmine.json_response_body::<_, RolesWrapper<RoleEssentials>>(&list_endpoint)?;
260 for role in roles {
261 let endpoint = GetRole::builder().id(role.id).build()?;
262 let RoleWrapper { role: value } =
263 redmine.json_response_body::<_, RoleWrapper<serde_json::Value>>(&endpoint)?;
264 let o: Role = serde_json::from_value(value.clone())?;
265 let reserialized = serde_json::to_value(o)?;
266 assert_eq!(value, reserialized);
267 }
268 Ok(())
269 }
270}