cobble_core/minecraft/resourcepacks/
mod.rs

1mod resourcepack;
2
3use crate::error::CobbleError;
4use crate::error::CobbleResult;
5use futures::try_join;
6use futures::TryStreamExt;
7pub use resourcepack::*;
8use std::path::{Path, PathBuf};
9use tokio::fs::read_dir;
10use tokio_stream::wrappers::ReadDirStream;
11
12/// Loads all resourcepacks from the minecraft folder.
13#[cfg_attr(doc_cfg, doc(cfg(feature = "resourcepacks")))]
14#[instrument(
15    name = "load_resourcepacks",
16    level = "debug",
17    skip_all,
18    fields(minecraft_path)
19)]
20pub async fn load_resourcepacks(
21    minecraft_path: impl AsRef<Path> + Send,
22) -> CobbleResult<Vec<Resourcepack>> {
23    // Resourcepacks
24    let mut resourcepacks_path = PathBuf::from(minecraft_path.as_ref());
25    resourcepacks_path.push("resourcepacks");
26    let resourcepacks_future = load_packs(resourcepacks_path, ResourcepackType::Resourcepack);
27
28    // Texturepacks
29    let mut texturepacks_path = PathBuf::from(minecraft_path.as_ref());
30    texturepacks_path.push("texturepacks");
31    let texturepacks_future = load_packs(texturepacks_path, ResourcepackType::Texturepack);
32
33    let (resourcepacks, texturepacks) = try_join!(resourcepacks_future, texturepacks_future)?;
34
35    let mut packs = vec![];
36    packs.extend(resourcepacks);
37    packs.extend(texturepacks);
38
39    Ok(packs)
40}
41
42async fn load_packs(path: PathBuf, _type: ResourcepackType) -> CobbleResult<Vec<Resourcepack>> {
43    if !path.is_dir() {
44        trace!("{:?} directory is empty", _type);
45        return Ok(vec![]);
46    }
47
48    trace!("Loading {:?}s...", _type);
49    let file_stream = ReadDirStream::new(read_dir(path).await?);
50    let packs = file_stream
51        .map_err(CobbleError::from)
52        .try_filter_map(|e| parse_resourcepack(e.path(), _type))
53        .try_collect()
54        .await?;
55
56    Ok(packs)
57}
58
59#[instrument(name = "parse_resourcepack", level = "trace", skip_all, fields(path))]
60pub(crate) async fn parse_resourcepack(
61    path: impl AsRef<Path>,
62    _type: ResourcepackType,
63) -> CobbleResult<Option<Resourcepack>> {
64    // Check if file
65    if !path.as_ref().is_file() {
66        trace!("Entry is not a file.");
67        return Ok(None);
68    }
69
70    // Check if archive
71    let zip_mime = "application/zip".parse::<mime_guess::Mime>().unwrap();
72    let mime = match mime_guess::from_path(&path).first() {
73        Some(mime) => mime,
74        None => {
75            trace!("Could not get MIME type for file.");
76            return Ok(None);
77        }
78    };
79    if mime.type_() != zip_mime.type_() {
80        trace!("Entry is not an archive.");
81        return Ok(None);
82    }
83
84    // Parse
85    let name = match path.as_ref().file_name() {
86        Some(name) => name.to_string_lossy().to_string(),
87        None => return Ok(None),
88    };
89
90    Ok(Some(Resourcepack {
91        name,
92        path: PathBuf::from(path.as_ref()),
93        _type,
94    }))
95}