1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use crate::users::User;
use crate::Future;
use crate::{Error, Github};
use futures::prelude::*;
use http::StatusCode;
use std::collections::HashMap;
use std::fmt;
#[derive(Debug)]
pub enum Permissions {
    Admin,
    Push,
    Pull,
}

impl Default for Permissions {
    fn default() -> Permissions {
        Permissions::Push
    }
}

impl fmt::Display for Permissions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match *self {
                Permissions::Admin => "admin",
                Permissions::Push => "push",
                Permissions::Pull => "pull",
            }
        )
    }
}

pub struct Collaborators {
    github: Github,
    owner: String,
    repo: String,
}

impl Collaborators {
    pub fn new<O, R>(github: Github, owner: O, repo: R) -> Collaborators
    where
        O: Into<String>,
        R: Into<String>,
    {
        Collaborators {
            github,
            owner: owner.into(),
            repo: repo.into(),
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/collaborators{}", self.owner, self.repo, more)
    }

    pub fn list(&self) -> Future<Vec<User>> {
        self.github.get::<Vec<User>>(&self.path(""))
    }

    pub fn is_collaborator(&self, username: &str) -> Future<bool> {
        Box::pin(
            self.github
                .get::<()>(&self.path(&format!("/{}", username)))
                .map_ok(|_| true)
                .or_else(|err| async move {
                    match err {
                        Error::Fault {
                            code: StatusCode::NOT_FOUND,
                            ..
                        } => Ok(false),
                        Error::Codec(_) => Ok(true),
                        otherwise => Err(otherwise),
                    }
                }),
        )
    }

    pub fn add(&self, username: &str, permissions: &Permissions) -> Future<()> {
        let mut permission_params = HashMap::new();
        permission_params.insert("permission", permissions.to_string());

        match serde_json::to_string(&permission_params) {
            Ok(data) => self
                .github
                .put::<()>(&self.path(&format!("/{}", username)), data.into_bytes()),
            Err(e) => Box::pin(futures::future::err(e.into())),
        }
    }

    pub fn remove(&self, username: &str) -> Future<()> {
        self.github.delete(&self.path(&format!("/{}", username)))
    }
}