cobble_core/minecraft/log_files/
log_file.rs

1use crate::error::CobbleResult;
2use async_zip::read::seek::ZipFileReader;
3use std::path::{Path, PathBuf};
4use time::OffsetDateTime;
5use tokio::fs::{remove_file, File};
6
7/// Represents a single log file.
8/// On each launch the game creates a new log file and compresses the old one.
9#[cfg_attr(doc_cfg, doc(cfg(feature = "log-files")))]
10#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
11#[derive(Clone, Debug)]
12pub struct LogFile {
13    /// Filename
14    pub name: String,
15    /// Filepath
16    pub path: PathBuf,
17    /// Type
18    pub _type: LogFileType,
19    /// Modified timestamp on filesystem
20    #[cfg_attr(feature = "serde", serde(with = "time::serde::rfc3339::option"))]
21    pub modified: Option<OffsetDateTime>,
22}
23
24impl LogFile {
25    /// Removes the log file from disk.
26    ///
27    /// **Warning**: This will permanently delete the file!
28    #[instrument(
29        name = "remove_log_file",
30        level = "trace",
31        skip_all,
32        fields(
33            path = %self.path.to_string_lossy(),
34        )
35    )]
36    pub async fn remove(self) -> CobbleResult<()> {
37        remove_file(&self.path).await?;
38        Ok(())
39    }
40
41    /// Unpacks the log file if it is compressed.
42    /// If the file is **not** compressed, it copies the file to the target path.
43    #[instrument(
44        name = "extract_log_file",
45        level = "trace",
46        skip_all,
47        fields(
48            source = %self.path.to_string_lossy(),
49            target = %target.as_ref().to_string_lossy(),
50        )
51    )]
52    pub async fn unpack(&self, target: impl AsRef<Path> + Send) -> CobbleResult<()> {
53        match self._type {
54            LogFileType::Plain => {
55                trace!("Copying file to target location");
56                tokio::fs::copy(&self.path, target).await?;
57            }
58            LogFileType::GzipCompressed => {
59                trace!("Extracting log file to target location");
60                let mut file = File::open(&self.path).await?;
61                let mut archive = ZipFileReader::new(&mut file).await?;
62
63                let entry_reader = archive.entry_reader(0).await?;
64
65                let mut out_file = File::create(target).await?;
66                entry_reader.copy_to_end_crc(&mut out_file, 64000).await?;
67            }
68        }
69
70        Ok(())
71    }
72}
73
74/// Whether the file is compressed or in plain text.
75#[cfg_attr(doc_cfg, doc(cfg(feature = "log-files")))]
76#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
77#[derive(Clone, Copy, Debug)]
78pub enum LogFileType {
79    /// Plain text
80    Plain,
81    /// GZip compressed text
82    GzipCompressed,
83}