cobble_core/instance/fabric/
backup.rs

1use crate::error::{CobbleError, CobbleResult};
2use crate::instance::backup::{
3    append_assets, append_instance_folder, append_instance_information, append_libraries,
4    append_log_config, LIBRARIES_FOLDER,
5};
6use crate::minecraft::models::{AssetIndex, VersionData};
7use crate::minecraft::FabricVersionData;
8use crate::Instance;
9use flate2::write::GzEncoder;
10use std::fs::File;
11use std::path::PathBuf;
12
13impl Instance {
14    /// Exports the instance using a gzip compressed tar archive.
15    /// The `compression` is a level is an integer from 0-9 where 0 means "no
16    /// compression" and 9 means "take as long as you'd like".
17    ///
18    /// The `offline` parameter determines whether all needed assets are packaged as well.
19    #[instrument(
20        name = "export_instance",
21        level = "trace",
22        skip_all,
23        fields(
24            instance_path = %self.instance_path.to_string_lossy(),
25            offline,
26            dest,
27            compression,
28        )
29    )]
30    #[cfg_attr(doc_cfg, doc(cfg(feature = "backup")))]
31    pub async fn export(
32        &self,
33        dest: impl AsRef<std::path::Path>,
34        offline: bool,
35        compression: u32,
36    ) -> CobbleResult<()> {
37        if !self.installed {
38            return Err(CobbleError::NotInstalled);
39        }
40
41        let this = self.clone();
42        let dest = PathBuf::from(dest.as_ref());
43
44        trace!("Getting version data");
45        let version_data = self.read_version_data().await?;
46        trace!("Getting asset index");
47        let asset_index = version_data.asset_index.fetch_index().await?;
48
49        let fabric_version_data = match &self.fabric_version {
50            Some(_) => {
51                trace!("Getting fabric version data");
52                Some(self.read_fabric_version_data().await?)
53            }
54            None => None,
55        };
56
57        tokio::task::spawn_blocking(move || {
58            this.export_blocking(
59                version_data,
60                fabric_version_data,
61                asset_index,
62                dest,
63                offline,
64                compression,
65            )
66        })
67        .await?
68    }
69
70    #[instrument(
71        name = "export_instance_blocking",
72        level = "trace",
73        skip_all,
74        fields(
75            instance_path = %self.instance_path.to_string_lossy(),
76            offline,
77            dest,
78            compression,
79        )
80    )]
81    fn export_blocking(
82        mut self,
83        version_data: VersionData,
84        fabric_version_data: Option<FabricVersionData>,
85        asset_index: AssetIndex,
86        dest: PathBuf,
87        offline: bool,
88        compression: u32,
89    ) -> CobbleResult<()> {
90        trace!("Creating archive file");
91        let archive_file = File::create(dest)?;
92        let encoder = GzEncoder::new(archive_file, flate2::Compression::new(compression));
93        let mut tar = tar::Builder::new(encoder);
94
95        append_instance_information(&mut self, offline, &mut tar)?;
96        append_instance_folder(&self, &mut tar)?;
97
98        if offline {
99            append_libraries(&self, &version_data, &mut tar)?;
100
101            if let Some(fabric_version_data) = fabric_version_data {
102                append_fabric_libraries(&self, &fabric_version_data, &mut tar)?;
103            }
104
105            append_assets(&self, &asset_index, &version_data, &mut tar)?;
106            append_log_config(&self, &version_data, &mut tar)?;
107        }
108
109        trace!("Flushing archive");
110        let mut encoder = tar.into_inner()?;
111        encoder.try_finish()?;
112
113        Ok(())
114    }
115}
116
117fn append_fabric_libraries(
118    instance: &Instance,
119    fabric_version_data: &FabricVersionData,
120    tar: &mut tar::Builder<GzEncoder<File>>,
121) -> CobbleResult<()> {
122    trace!("Adding fabric libraries to archive");
123    let libraries_path = instance.libraries_path();
124
125    fabric_version_data
126        .libraries
127        .iter()
128        .try_for_each(|library| -> CobbleResult<()> {
129            // File on disk
130            let file_path = library.jar_path(&libraries_path);
131            let mut file = File::open(file_path)?;
132
133            // Path in archive
134            let mut relative_path = PathBuf::from(LIBRARIES_FOLDER);
135            relative_path.push(library.relative_jar_path());
136
137            trace!("Adding library {} to archive", &library.name);
138            tar.append_file(relative_path, &mut file)?;
139
140            Ok(())
141        })?;
142
143    Ok(())
144}