use std::{
env,
fs::{self, File},
io::{BufRead, BufReader, Write},
};
use error::Error;
use reqwest::{
header::{self, HeaderMap},
StatusCode,
};
pub mod apis;
pub mod error;
mod macros;
pub trait ReleaseAsset {
fn get_name(&self) -> String;
fn get_download_url(&self) -> String;
fn download(
&self,
additional_headers: HeaderMap,
download_callback: Option<Box<dyn Fn(f32)>>,
) -> Result<(), Error>;
}
pub(crate) fn download<Asset: ReleaseAsset>(
asset: &Asset,
additional_headers: HeaderMap,
download_callback: Option<Box<dyn Fn(f32)>>,
) -> Result<(), Error> {
let mut additional_headers = additional_headers;
additional_headers.insert(header::USER_AGENT, "rust-reqwest/updater".parse().unwrap());
additional_headers.insert(header::ACCEPT, "application/octet-stream".parse().unwrap());
println!("headers: {:?}", additional_headers);
let response = reqwest::blocking::Client::new()
.get(&asset.get_download_url())
.headers(additional_headers)
.send()?;
if response.status() != StatusCode::OK {
println!("{}", asset.get_download_url());
return Err(Error::http(response.status()));
}
set_ssl_vars!();
let tmp_dir = tempfile::Builder::new()
.prefix(&format!("{}_dl", asset.get_name()))
.tempdir()?;
let tmp_file = tmp_dir.path().join(&asset.get_name());
let mut updated_file = File::create(&tmp_file)?;
let total_size = response.content_length().unwrap_or(0);
let mut src = BufReader::new(response);
let mut downloaded = 0;
loop {
let n = {
let buf = src.fill_buf()?;
updated_file.write_all(buf)?;
buf.len()
};
if n == 0 {
break;
}
src.consume(n);
downloaded = u64::min(total_size, downloaded + n as u64);
if let Some(ref download_callback) = download_callback {
download_callback(f32::min(downloaded as f32 / total_size as f32, 1.0));
}
}
if let Some(ref download_callback) = download_callback {
download_callback(1.0);
}
#[cfg(not(windows))]
{
let mut permissions = fs::metadata(&tmp_file)?.permissions();
permissions.set_mode(0o755);
fs::set_permissions(tmp_file, permissions)?;
}
let current_executable = env::current_exe()?;
let old_executable = current_executable.with_extension("exe.old");
let updated = current_executable.with_extension("updated");
fs::remove_file(&old_executable)?;
fs::rename(¤t_executable, &old_executable)?;
fs::copy(&tmp_file, &updated)?;
fs::rename(&updated, ¤t_executable)?;
Ok(())
}