cobble_core/minecraft/log_files/
mod.rs1mod log_file;
2
3use crate::error::{CobbleError, CobbleResult};
4use futures::TryStreamExt;
5pub use log_file::*;
6use std::path::{Path, PathBuf};
7use std::time::SystemTime;
8use tokio::fs::{metadata, read_dir};
9use tokio_stream::wrappers::ReadDirStream;
10
11#[cfg_attr(doc_cfg, doc(cfg(feature = "log-files")))]
13#[instrument(
14 name = "load_log_files",
15 level = "debug",
16 skip_all,
17 fields(minecraft_path)
18)]
19pub async fn load_log_files(minecraft_path: impl AsRef<Path> + Send) -> CobbleResult<Vec<LogFile>> {
20 let mut logs_path = PathBuf::from(minecraft_path.as_ref());
21 logs_path.push("logs");
22
23 if !logs_path.is_dir() {
24 trace!("Logs directory is empty");
25 return Ok(vec![]);
26 }
27
28 trace!("Loading log files...");
29 let file_stream = ReadDirStream::new(read_dir(logs_path).await?);
30 let log_files = file_stream
31 .map_err(CobbleError::from)
32 .try_filter_map(|e| parse_log_file(e.path()))
33 .try_collect()
34 .await?;
35
36 Ok(log_files)
37}
38
39#[cfg_attr(doc_cfg, doc(cfg(feature = "log-files")))]
40#[instrument(name = "parse_log_file", level = "trace", skip_all, fields(path,))]
41async fn parse_log_file(path: impl AsRef<Path>) -> CobbleResult<Option<LogFile>> {
42 if !path.as_ref().is_file() {
44 trace!("Entry is not a file.");
45 return Ok(None);
46 }
47
48 let mime = match mime_guess::from_path(&path).first() {
50 Some(mime) => mime,
51 None => {
52 trace!("Could not get MIME type for file.");
53 return Ok(None);
54 }
55 };
56 let _type = if mime == mime_guess::mime::TEXT_PLAIN {
57 LogFileType::Plain
58 } else if mime == "application/gzip" {
59 LogFileType::GzipCompressed
60 } else {
61 trace!("Entry is neither plain text nor gzip compressed file");
62 return Ok(None);
63 };
64
65 let name = match path.as_ref().file_name() {
67 Some(name) => name.to_string_lossy().to_string(),
68 None => return Ok(None),
69 };
70
71 let modified = metadata(path.as_ref())
72 .await
73 .and_then(|m| m.modified())
74 .ok()
75 .and_then(|st| st.duration_since(SystemTime::UNIX_EPOCH).ok())
76 .and_then(|d| time::OffsetDateTime::from_unix_timestamp(d.as_secs() as i64).ok());
77
78 Ok(Some(LogFile {
79 name,
80 path: PathBuf::from(path.as_ref()),
81 _type,
82 modified,
83 }))
84}