conduit_cli/core/engine/io/
conduit.rs1use 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}