use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use crate::{
apis::modrinth::{extract_current_version, versions, Version},
config::Cfg,
data::list::List,
db::{
mods_get_info, userlist_change_versions, userlist_get_all_ids,
userlist_get_applicable_versions, userlist_get_current_version,
userlist_get_set_version,
},
errors::{Error, MLE},
files::{
clean_list_dir, delete_version, disable_version, download_versions,
},
PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION,
};
/// # Errors
pub async fn update(
config: &Cfg,
liststack: Vec<List>,
clean: bool,
direct_download: bool,
delete_old: bool,
) -> MLE<()> {
let mp = MultiProgress::new();
let update_p = mp.add(ProgressBar::new(liststack.len().try_into()?));
update_p.set_style(
ProgressStyle::with_template(STYLE_BAR_POS)?
.progress_chars(PROGRESS_CHARS),
);
for current_list in liststack {
update_p.set_message(format!("Update {}", current_list.id));
let list_p = mp.insert_before(&update_p, ProgressBar::new(2));
list_p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
list_p.set_message("Update mods");
let mods = userlist_get_all_ids(config, ¤t_list.id)?;
let list_u_p =
mp.insert_before(&list_p, ProgressBar::new(mods.len().try_into()?));
list_u_p.set_style(
ProgressStyle::with_template(STYLE_BAR_POS)?
.progress_chars(PROGRESS_CHARS),
);
let mut current_versions: Vec<(String, String)> = vec![];
let mut updatestack: Vec<Version> = vec![];
for id in mods {
update_mod(
config,
id,
list_u_p.clone(),
¤t_list,
&mut updatestack,
&mut current_versions,
clean,
)
.await?;
}
list_u_p.finish_with_message(format!(
"Updated mods in {}",
current_list.id
));
if clean {
list_p.set_message("Cleaning");
clean_list_dir(¤t_list)?;
};
if direct_download && !updatestack.is_empty() {
download_versions(
current_list.clone(),
config.clone(),
updatestack,
&mp,
&list_p,
)
.await?;
//Disable old versions
if !clean {
let d_p = mp.insert_before(
&list_p,
ProgressBar::new(
current_versions.len().try_into()?,
),
);
d_p.set_style(
ProgressStyle::with_template(STYLE_BAR_POS)?
.progress_chars(PROGRESS_CHARS),
);
for ver in current_versions {
if delete_old {
d_p.set_message(format!("Delete version {}", ver.0));
d_p.inc(1);
delete_version(¤t_list, &ver.0)?;
} else if ver.0 != "NONE" {
d_p.set_message(format!("Disable version {}", ver.0));
d_p.inc(1);
disable_version(config, ¤t_list, ver.0, ver.1)?;
};
}
let del_msg = if delete_old {
"Deleted all old versions"
} else {
"Disabled all old versions"
};
d_p.finish_with_message(del_msg);
}
};
list_p.finish_with_message(format!("Updated {}", current_list.id));
update_p.inc(1);
}
update_p.finish_with_message("Updated all lists");
Ok(())
}
async fn update_mod(
config: &Cfg,
id: String,
list_u_p: ProgressBar,
current_list: &List,
updatestack: &mut Vec<Version>,
current_versions: &mut Vec<(String, String)>,
clean: bool,
) -> MLE<()> {
let info = mods_get_info(config, &id)?;
list_u_p.set_message(format!("Update {}", info.title));
//Skip check if version is set
if userlist_get_set_version(config, ¤t_list.id, &id)? {
list_u_p.inc(1);
return Ok(());
}
//Getting current installed version for disable or delete
let disable_version =
userlist_get_current_version(config, ¤t_list.id, &id)?;
let version = specific_update(
config,
clean,
current_list.clone(),
&id,
&list_u_p,
).await?;
if let Some(v) = version {
updatestack.push(v);
current_versions.push((disable_version, id.to_string()));
}
list_u_p.inc(1);
Ok(())
}
async fn specific_update(
config: &Cfg,
clean: bool,
list: List,
id: &str,
progress: &ProgressBar,
) -> MLE<Option<Version>> {
let applicable_versions =
versions(&config.apis.modrinth, String::from(id), list.clone()).await?;
let mut versions: Vec<String> = vec![];
if applicable_versions.is_empty() {
versions.push(String::from("NONE"));
} else {
for ver in &applicable_versions {
versions.push(String::from(&ver.id));
}
}
let mut current: Option<Version> = None;
if clean || (versions.join("|") != userlist_get_applicable_versions(
config,
&list.id,
String::from(id),
)?)
{
let current_str = extract_current_version(applicable_versions.clone())?;
if !clean {
progress.println(format!(
"Found new version for {}",
mods_get_info(config, id).unwrap().title
));
}
//get new versions
let Some(current_ver) = applicable_versions.into_iter().find(|ver| ver.id == current_str) else {
return Err(Error::NoCurrentVersion);
};
current = Some(current_ver.clone());
let files = ¤t_ver.files;
let link = match files.clone().into_iter().find(|f| f.primary) {
Some(f) => f,
None => files[0].clone(),
}
.url;
userlist_change_versions(
config,
&list.id,
current_str,
versions.join("|"),
link,
id.to_string(),
)?;
};
if current.is_none() {
// No Update Available
Ok(None)
} else {
Ok(current)
}
}