#![allow(clippy::too_many_lines)]
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use crate::{
config::Cfg,
db::{
mods_get_info, userlist_change_versions, userlist_get_all_ids,
userlist_get_applicable_versions, userlist_get_current_version,
userlist_get_set_version,
},
error::{EType, MLErr, MLE},
files::{
clean_list_dir, delete_version, disable_version, download_versions,
},
modrinth::{extract_current_version, versions, Version},
List, 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()
.map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?,
));
update_p.set_style(
ProgressStyle::with_template(STYLE_BAR_POS)
.map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
.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).map_err(|_| {
MLErr::new(EType::LibIndicatif, "template error")
})?,
);
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()
.map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?,
),
);
list_u_p.set_style(
ProgressStyle::with_template(STYLE_BAR_POS)
.map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
.progress_chars(PROGRESS_CHARS),
);
let mut current_versions: Vec<(String, String)> = vec![];
let mut updatestack: Vec<Version> = vec![];
for id in mods {
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);
continue;
}
//Getting current installed version for disable or delete
let disable_version =
userlist_get_current_version(config, ¤t_list.id, &id)?;
updatestack.push(
match specific_update(
config,
clean,
current_list.clone(),
&id,
&list_u_p,
)
.await
{
Ok(ver) => {
current_versions.push((disable_version, id));
ver
}
Err(e) => {
if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
} else {
return Err(e);
};
list_u_p.inc(1);
continue;
}
},
);
list_u_p.inc(1);
}
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().map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?,
),
);
d_p.set_style(
ProgressStyle::with_template(STYLE_BAR_POS)
.map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
.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 specific_update(
config: &Cfg,
clean: bool,
list: List,
id: &str,
progress: &ProgressBar,
) -> MLE<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: Vec<Version> = vec![];
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 current_ver = match applicable_versions
.into_iter()
.find(|ver| ver.id == current_str)
.ok_or("!no current version in applicable_versions")
{
Ok(v) => Ok(v),
Err(e) => Err(MLErr::new(EType::Other, e)),
}?;
current.push(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_empty() {
return Err(MLErr::new(EType::ModError, "NO_UPDATE_AVAILABLE"));
};
Ok(current[0].clone())
}