cobble_core/minecraft/resourcepacks/
mod.rs1mod 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#[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 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 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 if !path.as_ref().is_file() {
66 trace!("Entry is not a file.");
67 return Ok(None);
68 }
69
70 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 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}