Skip to main content

conduit_cli/core/engine/io/
conduit.rs

1use std::{
2    fs::File,
3    path::{Path, PathBuf},
4};
5
6use crate::{
7    core::{
8        engine::{archive::SafeArchive, io::IncludeFile},
9        schemas::{include::ConduitInclude, lock::Lockfile, manifest::Manifest},
10    },
11    errors::{ConduitError, ConduitResult},
12    paths::ConduitPaths,
13};
14
15pub struct ConduitModpackManager {
16    pub manifest: Manifest,
17    pub lock: Lockfile,
18    pub include: ConduitInclude,
19}
20
21impl ConduitModpackManager {
22    pub fn new(
23        path: PathBuf,
24        manifest: Manifest,
25        lock: Lockfile,
26        include: ConduitInclude,
27        root: &Path,
28    ) -> ConduitResult<Self> {
29        let mut writer = SafeArchive::create(path)?;
30
31        SafeArchive::serialize_and_add(&mut writer, ConduitPaths::manifest_name(), &manifest)?;
32        SafeArchive::serialize_and_add(&mut writer, ConduitPaths::lockfile_name(), &lock)?;
33
34        let include_content = include.paths.join("\n");
35        SafeArchive::add_file(
36            &mut writer,
37            ConduitPaths::include_name(),
38            include_content.as_bytes(),
39        )?;
40
41        for file_path in include.scan(root) {
42            if file_path.is_file() {
43                let relative = file_path
44                    .strip_prefix(root)
45                    .map_err(|_| ConduitError::Storage("Failed to strip prefix".to_string()))?;
46
47                let zip_path = format!(
48                    "overrides/{}",
49                    relative.to_string_lossy().replace('\\', "/")
50                );
51
52                let file = File::open(&file_path)?;
53                SafeArchive::add_file_from_reader(&mut writer, &zip_path, file)?;
54            }
55        }
56
57        writer.finish().map_err(|e| {
58            ConduitError::Storage(format!("Failed to finalize conduit modpack: {e}"))
59        })?;
60
61        Ok(Self {
62            manifest,
63            lock,
64            include,
65        })
66    }
67
68    pub fn open(path: PathBuf) -> ConduitResult<Self> {
69        let mut zip = SafeArchive::open(path)?;
70
71        let manifest = SafeArchive::read_and_deserialize(&mut zip, ConduitPaths::manifest_name())?;
72        let lock = SafeArchive::read_and_deserialize(&mut zip, ConduitPaths::lockfile_name())?;
73
74        let include_raw = SafeArchive::read_metadata(&mut zip, ConduitPaths::include_name())?;
75        let patterns = include_raw
76            .lines()
77            .map(|l| l.trim().to_string())
78            .filter(|l| !l.is_empty())
79            .collect();
80
81        let include = ConduitInclude { paths: patterns };
82
83        Ok(Self {
84            manifest,
85            lock,
86            include,
87        })
88    }
89}