rootle_github/client/
blobs.rs1use super::transport::{classify_send, classify_status};
4use super::{API, GitHubClient, urlencoding};
5use rootle_provider::{ProviderError, ProviderResult};
6
7impl GitHubClient {
8 pub fn blob_at(
11 &self,
12 owner: &str,
13 repo: &str,
14 path: &str,
15 ref_: Option<&str>,
16 ) -> ProviderResult<(Vec<u8>, String)> {
17 #[derive(serde::Deserialize)]
18 struct Contents {
19 content: String, sha: String,
21 }
22 let mut url = format!("{API}/repos/{owner}/{repo}/contents/{path}");
23 if let Some(r) = ref_ {
24 url.push_str(&format!("?ref={}", urlencoding(r)));
25 }
26 let item: Contents = self.get(&url)?;
27 use base64::Engine;
28 let clean: String = item
29 .content
30 .chars()
31 .filter(|c| !c.is_whitespace())
32 .collect();
33 let bytes = base64::engine::general_purpose::STANDARD
34 .decode(clean)
35 .map_err(|e| ProviderError::other(format!("contents base64: {e}")))?;
36 Ok((bytes, item.sha))
37 }
38
39 pub fn source_tarball(&self, repo: &str) -> ProviderResult<Vec<u8>> {
43 const CAP: u64 = 64 * 1024 * 1024;
44 let url = format!("{API}/repos/{repo}/tarball");
45 let mut req = self.http.get(&url);
46 if let Some(token) = &self.token {
47 req = req.bearer_auth(token);
48 }
49 let mut resp = req.send().map_err(classify_send)?;
50 if !resp.status().is_success() {
51 return Err(classify_status(resp));
52 }
53 if let Some(len) = resp.content_length()
54 && len > CAP
55 {
56 return Err(ProviderError::other(format!(
57 "tarball too large for local grep ({len} bytes)"
58 )));
59 }
60 let mut bytes = Vec::new();
61 let mut capped = std::io::Read::take(&mut resp, CAP);
62 std::io::Read::read_to_end(&mut capped, &mut bytes)
63 .map_err(|e| ProviderError::other(e.to_string()))?;
64 Ok(bytes)
65 }
66
67 pub fn fetch_blob(&self, owner: &str, repo: &str, sha: &str) -> ProviderResult<Vec<u8>> {
70 if let Some(bytes) = crate::cache::read_blob(sha) {
71 return Ok(bytes);
72 }
73 #[derive(serde::Deserialize)]
74 struct BlobResponse {
75 content: String,
76 size: u64,
77 }
78 let url = format!("{API}/repos/{owner}/{repo}/git/blobs/{sha}");
79 let blob: BlobResponse = self.get(&url)?;
80 if blob.size > 1024 * 1024 {
81 return Err(ProviderError::other(format!(
82 "file too large to preview ({} bytes)",
83 blob.size
84 )));
85 }
86 use base64::Engine;
87 let clean: String = blob
88 .content
89 .chars()
90 .filter(|c| !c.is_whitespace())
91 .collect();
92 let bytes = base64::engine::general_purpose::STANDARD
93 .decode(clean)
94 .map_err(|e| ProviderError::other(e.to_string()))?;
95 let _ = crate::cache::write_blob(sha, &bytes);
96 Ok(bytes)
97 }
98}