1use crate::config::Config;
2use crate::error::Error;
3use octocrab as Git;
4use octocrab::models::repos::Release;
5use octocrab::{Octocrab, Page};
6use reqwest::Client as Http;
7use std::io::Cursor;
8use std::sync::Arc;
9use std::{fs, io};
10
11static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
12
13pub struct Source {
14 git: Arc<Octocrab>,
15 http: Http,
16}
17
18impl Source {
19 pub fn new(_config: Option<Config>) -> Self {
20 Self {
21 git: Git::instance(),
22 http: Http::builder().user_agent(USER_AGENT).build().unwrap(),
23 }
24 }
25
26 pub async fn get_versions(&self) -> Result<Page<Release>, Error> {
27 let releases = self
28 .git
29 .repos("akumarujon", "wall-rs-mirror")
30 .releases()
31 .list()
32 .send()
33 .await;
34
35 let releases = match releases {
36 Ok(r) => r,
37 Err(err) => return Err(Error::ReleaseFetchError(err)),
38 };
39
40 Ok(releases)
41 }
42
43 pub async fn get_latest_version(&self) -> Result<String, Error> {
44 let releases = self
45 .git
46 .repos("akumarujon", "wall-rs-mirror")
47 .releases()
48 .list()
49 .send()
50 .await;
51
52 let releases = match releases {
53 Ok(r) => r,
54 Err(err) => return Err(Error::ReleaseFetchError(err)),
55 };
56
57 let latest = match releases.items.first() {
58 Some(v) => v,
59 None => return Err(Error::NoVersionFound),
60 };
61
62 Ok(latest.tag_name.to_owned())
63 }
64
65 pub async fn download_file<T>(&self, url: T, dest: T) -> Result<(), Error>
66 where
67 T: ToString,
68 {
69 let response = match self.http.get(url.to_string()).send().await {
70 Ok(d) => d,
71 Err(_) => return Err(Error::NoInternetConnection),
72 };
73
74 let mut file = match std::fs::File::create(dest.to_string().clone()) {
75 Ok(f) => f,
76 Err(_) => return Err(Error::CantCreateDownloadedFile(dest.to_string())),
77 };
78
79 let mut content = Cursor::new(match response.bytes().await {
80 Ok(b) => b,
81 Err(_) => return Err(Error::CantCreateCursorBytes),
82 });
83
84 match std::io::copy(&mut content, &mut file) {
85 Ok(_) => {}
86 Err(_) => return Err(Error::CantCopyBytes),
87 };
88
89 Ok(())
90 }
91
92 pub fn extract_file<T>(&self, file: T) -> Result<(), Error>
93 where
94 T: AsRef<str>,
95 {
96 let fname = std::path::Path::new(file.as_ref());
97 let file = fs::File::open(fname).unwrap();
98
99 let mut archive = zip::ZipArchive::new(file).unwrap();
100
101 for i in 0..archive.len() {
102 let mut file = archive.by_index(i).unwrap();
103 let outpath = match file.enclosed_name() {
104 Some(path) => path.to_owned(),
105 None => continue,
106 };
107
108 {
109 let comment = file.comment();
110 if !comment.is_empty() {
111 println!("File {i} comment: {comment}");
112 }
113 }
114
115 if (*file.name()).ends_with('/') {
116 println!("File {} extracted to \"{}\"", i, outpath.display());
117 fs::create_dir_all(&outpath).unwrap();
118 } else {
119 println!(
120 "File {} extracted to \"{}\" ({} bytes)",
121 i,
122 outpath.display(),
123 file.size()
124 );
125 if let Some(p) = outpath.parent() {
126 if !p.exists() {
127 fs::create_dir_all(p).unwrap();
128 }
129 }
130 let mut outfile = fs::File::create(&outpath).unwrap();
131 io::copy(&mut file, &mut outfile).unwrap();
132 }
133
134 #[cfg(unix)]
136 {
137 use std::os::unix::fs::PermissionsExt;
138
139 if let Some(mode) = file.unix_mode() {
140 fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap();
141 }
142 }
143 }
144
145 Ok(())
146 }
147}