codecov_cache/
lib.rs

1pub mod cache;
2pub mod errors;
3
4use crate::errors::Error;
5use codecov::{
6    author::Author, branch_detail::BranchDetailAPIResponse, branches::BranchesAPIResponse,
7    commits::CommitsAPIResponse, owner::Owner, repos::Repo, Client as CodecovClient,
8};
9
10/**
11 * Client is a struct wrapping CodecovClient.
12 */
13pub struct Client {
14    codecov_client: CodecovClient,
15    cache_client: cache::Client,
16}
17
18/**
19 * Client is a struct wrapping CodecovClient.
20 * pub methods are same as CodecovClient.
21 * https://docs.rs/codecov/latest/codecov/struct.Client.html
22 */
23impl Client {
24    pub fn new_from_env() -> Result<Client, Error> {
25        let cache_dir = Client::resolve_cache_dir_root();
26        Ok(Client {
27            codecov_client: CodecovClient::new_from_env()?,
28            cache_client: cache::Client::new(cache_dir, "data.json".to_string()),
29        })
30    }
31
32    fn resolve_cache_dir_root() -> std::path::PathBuf {
33        match std::env::var("CODECOV_CACHE_DIR") {
34            Ok(path) => std::path::PathBuf::from(path),
35            Err(_) => Client::default_cache_dir_root(),
36        }
37    }
38
39    fn default_cache_dir_root() -> std::path::PathBuf {
40        let Some(mut path) = dirs::cache_dir() else {
41            panic!("Unsupported platform");
42        };
43        path.push("rust-codecov-cache");
44        path
45    }
46
47    pub fn new(token: String, cache_dir: std::path::PathBuf) -> Client {
48        Client {
49            codecov_client: CodecovClient::new(token),
50            cache_client: cache::Client::new(cache_dir, "data.json".to_string()),
51        }
52    }
53
54    /**
55     * get_all_repos returns a list of all repos for a given owner.
56     * /repos endpoint returns a list of repos for a given owner with pagination.
57     * This function will make multiple requests to get all repos.
58     */
59    pub fn get_all_repos(&self, owner: &Owner) -> Result<Vec<Repo>, Error> {
60        Ok(self.codecov_client.get_all_repos(owner)?)
61    }
62
63    /**
64     * get_commits returns a list of commits for a given author.
65     * https://docs.codecov.com/reference/repos_commits_list
66     */
67    pub fn get_commits(&self, author: &Author) -> Result<CommitsAPIResponse, Error> {
68        Ok(self.codecov_client.get_commits(author)?)
69    }
70
71    /**
72     * get_branches returns a list of branches for a given author.
73     * https://docs.codecov.com/reference/repos_branches_list
74     */
75    pub fn get_branches(&self, author: &Author) -> Result<BranchesAPIResponse, Error> {
76        Ok(self.codecov_client.get_branches(author)?)
77    }
78
79    /**
80     * get_branch_detail returns a branch detail for a given author and branch name.
81     * https://docs.codecov.com/reference/repos_branches_retrieve
82     */
83    pub fn get_branch_detail(
84        &self,
85        author: &Author,
86        branch_name: &str,
87    ) -> Result<BranchDetailAPIResponse, Error> {
88        Ok(self.codecov_client.get_branch_detail(author, branch_name)?)
89    }
90
91    /**
92     * get_branch_detail returns a branch detail for a given author and branch name.
93     * https://docs.codecov.com/reference/repos_branches_retrieve
94     */
95    pub fn get_branch_detail_with_commit_id(
96        &self,
97        author: &Author,
98        branch_name: &str,
99        commit_id: &str,
100    ) -> Result<BranchDetailAPIResponse, Error> {
101        let cache_key = &[
102            &author.service,
103            &author.username,
104            &author.name,
105            branch_name,
106            commit_id,
107        ];
108        // Use cache if exists
109        if let Ok(data) = self.cache_client.load(cache_key) {
110            if let Ok(value) = serde_json::from_slice(&data) {
111                if let Ok(branch_detail) = serde_json::from_value(value) {
112                    return Ok(branch_detail);
113                }
114            }
115        }
116        // If cache does not exist, fetch from Codecov API
117        let retrieved = self.codecov_client.get_branch_detail(author, branch_name)?;
118        // Save to cache
119        if let BranchDetailAPIResponse::Success(detail) = &retrieved {
120            if let Ok(data) = serde_json::to_vec(&detail) {
121                let cache_key = &[
122                    &author.service,
123                    &author.username,
124                    &author.name,
125                    branch_name,
126                    &detail.head_commit.commitid,
127                ];
128                if let Err(err) = self.cache_client.save(cache_key, &data) {
129                    println!("Failed to save cache: {:?}", err);
130                }
131            }
132            return Ok(retrieved);
133        }
134        Ok(retrieved)
135    }
136}
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_fetch_and_cache() {
143        let client = Client::new_from_env().unwrap(); // Read CODECOV_OWNER_TOKEN from environment variable
144        let owner = Owner::new("github", "kitsuyui");
145        let author = owner.new_author("rust-codecov");
146
147        let detail = client.get_branch_detail(&author, "main").unwrap();
148
149        let BranchDetailAPIResponse::Success(detail) = detail else {
150            panic!("Unexpected response");
151        };
152        let commit_id = detail.head_commit.commitid.to_string();
153
154        client
155            .get_branch_detail_with_commit_id(&author, "main", &commit_id)
156            .unwrap();
157        assert!(client.cache_client.has(&[
158            "github",
159            "kitsuyui",
160            "rust-codecov",
161            "main",
162            &commit_id
163        ]));
164
165        // Check cache dir exists
166        let mut cache_dir = dirs::cache_dir().unwrap();
167        cache_dir.extend(&[
168            "rust-codecov-cache",
169            "github",
170            "kitsuyui",
171            "rust-codecov",
172            "main",
173            &commit_id,
174        ]);
175        assert!(cache_dir.exists());
176    }
177}