1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use url::form_urlencoded;
5
6use crate::{Future, Github, SortDirection, Stream};
7use crate::issues::State;
8use crate::users::User;
9
10pub struct Milestones {
11 github: Github,
12 owner: String,
13 repo: String,
14}
15
16#[derive(Debug, Serialize, Deserialize)]
17pub struct Milestone {
18 pub url: String,
19 pub html_url: String,
20 pub labels_url: String,
21 pub id: u64,
22 pub node_id: String,
23 pub number: u64,
24 pub title: String,
25 pub description: Option<String>,
26 pub creator: User,
27 pub open_issues: u64,
28 pub closed_issues: u64,
29 pub state: String,
30 pub created_at: String,
31 pub updated_at: String,
32 pub due_on: Option<String>,
33 pub closed_at: Option<String>,
34}
35
36#[derive(Debug, Serialize)]
37pub struct MilestoneOptions {
38 pub title: String,
39 pub state: String,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub description: Option<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub due_on: Option<String>,
44}
45
46#[derive(Default)]
47pub struct MilestoneListOptions {
48 params: HashMap<&'static str, String>,
49}
50
51impl MilestoneListOptions {
52 pub fn builder() -> MilestoneListOptionsBuilder {
53 MilestoneListOptionsBuilder::default()
54 }
55
56 pub fn serialize(&self) -> Option<String> {
57 if self.params.is_empty() {
58 None
59 } else {
60 let encoded: String = form_urlencoded::Serializer::new(String::new())
61 .extend_pairs(&self.params)
62 .finish();
63 Some(encoded)
64 }
65 }
66}
67
68#[derive(Default)]
70pub struct MilestoneListOptionsBuilder(MilestoneListOptions);
71
72impl MilestoneListOptionsBuilder {
73 pub fn state(&mut self, state: State) -> &mut Self {
74 self.0.params.insert("state", state.to_string());
75 self
76 }
77
78 pub fn direction(&mut self, direction: SortDirection) -> &mut Self {
79 self.0.params.insert("direction", direction.to_string());
80 self
81 }
82
83 pub fn per_page(&mut self, n: u32) -> &mut Self {
84 self.0.params.insert("per_page", n.to_string());
85 self
86 }
87
88 pub fn build(&self) -> MilestoneListOptions {
89 MilestoneListOptions {
90 params: self.0.params.clone(),
91 }
92 }
93}
94
95impl Milestones {
96 pub fn new<O, R>(github: Github, owner: O, repo: R) -> Self
98 where
99 O: Into<String>,
100 R: Into<String>,
101 {
102 Milestones {
103 github,
104 owner: owner.into(),
105 repo: repo.into(),
106 }
107 }
108
109 fn path(&self, more: &str) -> String {
110 format!("/repos/{}/{}/milestones{}", self.owner, self.repo, more)
111 }
112
113 pub fn create(&self, is: &MilestoneOptions) -> Future<Milestone> {
114 self.github.post(&self.path(""), json!(is))
115 }
116
117 pub fn update(&self, is: &MilestoneOptions) -> Future<Milestone> {
118 self.github.patch(&self.path(""), json!(is))
119 }
120
121 pub fn list(&self, options: &MilestoneListOptions) -> Future<Vec<Milestone>> {
125 let mut uri = vec![self.path("")];
126 if let Some(query) = options.serialize() {
127 uri.push(query);
128 }
129 self.github.get(&uri.join("?"))
130 }
131
132 pub fn iter(&self, options: &MilestoneListOptions) -> Stream<Milestone> {
140 let mut uri = vec![self.path("")];
141 if let Some(query) = options.serialize() {
142 uri.push(query);
143 }
144 self.github.get_stream(&uri.join("?"))
145 }
146}