cobble-core 1.2.0

Library for managing, installing and launching Minecraft instances and more.
Documentation
use crate::error::{CobbleError, CobbleResult};
use crate::instance::backup::{
    append_assets, append_instance_folder, append_instance_information, append_libraries,
    append_log_config, LIBRARIES_FOLDER,
};
use crate::minecraft::models::{AssetIndex, VersionData};
use crate::minecraft::FabricVersionData;
use crate::Instance;
use flate2::write::GzEncoder;
use std::fs::File;
use std::path::PathBuf;

impl Instance {
    /// Exports the instance using a gzip compressed tar archive.
    /// The `compression` is a level is an integer from 0-9 where 0 means "no
    /// compression" and 9 means "take as long as you'd like".
    ///
    /// The `offline` parameter determines whether all needed assets are packaged as well.
    #[instrument(
        name = "export_instance",
        level = "trace",
        skip_all,
        fields(
            instance_path = %self.instance_path.to_string_lossy(),
            offline,
            dest,
            compression,
        )
    )]
    #[cfg_attr(doc_cfg, doc(cfg(feature = "backup")))]
    pub async fn export(
        &self,
        dest: impl AsRef<std::path::Path>,
        offline: bool,
        compression: u32,
    ) -> CobbleResult<()> {
        if !self.installed {
            return Err(CobbleError::NotInstalled);
        }

        let this = self.clone();
        let dest = PathBuf::from(dest.as_ref());

        trace!("Getting version data");
        let version_data = self.read_version_data().await?;
        trace!("Getting asset index");
        let asset_index = version_data.asset_index.fetch_index().await?;

        let fabric_version_data = match &self.fabric_version {
            Some(_) => {
                trace!("Getting fabric version data");
                Some(self.read_fabric_version_data().await?)
            }
            None => None,
        };

        tokio::task::spawn_blocking(move || {
            this.export_blocking(
                version_data,
                fabric_version_data,
                asset_index,
                dest,
                offline,
                compression,
            )
        })
        .await?
    }

    #[instrument(
        name = "export_instance_blocking",
        level = "trace",
        skip_all,
        fields(
            instance_path = %self.instance_path.to_string_lossy(),
            offline,
            dest,
            compression,
        )
    )]
    fn export_blocking(
        mut self,
        version_data: VersionData,
        fabric_version_data: Option<FabricVersionData>,
        asset_index: AssetIndex,
        dest: PathBuf,
        offline: bool,
        compression: u32,
    ) -> CobbleResult<()> {
        trace!("Creating archive file");
        let archive_file = File::create(dest)?;
        let encoder = GzEncoder::new(archive_file, flate2::Compression::new(compression));
        let mut tar = tar::Builder::new(encoder);

        append_instance_information(&mut self, offline, &mut tar)?;
        append_instance_folder(&self, &mut tar)?;

        if offline {
            append_libraries(&self, &version_data, &mut tar)?;

            if let Some(fabric_version_data) = fabric_version_data {
                append_fabric_libraries(&self, &fabric_version_data, &mut tar)?;
            }

            append_assets(&self, &asset_index, &version_data, &mut tar)?;
            append_log_config(&self, &version_data, &mut tar)?;
        }

        trace!("Flushing archive");
        let mut encoder = tar.into_inner()?;
        encoder.try_finish()?;

        Ok(())
    }
}

fn append_fabric_libraries(
    instance: &Instance,
    fabric_version_data: &FabricVersionData,
    tar: &mut tar::Builder<GzEncoder<File>>,
) -> CobbleResult<()> {
    trace!("Adding fabric libraries to archive");
    let libraries_path = instance.libraries_path();

    fabric_version_data
        .libraries
        .iter()
        .try_for_each(|library| -> CobbleResult<()> {
            // File on disk
            let file_path = library.jar_path(&libraries_path);
            let mut file = File::open(file_path)?;

            // Path in archive
            let mut relative_path = PathBuf::from(LIBRARIES_FOLDER);
            relative_path.push(library.relative_jar_path());

            trace!("Adding library {} to archive", &library.name);
            tar.append_file(relative_path, &mut file)?;

            Ok(())
        })?;

    Ok(())
}