use crate::error::CobbleResult;
use crate::utils::Either;
use async_zip::read::seek::ZipFileReader;
use std::collections::HashMap;
use std::path::PathBuf;
use tokio::fs::{remove_file, rename, File};
#[cfg_attr(doc_cfg, doc(cfg(feature = "loader-mods")))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug)]
pub struct LoaderMod {
pub name: String,
pub version: String,
pub description: Option<String>,
pub icon_path: Option<String>,
pub path: PathBuf,
pub enabled: bool,
}
impl LoaderMod {
pub(crate) const DISABLED_JAR_SUFFIX: &str = ".jar.disabled";
pub(crate) const ENABLED_JAR_SUFFIX: &str = ".jar";
#[instrument(
name = "remove_loader_mod",
level = "trace",
skip_all,
fields(
name,
path = %self.path.to_string_lossy(),
)
)]
pub async fn remove(self) -> CobbleResult<()> {
remove_file(&self.path).await?;
Ok(())
}
#[instrument(
name = "enable_loader_mod",
level = "trace",
skip_all,
fields(
name,
path = %self.path.to_string_lossy(),
)
)]
pub async fn enable(&mut self) -> CobbleResult<()> {
let new_path = self
.path
.to_string_lossy()
.to_string()
.replace(Self::DISABLED_JAR_SUFFIX, Self::ENABLED_JAR_SUFFIX);
rename(&self.path, &new_path).await?;
self.path = PathBuf::from(new_path);
self.enabled = true;
Ok(())
}
#[instrument(
name = "disable_loader_mod",
level = "trace",
skip_all,
fields(
name,
path = %self.path.to_string_lossy(),
)
)]
pub async fn disable(&mut self) -> CobbleResult<()> {
let new_path = self
.path
.to_string_lossy()
.to_string()
.replace(Self::ENABLED_JAR_SUFFIX, Self::DISABLED_JAR_SUFFIX);
rename(&self.path, &new_path).await?;
self.path = PathBuf::from(new_path);
self.enabled = false;
Ok(())
}
#[instrument(
name = "loader_mod_icon",
level = "trace",
skip_all,
fields(
name = self.name,
path = %self.path.to_string_lossy(),
)
)]
pub async fn icon(&self) -> CobbleResult<Option<Vec<u8>>> {
trace!("Trying to extract icon from archive");
let mut file = File::open(&self.path).await?;
let mut archive = ZipFileReader::new(&mut file).await?;
let icon_path = match &self.icon_path {
Some(path) => path,
None => return Ok(None),
};
let icon = match archive.entry(icon_path) {
Some((i, _entry)) => {
trace!("Found icon file");
let entry_reader = archive.entry_reader(i).await?;
let bytes = entry_reader.read_to_end_crc().await?;
Some(bytes)
}
None => {
trace!("Loader mod does not contain an icon file");
None
}
};
Ok(icon)
}
}
#[derive(Clone, Debug, serde::Deserialize)]
pub(crate) struct ModMetadata {
pub id: String,
pub version: String,
pub name: Option<String>,
pub description: Option<String>,
pub icon: Option<Either<String, HashMap<String, String>>>,
}