wicked-waifus-rs/wicked-waifus-asset-updater/src/lib.rs
xavo95 ab868a158a 2.2.2 (#3)
Ongoing changes for 2.2.2

Reviewed-on: WutheringSlaves/wicked-waifus-rs#3
2025-03-09 09:10:08 +00:00

145 lines
No EOL
5.6 KiB
Rust

use std::io::{Cursor, Read, Write};
use std::path::Path;
use git2::{FetchOptions, RemoteCallbacks, Repository};
use git2::build::RepoBuilder;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("UReq error: {0}")]
UReq(#[from] ureq::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("ZipExtract error: {0}")]
ZipExtract(#[from] zip_extract::ZipExtractError),
#[error("Git error: {0}")]
Git(#[from] git2::Error),
#[error("Option conversion error")]
Option(),
}
pub fn update_from_releases<T: AsRef<str> + std::fmt::Display>(object_url: T,
target_folder: T,
max_buffer: usize) -> Result<(), Error> {
let path = Path::new(target_folder.as_ref());
std::fs::create_dir_all(path)?;
let version = path.join("version.txt");
let new_version = object_url.as_ref();
if version.exists() {
tracing::debug!("Version marker exists, checking version");
let old_version = std::fs::read_to_string(&version)?;
if old_version.eq(new_version) {
tracing::debug!("The version to download is the current one, skipping ...");
return Ok(())
} else {
tracing::debug!("The old version is: {old_version}, new version: {new_version}")
}
} else {
tracing::debug!("Version marker doesn't exist, fetching resources for first time")
}
tracing::debug!("Starting the download of static assets from {object_url}");
let mut bytes: Vec<u8> = Vec::with_capacity(max_buffer);
let agent = ureq::config::Config::builder()
.timeout_send_body(Some(std::time::Duration::from_secs(60)))
.timeout_recv_body(Some(std::time::Duration::from_secs(60)))
.build()
.new_agent();
let response = agent.get(new_version).call()?;
response.into_body().as_reader().read_to_end(&mut bytes)?;
tracing::debug!("Downloading assets from: {object_url} finished, extracting into {target_folder}");
zip_extract::extract(Cursor::new(bytes), path, true)?;
tracing::debug!("Resources extracted");
let mut file = std::fs::File::create(&version)?;
file.write_all(new_version.as_bytes())?;
Ok(())
}
pub fn clone_or_update_repository<T: AsRef<str> + std::fmt::Display>(repository_url: T,
repository_path: T,
ref_name: T,
discard_local_changes: bool) -> Result<(), Error> {
// Open existing repository or clone if not existing
let path = Path::new(repository_path.as_ref());
let repository = match path.exists() {
true => {
tracing::debug!("Path {repository_path} exists, opening repository");
Repository::open(repository_path.as_ref())
}
false => clone_repository(repository_url.as_ref(), path)
}?;
// TODO: Fetch remote updates????
// Get reference
let (object, reference) = repository.revparse_ext(ref_name.as_ref())?;
tracing::debug!("Reference {ref_name} found in repository");
let update_needed = match &reference {
Some(reference) => repository.head()?.eq(reference),
None => repository.head()?.target().ok_or(Error::Option())?.eq(&object.id()),
};
// If we are already in commit, no need to modify anything
if update_needed {
// Discard all local changes
if discard_local_changes {
tracing::debug!("discard_local_changes was set to true, changes will be discarded");
repository.checkout_head(None)?;
} else {
tracing::warn!("discard_local_changes was set to false, changes will be kept");
}
// Checkout required remote
repository.checkout_tree(&object, None)?;
// Checkout the needed commit
match reference {
Some(repo_reference) => repository.set_head(repo_reference.name().unwrap())?,
None => repository.set_head_detached(object.id())?,
}
tracing::debug!("Repository has been updated to {ref_name}");
} else {
tracing::debug!("Repository is already at the desired reference, no update needed");
};
Ok(())
}
fn clone_repository(repository_url: &str, path: &Path) -> Result<Repository, git2::Error> {
tracing::debug!(
"Path {path:?} doesn't exists, cloning repository at {repository_url}"
);
let mut remote_callbacks = RemoteCallbacks::new();
remote_callbacks.sideband_progress(|data| {
tracing::debug!("remote: {}", std::str::from_utf8(data).unwrap());
std::io::stdout().flush().unwrap();
true
});
remote_callbacks.transfer_progress(|stats| {
if stats.received_objects() == stats.total_objects() {
tracing::debug!(
"Resolving deltas {}/{}\r",
stats.indexed_deltas(),
stats.total_deltas()
);
} else if stats.total_objects() > 0 {
tracing::debug!(
"Received {}/{} objects ({}) in {} bytes\r",
stats.received_objects(),
stats.total_objects(),
stats.indexed_objects(),
stats.received_bytes()
);
}
std::io::stdout().flush().unwrap();
true
});
let mut fetch_options = FetchOptions::new();
fetch_options.remote_callbacks(remote_callbacks);
let result = RepoBuilder::new()
.fetch_options(fetch_options)
.clone(repository_url, path);
result
}