rootle_github/client/
trees.rs1use super::transport::Conditional;
4use super::{API, GitHubClient};
5use crate::types::{RepoMeta, TreeResponse};
6use rootle_provider::{ErrorKind, ProviderError, ProviderResult};
7
8impl GitHubClient {
9 pub fn fetch_tree(
17 &self,
18 owner: &str,
19 repo: &str,
20 ref_: Option<&str>,
21 ) -> ProviderResult<(
22 TreeResponse,
23 bool,
24 String,
25 )> {
26 if let Some(r) = ref_ {
27 return self.fetch_tree_on(owner, repo, r);
28 }
29 let cached_branch = crate::cache::cached_branch(owner, repo);
32 let branch = match &cached_branch {
33 Some(b) => {
34 rootle_provider::trace(&format!("tree branch cached {owner}/{repo} {b}"));
35 b.clone()
36 }
37 None => {
38 rootle_provider::trace(&format!("tree branch meta-fetch {owner}/{repo}"));
39 let meta: RepoMeta = self.get(&format!("{API}/repos/{owner}/{repo}"))?;
40 meta.default_branch
41 }
42 };
43 match self.fetch_tree_on(owner, repo, &branch) {
44 Err(e) if cached_branch.is_some() && e.kind == ErrorKind::NotFound => {
47 let meta: RepoMeta = self.get(&format!("{API}/repos/{owner}/{repo}"))?;
48 self.fetch_tree_on(owner, repo, &meta.default_branch)
49 }
50 other => other,
51 }
52 }
53
54 fn fetch_tree_on(
55 &self,
56 owner: &str,
57 repo: &str,
58 branch: &str,
59 ) -> ProviderResult<(
60 TreeResponse,
61 bool,
62 String,
63 )> {
64 let cached_ref = crate::cache::read_ref(owner, repo, branch);
65 let url = format!("{API}/repos/{owner}/{repo}/git/trees/{branch}?recursive=1");
66
67 let etag = cached_ref.as_ref().and_then(|r| r.etag.clone());
68 match self.get_conditional::<TreeResponse>(&url, etag.as_deref())? {
69 Conditional::NotModified => {
70 let sha = cached_ref.expect("304 without a cached ref").tree_sha;
71 match crate::cache::read_tree(&sha) {
72 Some(tree) => Ok((tree.clone(), tree.truncated, branch.to_string())),
73 None => {
74 rootle_provider::trace(&format!(
84 "304 but tree {sha} missing from cache; refetching"
85 ));
86 let Conditional::Fresh { body, etag } =
87 self.get_conditional::<TreeResponse>(&url, None)?
88 else {
89 return Err(ProviderError::other(
90 "unconditional revalidation returned 304",
91 ));
92 };
93 self.store_tree(owner, repo, branch, body, etag)
94 }
95 }
96 }
97 Conditional::Fresh { body, etag } => self.store_tree(owner, repo, branch, body, etag),
98 }
99 }
100
101 fn store_tree(
105 &self,
106 owner: &str,
107 repo: &str,
108 branch: &str,
109 body: TreeResponse,
110 etag: Option<String>,
111 ) -> ProviderResult<(
112 TreeResponse,
113 bool,
114 String,
115 )> {
116 crate::cache::write_tree(&body).map_err(|e| ProviderError::other(e.to_string()))?;
117 crate::cache::write_ref(
118 owner,
119 repo,
120 branch,
121 &crate::cache::RefCache {
122 tree_sha: body.sha.clone(),
123 etag,
124 },
125 )
126 .map_err(|e| ProviderError::other(e.to_string()))?;
127 let truncated = body.truncated;
128 Ok((body, truncated, branch.to_string()))
129 }
130}