1use serde::{Deserialize, Serialize};
3
4use crate::users::User;
5use crate::{Future, Github};
6
7pub struct Statuses {
9 github: Github,
10 owner: String,
11 repo: String,
12}
13
14impl Statuses {
15 #[doc(hidden)]
16 pub fn new<O, R>(github: Github, owner: O, repo: R) -> Self
17 where
18 O: Into<String>,
19 R: Into<String>,
20 {
21 Statuses {
22 github,
23 owner: owner.into(),
24 repo: repo.into(),
25 }
26 }
27
28 fn path(&self, more: &str) -> String {
29 format!("/repos/{}/{}/statuses{}", self.owner, self.repo, more)
30 }
31
32 pub fn create(&self, sha: &str, status: &StatusOptions) -> Future<Status> {
34 self.github
35 .post(&self.path(&format!("/{}", sha)), json!(status))
36 }
37
38 pub fn list(&self, sha: &str) -> Future<Vec<Status>> {
40 self.github.get(&format!(
41 "/repos/{}/{}/commits/{}/statuses",
42 self.owner, self.repo, sha
43 ))
44 }
45
46 pub fn combined(&self, sha: &str) -> Future<String> {
49 self.github.get(&format!(
50 "/repos/{}/{}/commits/{}/status",
51 self.owner, self.repo, sha
52 ))
53 }
54}
55
56#[derive(Debug, Deserialize)]
59pub struct Status {
60 pub created_at: Option<String>,
61 pub updated_at: Option<String>,
62 pub state: State,
63 pub target_url: Option<String>,
64 pub description: String,
65 pub id: u64,
66 pub url: String,
67 pub context: String,
68 pub creator: User,
69}
70
71#[derive(Debug, Default, Serialize)]
72pub struct StatusOptions {
73 state: State,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 target_url: Option<String>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 description: Option<String>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 context: Option<String>,
80}
81
82pub struct StatusOptionsBuilder(StatusOptions);
83
84impl StatusOptionsBuilder {
85 #[doc(hidden)]
86 pub(crate) fn new(state: State) -> Self {
87 StatusOptionsBuilder(StatusOptions {
88 state,
89 ..Default::default()
90 })
91 }
92
93 pub fn target_url<T>(&mut self, url: T) -> &mut Self
94 where
95 T: Into<String>,
96 {
97 self.0.target_url = Some(url.into());
98 self
99 }
100
101 pub fn description<D>(&mut self, desc: D) -> &mut Self
102 where
103 D: Into<String>,
104 {
105 self.0.description = Some(desc.into());
106 self
107 }
108
109 pub fn context<C>(&mut self, ctx: C) -> &mut Self
110 where
111 C: Into<String>,
112 {
113 self.0.context = Some(ctx.into());
114 self
115 }
116
117 pub fn build(&self) -> StatusOptions {
118 StatusOptions::new(
119 self.0.state.clone(),
120 self.0.target_url.clone(),
121 self.0.description.clone(),
122 self.0.context.clone(),
123 )
124 }
125}
126
127impl StatusOptions {
128 #[doc(hidden)]
129 pub fn new<T, D, C>(
130 state: State,
131 target_url: Option<T>,
132 descr: Option<D>,
133 context: Option<C>,
134 ) -> Self
135 where
136 T: Into<String>,
137 D: Into<String>,
138 C: Into<String>,
139 {
140 StatusOptions {
141 state,
142 target_url: target_url.map(|t| t.into()),
143 description: descr.map(|d| d.into()),
144 context: context.map(|c| c.into()),
145 }
146 }
147
148 pub fn builder(state: State) -> StatusOptionsBuilder {
149 StatusOptionsBuilder::new(state)
150 }
151}
152
153#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
154pub enum State {
155 #[serde(rename = "pending")]
157 Pending,
158 #[serde(rename = "success")]
160 Success,
161 #[serde(rename = "error")]
163 Error,
164 #[serde(rename = "failure")]
166 Failure,
167}
168
169impl Default for State {
170 fn default() -> State {
171 State::Pending
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use serde::ser::Serialize;
179
180 fn test_encoding<E: Serialize>(tests: Vec<(E, &str)>) {
181 for test in tests {
182 let (k, v) = test;
183 assert_eq!(serde_json::to_string(&k).unwrap(), v);
184 }
185 }
186
187 #[test]
188 fn deserialize_status_state() {
189 for (json, value) in vec![
190 ("\"pending\"", State::Pending),
191 ("\"success\"", State::Success),
192 ("\"error\"", State::Error),
193 ("\"failure\"", State::Failure),
194 ] {
195 assert_eq!(serde_json::from_str::<State>(json).unwrap(), value)
196 }
197 }
198
199 #[test]
200 fn serialize_status_state() {
201 for (json, value) in vec![
202 ("\"pending\"", State::Pending),
203 ("\"success\"", State::Success),
204 ("\"error\"", State::Error),
205 ("\"failure\"", State::Failure),
206 ] {
207 assert_eq!(serde_json::to_string(&value).unwrap(), json)
208 }
209 }
210
211 #[test]
212 fn status_reqs() {
213 let tests = vec![
214 (
215 StatusOptions::builder(State::Pending).build(),
216 r#"{"state":"pending"}"#,
217 ),
218 (
219 StatusOptions::builder(State::Success)
220 .target_url("http://acme.com")
221 .build(),
222 r#"{"state":"success","target_url":"http://acme.com"}"#,
223 ),
224 (
225 StatusOptions::builder(State::Error)
226 .description("desc")
227 .build(),
228 r#"{"state":"error","description":"desc"}"#,
229 ),
230 (
231 StatusOptions::builder(State::Failure)
232 .target_url("http://acme.com")
233 .description("desc")
234 .build(),
235 r#"{"state":"failure","target_url":"http://acme.com","description":"desc"}"#,
236 ),
237 ];
238 test_encoding(tests)
239 }
240}