1use derive_builder::Builder;
9use reqwest::Method;
10use std::borrow::Cow;
11
12use crate::api::{Endpoint, ReturnsJsonResponse};
13
14#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
18pub struct RoleEssentials {
19 pub id: u64,
21 pub name: String,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub inherited: Option<bool>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
30pub enum IssuesVisibility {
31 #[serde(rename = "all")]
33 All,
34 #[serde(rename = "default")]
36 AllNonPrivate,
37 #[serde(rename = "own")]
39 Own,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
44pub enum TimeEntriesVisibility {
45 #[serde(rename = "all")]
47 All,
48 #[serde(rename = "own")]
50 Own,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
55pub enum UsersVisibility {
56 #[serde(rename = "all")]
58 All,
59 #[serde(rename = "members_of_visible_projects")]
61 MembersOfVisibleProjects,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
68pub struct Role {
69 pub id: u64,
71 pub name: String,
73 pub assignable: bool,
75 pub issues_visibility: IssuesVisibility,
77 pub time_entries_visibility: TimeEntriesVisibility,
79 pub users_visibility: UsersVisibility,
81 pub permissions: Vec<String>,
84}
85
86#[derive(Debug, Clone, Builder)]
91#[builder(setter(strip_option))]
92pub struct ListRoles {}
93
94impl ReturnsJsonResponse for ListRoles {}
95
96impl ListRoles {
97 #[must_use]
99 pub fn builder() -> ListRolesBuilder {
100 ListRolesBuilder::default()
101 }
102}
103
104impl Endpoint for ListRoles {
105 fn method(&self) -> Method {
106 Method::GET
107 }
108
109 fn endpoint(&self) -> Cow<'static, str> {
110 "roles.json".into()
111 }
112}
113
114#[derive(Debug, Clone, Builder)]
116#[builder(setter(strip_option))]
117pub struct GetRole {
118 id: u64,
120}
121
122impl ReturnsJsonResponse for GetRole {}
123
124impl GetRole {
125 #[must_use]
127 pub fn builder() -> GetRoleBuilder {
128 GetRoleBuilder::default()
129 }
130}
131
132impl Endpoint for GetRole {
133 fn method(&self) -> Method {
134 Method::GET
135 }
136
137 fn endpoint(&self) -> Cow<'static, str> {
138 format!("roles/{}.json", self.id).into()
139 }
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
144pub struct RolesWrapper<T> {
145 pub roles: Vec<T>,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
152pub struct RoleWrapper<T> {
153 pub role: T,
155}
156
157#[cfg(test)]
158mod test {
159 use super::*;
160 use pretty_assertions::assert_eq;
161 use std::error::Error;
162 use tracing_test::traced_test;
163
164 #[traced_test]
165 #[test]
166 fn test_list_roles_no_pagination() -> Result<(), Box<dyn Error>> {
167 dotenvy::dotenv()?;
168 let redmine = crate::api::Redmine::from_env()?;
169 let endpoint = ListRoles::builder().build()?;
170 redmine.json_response_body::<_, RolesWrapper<RoleEssentials>>(&endpoint)?;
171 Ok(())
172 }
173
174 #[test]
175 fn test_get_role() -> Result<(), Box<dyn Error>> {
176 dotenvy::dotenv()?;
177 let redmine = crate::api::Redmine::from_env()?;
178 let endpoint = GetRole::builder().id(8).build()?;
179 redmine.json_response_body::<_, RoleWrapper<Role>>(&endpoint)?;
180 Ok(())
181 }
182
183 #[traced_test]
188 #[test]
189 fn test_completeness_role_type() -> Result<(), Box<dyn Error>> {
190 dotenvy::dotenv()?;
191 let redmine = crate::api::Redmine::from_env()?;
192 let list_endpoint = ListRoles::builder().build()?;
193 let RolesWrapper { roles } =
194 redmine.json_response_body::<_, RolesWrapper<RoleEssentials>>(&list_endpoint)?;
195 for role in roles {
196 let endpoint = GetRole::builder().id(role.id).build()?;
197 let RoleWrapper { role: value } =
198 redmine.json_response_body::<_, RoleWrapper<serde_json::Value>>(&endpoint)?;
199 let o: Role = serde_json::from_value(value.clone())?;
200 let reserialized = serde_json::to_value(o)?;
201 assert_eq!(value, reserialized);
202 }
203 Ok(())
204 }
205}