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
 96
 97
 98
 99
100
101
use failure::{Error, ResultExt};
use gitlab::{Gitlab as Client, Project};

use config::GitLabConfig;
use {Provider, Repo, SyncResult};

/// A provider which queries the GitLab API.
#[derive(Debug)]
pub struct GitLab {
    client: Client,
    cfg: GitLabConfig,
}

impl GitLab {
    /// Create a new `GitLab` provider using its config.
    pub fn with_config(cfg: GitLabConfig) -> Result<GitLab, Error> {
        let client = Client::new(&cfg.host, cfg.api_key.reveal_str())
            .sync()
            .context("Invalid API key")?;

        Ok(GitLab { client, cfg })
    }

    fn get_owned(&self) -> Result<Vec<Repo>, Error> {
        debug!("Fetching owned repos");
        let owned = self.client.owned_projects().sync()?;

        let repos: Vec<Repo> = owned
            .into_iter()
            .map(|p| self.gitlab_project_to_repo(p))
            .collect();

        debug!("Found {} owned projects", repos.len());

        Ok(repos)
    }

    fn get_organisation_repos(&self) -> Result<Vec<Repo>, Error> {
        debug!("Fetching organisation repos");

        let current_user = self
            .client
            .current_user()
            .sync()
            .context("Unable to get the name of the current user")?
            .username;
        trace!("Current GitLab user is {}", current_user);

        let all_repos = self.client.projects().sync()?;

        let org_repos: Vec<Repo> = all_repos
            .into_iter()
            .map(|p| self.gitlab_project_to_repo(p))
            .filter(|r| r.owner != current_user)
            .collect();

        debug!(
            "Found {} repos owned by organisations you are a part of",
            org_repos.len()
        );
        Ok(org_repos)
    }

    fn gitlab_project_to_repo(&self, project: Project) -> Repo {
        let mut split = project.path_with_namespace.split("/");
        let owner = split.next().expect("Namespaces always have an owner");
        let name = split.next().expect("unreachable");

        Repo {
            owner: owner.to_string(),
            name: name.to_string(),
            url: project.ssh_url_to_repo,
            provider: self.name().to_string(),
        }
    }
}

impl Provider for GitLab {
    fn name(&self) -> &str {
        "gitlab"
    }

    fn repositories(&self) -> Result<Vec<Repo>, Error> {
        let mut repos = Vec::new();

        if self.cfg.owned {
            let owned =
                self.get_owned().context("Unable to get owned repos")?;
            repos.extend(owned);
        }

        if self.cfg.organisations {
            let org_repos = self.get_organisation_repos().context(
                "Unable to get repos owned by organisations you are a part of",
            )?;
            repos.extend(org_repos);
        }

        Ok(repos)
    }
}