azure_rust/
projects.rs

1//! Projects interface
2use std::collections::HashMap;
3
4use url::form_urlencoded;
5
6use crate::{AzureClient, Future};
7pub use new_project_options::{ProjectsOptions, ProjectsOptionsBuilder};
8pub use new_project_response::ProjectStatus;
9pub use project_list_response::ProjectsResponse;
10pub use project_response::ProjectResponse;
11
12pub struct Projects {
13    ops: AzureClient,
14}
15
16impl Projects {
17    #[doc(hidden)]
18    pub fn new(ops: AzureClient) -> Self {
19        Self { ops }
20    }
21
22    /// Create a new project
23    pub fn create(&self, project: &ProjectsOptions) -> Future<ProjectStatus> {
24        self.ops.post(&self.path(""), json!(project))
25    }
26
27    fn path(&self, more: &str) -> String {
28        format!("/{}/{}/_apis/projects", self.ops.org, more)
29    }
30
31    /// List existing projects
32    pub fn list(&self, options: &ProjectOptions) -> Future<ProjectsResponse> {
33        let mut uri = vec![self.path("")];
34        if let Some(query) = options.serialize() {
35            uri.push(query);
36        }
37
38        // FIXME: It seems incorrect. Only one ? it used in a URL to start a query and they should be separated with &.
39        self.ops.get(&uri.join("?"))
40    }
41}
42mod new_project_response {
43    use serde::*;
44    #[derive(Debug, Default, Deserialize)]
45    pub struct ProjectStatus {
46        pub id: String,
47        pub status: String,
48        pub url: String,
49    }
50}
51
52mod new_project_options {
53    use serde::*;
54    // #[serde(skip_serializing_if = "Option::is_none")]
55
56    #[derive(Debug, Default, Serialize)]
57    pub struct ProjectsOptions {
58        pub name: String,
59        pub description: String,
60        pub capabilities: Capabilities,
61    }
62    #[derive(Debug, Default, Serialize)]
63    pub struct Capabilities {
64        pub versioncontrol: Versioncontrol,
65        #[serde(rename = "processTemplate")]
66        pub process_template: ProcessTemplate,
67    }
68    #[derive(Debug, Default, Serialize)]
69    pub struct Versioncontrol {
70        #[serde(rename = "sourceControlType")]
71        pub source_control_type: String,
72    }
73    #[derive(Debug, Default, Serialize)]
74    pub struct ProcessTemplate {
75        #[serde(rename = "templateTypeId")]
76        pub template_type_id: String,
77    }
78
79    pub struct ProjectsOptionsBuilder(ProjectsOptions);
80
81    impl ProjectsOptionsBuilder {
82        pub(crate) fn new<N>(name: N) -> Self
83        where
84            N: Into<String>,
85        {
86            ProjectsOptionsBuilder(ProjectsOptions {
87                name: name.into(),
88                ..Default::default()
89            })
90        }
91
92        pub fn description<D>(&mut self, description: D) -> &mut Self
93        where
94            D: Into<String>,
95        {
96            self.0.description = description.into();
97            self
98        }
99
100        pub fn source_control_type<H>(&mut self, source_control_type: H) -> &mut Self
101        where
102            H: Into<String>,
103        {
104            self.0.capabilities.versioncontrol.source_control_type = source_control_type.into();
105            self
106        }
107
108        pub fn template_type_id(&mut self, template_type_id: String) -> &mut Self {
109            self.0.capabilities.process_template.template_type_id = template_type_id;
110            self
111        }
112
113        pub fn build(&self) -> ProjectsOptions {
114            ProjectsOptions::new(
115                self.0.name.as_str(),
116                self.0.description.as_str(),
117                self.0
118                    .capabilities
119                    .versioncontrol
120                    .source_control_type
121                    .clone(),
122                self.0
123                    .capabilities
124                    .process_template
125                    .template_type_id
126                    .clone(),
127            )
128        }
129    }
130
131    impl ProjectsOptions {
132        #[allow(clippy::too_many_arguments)] // exempted
133        pub fn new<N, D, H, E>(
134            name: N,
135            description: D,
136            source_control_type: H,
137            template_type_id: E,
138        ) -> Self
139        where
140            N: Into<String>,
141            D: Into<String>,
142            H: Into<String>,
143            E: Into<String>,
144        {
145            ProjectsOptions {
146                name: name.into(),
147                description: description.into(),
148                capabilities: Capabilities {
149                    versioncontrol: Versioncontrol {
150                        source_control_type: source_control_type.into(),
151                    },
152                    process_template: ProcessTemplate {
153                        template_type_id: template_type_id.into(),
154                    },
155                },
156            }
157        }
158
159        pub fn builder<N: Into<String>>(name: N) -> ProjectsOptionsBuilder {
160            ProjectsOptionsBuilder::new(name)
161        }
162    }
163}
164
165mod project_list_response {
166    use serde::Deserialize;
167    #[derive(Debug, Deserialize)]
168    #[serde(rename_all = "camelCase")]
169    pub struct ProjectsResponse {
170        pub value: Vec<Value>,
171        pub count: i64,
172    }
173    #[derive(Debug, Deserialize)]
174    #[serde(rename_all = "camelCase")]
175    pub struct Value {
176        pub id: String,
177        pub name: String,
178        pub url: String,
179        pub state: String,
180        pub revision: i64,
181        pub visibility: String,
182        pub last_update_time: String,
183    }
184}
185
186#[derive(Default)]
187pub struct ProjectOptions {
188    params: HashMap<&'static str, String>,
189}
190
191impl ProjectOptions {
192    /// serialize options as a string. returns None if no options are defined
193    pub fn serialize(&self) -> Option<String> {
194        if self.params.is_empty() {
195            None
196        } else {
197            let encoded: String = form_urlencoded::Serializer::new(String::new())
198                .extend_pairs(&self.params)
199                .finish();
200            Some(encoded)
201        }
202    }
203}
204
205pub struct Project {
206    ops: AzureClient,
207    project: String,
208}
209
210impl Project {
211    #[doc(hidden)]
212    pub fn new<P>(ops: AzureClient, project: P) -> Self
213    where
214        P: Into<String>,
215    {
216        Project {
217            ops: ops,
218            project: project.into(),
219        }
220    }
221
222    pub fn get(&self) -> Future<ProjectResponse> {
223        self.ops.get(&self.path(""))
224    }
225
226    pub fn delete(&self) -> Future<ProjectStatus> {
227        self.ops.delete(&self.path(""))
228    }
229
230    // GET https://dev.azure.com/{organization}/_apis/projects/{projectId}?api-version=5.1
231    fn path(&self, more: &str) -> String {
232        format!("/{}/_apis/projects/{}{}", self.ops.org, self.project, more)
233    }
234}
235
236mod project_response {
237    use serde::Deserialize;
238    #[derive(Debug, Deserialize)]
239    #[serde(rename_all = "camelCase")]
240    pub struct ProjectResponse {
241        pub id: String,
242        pub name: String,
243        pub url: String,
244        pub state: String,
245        pub revision: i64,
246        #[serde(rename = "_links")]
247        pub links: Links,
248        pub visibility: String,
249        #[serde(rename = "defaultTeam")]
250        pub default_team: DefaultTeam,
251        #[serde(rename = "lastUpdateTime")]
252        pub last_update_time: String,
253    }
254
255    #[derive(Debug, Deserialize)]
256    pub struct Links {
257        #[serde(rename = "self")]
258        pub self_field: Href,
259        pub collection: Collection,
260        pub web: Web,
261    }
262
263    #[derive(Debug, Deserialize)]
264    pub struct Href {
265        pub href: String,
266    }
267
268    #[derive(Debug, Deserialize)]
269    pub struct Collection {
270        pub href: String,
271    }
272
273    #[derive(Debug, Deserialize)]
274    pub struct Web {
275        pub href: String,
276    }
277
278    #[derive(Debug, Deserialize)]
279    pub struct DefaultTeam {
280        pub id: String,
281        pub name: String,
282        pub url: String,
283    }
284}