rust_codecov/
lib.rs

1pub mod author;
2pub mod commits;
3pub mod repos;
4pub mod url;
5/**
6 * Error is an enum wrapping all possible errors.
7 */
8#[derive(Debug)]
9pub enum Error {
10    ReqwestError(reqwest::Error),
11    EnvError(std::env::VarError),
12}
13
14/**
15 * Client is a struct that represents a client to the Codecov API.
16 */
17pub struct Client {
18    token: String,
19}
20
21/**
22 * Owner is a struct that represents an owner of repos.
23 */
24pub struct Owner {
25    service: String,
26    username: String,
27}
28
29impl Client {
30    pub fn new_from_env() -> Result<Client, Error> {
31        let token = match std::env::var("CODECOV_OWNER_TOKEN") {
32            Ok(token) => token,
33            Err(e) => return Err(Error::EnvError(e)),
34        };
35        Ok(Client::new(token))
36    }
37
38    pub fn new(token: String) -> Client {
39        Client { token }
40    }
41
42    fn auth_header_val(&self) -> String {
43        format!("bearer {}", self.token)
44    }
45
46    fn owner_endpoint(&self, owner: &Owner) -> String {
47        format!(
48            "{}/{}/{}",
49            "https://codecov.io/api/v2", owner.service, owner.username
50        )
51    }
52
53    /**
54     * get_all_repos returns a list of all repos for a given owner.
55     * /repos endpoint returns a list of repos for a given owner with pagination.
56     * This function will make multiple requests to get all repos.
57     */
58    pub fn get_all_repos(&self, owner: &Owner) -> Result<Vec<repos::Repo>, Error> {
59        let mut repos = Vec::new();
60        let mut url = format!("{}/repos?page_size=100", self.owner_endpoint(owner));
61        loop {
62            let mut repo_list = self.get_repos_page(&url)?;
63            match &mut repo_list {
64                repos::ReposAPIResponse {
65                    results,
66                    next: Some(next_url),
67                    ..
68                } => {
69                    repos.append(results);
70                    url = next_url.to_string();
71                    continue;
72                }
73                repos::ReposAPIResponse {
74                    results,
75                    next: None,
76                    ..
77                } => {
78                    repos.append(results);
79                    break;
80                }
81            }
82        }
83        Ok(repos)
84    }
85
86    /**
87     * get_repos_page returns a single page of repos.
88     * This is a helper function for get_all_repos.
89     */
90    fn get_repos_page(&self, url: &str) -> Result<repos::ReposAPIResponse, Error> {
91        let client = reqwest::blocking::Client::new();
92        let req = client
93            .get(url)
94            .header("Authorization", self.auth_header_val());
95        let res = match req.send() {
96            Ok(res) => res,
97            Err(e) => return Err(Error::ReqwestError(e)),
98        };
99        let repos = match res.json::<repos::ReposAPIResponse>() {
100            Ok(repos) => repos,
101            Err(e) => return Err(Error::ReqwestError(e)),
102        };
103        Ok(repos)
104    }
105
106    pub fn get_commits(
107        &self,
108        author: &author::Author,
109    ) -> Result<commits::CommitsAPIResponse, Error> {
110        let client = reqwest::blocking::Client::new();
111        let owner_endpoint = self.owner_endpoint(&Owner {
112            service: author.service.clone(),
113            username: author.username.clone(),
114        });
115        let url = format!("{}/repos/{}/commits", owner_endpoint, author.name,);
116        let req = client
117            .get(url)
118            .header("Authorization", self.auth_header_val());
119        let res = match req.send() {
120            Ok(res) => res,
121            Err(e) => return Err(Error::ReqwestError(e)),
122        };
123        let commits = match res.json::<commits::CommitsAPIResponse>() {
124            Ok(commits) => commits,
125            Err(e) => return Err(Error::ReqwestError(e)),
126        };
127        Ok(commits)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_get_all_repos() {
137        let client = Client::new_from_env().unwrap();
138        let owner = Owner {
139            service: "github".to_string(),
140            username: "codecov".to_string(),
141        };
142        let repos = client.get_all_repos(&owner).unwrap();
143        assert!(!repos.is_empty());
144    }
145
146    #[test]
147    fn test_get_commits() {
148        let client = Client::new_from_env().unwrap();
149        let author = author::Author {
150            service: "github".to_string(),
151            username: "codecov".to_string(),
152            name: "codecov-demo".to_string(),
153        };
154        let commits = client.get_commits(&author).unwrap();
155        assert!(!commits.results.is_empty());
156    }
157}