hubcaps_ex/
statuses.rs

1//! Statuses interface
2use serde::{Deserialize, Serialize};
3
4use crate::users::User;
5use crate::{Future, Github};
6
7/// interface for statuses associated with a repository
8pub 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    /// creates a new status for a target sha
33    pub fn create(&self, sha: &str, status: &StatusOptions) -> Future<Status> {
34        self.github
35            .post(&self.path(&format!("/{}", sha)), json!(status))
36    }
37
38    /// lists all statuses associated with a given git sha
39    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    /// list the combined statuses for a given git sha
47    /// fixme: give this a type
48    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// representations (todo: replace with derive_builder)
57
58#[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    /// pending
156    #[serde(rename = "pending")]
157    Pending,
158    /// success
159    #[serde(rename = "success")]
160    Success,
161    /// error
162    #[serde(rename = "error")]
163    Error,
164    /// failure
165    #[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}