1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use crate::error::CobbleResult;
use crate::minecraft::{load_resourcepacks, parse_resourcepack, Resourcepack, ResourcepackType};
use crate::Instance;
use std::path::{Path, PathBuf};
use tokio::fs::create_dir_all;

impl Instance {
    /// Path to the .minecraft/resourcepacks or .minecraft/texturepacks folder.
    #[cfg_attr(doc_cfg, doc(cfg(feature = "resourcepacks")))]
    pub fn resourcepacks_path(&self) -> PathBuf {
        let mut resourcepacks_path = self.dot_minecraft_path();

        match self.resourcepacks_type() {
            ResourcepackType::Resourcepack => resourcepacks_path.push("resourcepacks"),
            ResourcepackType::Texturepack => resourcepacks_path.push("texturepacks"),
        }

        resourcepacks_path
    }

    /// Gets whether the instance uses resource- or texturepacks.
    #[cfg_attr(doc_cfg, doc(cfg(feature = "resourcepacks")))]
    pub fn resourcepacks_type(&self) -> ResourcepackType {
        if Resourcepack::TEXTUREPACK_VERSIONS.contains(&self.version.as_str()) {
            ResourcepackType::Texturepack
        } else {
            ResourcepackType::Resourcepack
        }
    }

    /// Loads all resourcepacks from this instance.
    #[cfg_attr(doc_cfg, doc(cfg(feature = "resourcepacks")))]
    pub async fn load_resourcepacks(&self) -> CobbleResult<Vec<Resourcepack>> {
        load_resourcepacks(self.dot_minecraft_path()).await
    }

    /// Adds a resourcepack.
    /// Tries to put it in the apropriate folder for the current version.
    /// If it fails to determine the correct folder, it assumes the instance uses resourcepacks.
    ///
    /// Returns `None` when `src` is not a valid resourcepack.
    #[instrument(name = "add_resourcepack", level = "trace", skip_all, fields(src))]
    #[cfg_attr(doc_cfg, doc(cfg(feature = "resourcepacks")))]
    pub async fn add_resourcepack(
        &self,
        src: impl AsRef<Path>,
    ) -> CobbleResult<Option<Resourcepack>> {
        let _type = self.resourcepacks_type();

        trace!("Validating src...");
        if parse_resourcepack(PathBuf::from(src.as_ref()), _type)
            .await?
            .is_none()
        {
            return Ok(None);
        }

        trace!("Building new path for resourcepack");
        let file_name = src
            .as_ref()
            .file_name()
            .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "Path ends with '..'"))?;
        let mut resourcepack_path = self.resourcepacks_path();
        resourcepack_path.push(file_name);
        tracing::Span::current().record("dest", resourcepack_path.to_string_lossy().to_string());

        if let Some(parent) = resourcepack_path.parent() {
            trace!("Creating resourcepacks folder...");
            create_dir_all(parent).await?;
        }

        trace!("Copying resourcepack...");
        tokio::fs::copy(src, &resourcepack_path).await?;

        trace!("Parsing new resourcepack...");
        let resourcepack = parse_resourcepack(resourcepack_path, _type).await?;
        Ok(resourcepack)
    }
}