1use std::{
10 fs::OpenOptions,
11 io::{self, Read, Write},
12 path::{Path, PathBuf},
13};
14
15use serde::{Deserialize, Serialize};
16
17use crate::{
18 io::{stream, DEFAULT_BUF_LEN},
19 metadata,
20 walk_dir::walk_dir,
21};
22
23#[derive(Serialize, Deserialize, Debug)]
24struct PackEntryMetadata(PathBuf, u64);
25
26impl PackEntryMetadata {
27 pub fn new<P>(path: P, file_len: u64) -> Self
28 where
29 P: AsRef<Path>,
30 {
31 PackEntryMetadata(path.as_ref().to_owned(), file_len)
32 }
33
34 pub fn path(&self) -> PathBuf {
35 self.0.to_owned()
36 }
37
38 pub fn file_len(&self) -> u64 {
39 self.1
40 }
41}
42
43#[derive(Debug)]
45pub enum ErrorKind {
46 Io(io::Error),
47 MetadataError(metadata::ErrorKind),
48 ConversionError,
49 PathAlreadyExists,
50 PathNotFound,
51 PathError,
52}
53type Result<T, E = ErrorKind> = std::result::Result<T, E>;
54
55pub fn create<P1, P2>(input_dir_path: P1, output_dir_path: P2) -> Result<()>
58where
59 P1: AsRef<Path>,
60 P2: AsRef<Path>,
61{
62 let mut dest = OpenOptions::new()
63 .write(true)
64 .create(true)
65 .open(output_dir_path.as_ref())
66 .map_err(|e| ErrorKind::Io(e))?;
67
68 let walk_dir = walk_dir(&input_dir_path).map_err(|e| ErrorKind::Io(e))?;
69
70 for entry in walk_dir {
71 let entry = entry.map_err(|e| ErrorKind::Io(e))?;
72
73 if entry.path().is_file() {
74 let mut source = OpenOptions::new()
75 .read(true)
76 .open(entry.path())
77 .map_err(|e| ErrorKind::Io(e))?;
78
79 let metadata = metadata::encode(PackEntryMetadata::new(
81 input_dir_path
82 .as_ref()
83 .strip_prefix(entry.path())
84 .map_err(|_| ErrorKind::PathError)?,
85 entry.metadata().map_err(|e| ErrorKind::Io(e))?.len(),
86 ))
87 .map_err(|e| ErrorKind::MetadataError(e))?;
88
89 dest.write_all(
91 &[
92 (metadata.len() as u16).to_be_bytes().as_ref(),
93 metadata.as_ref(),
94 ]
95 .concat(),
96 )
97 .map_err(|e| ErrorKind::Io(e))?;
98
99 stream(&mut source, &mut dest).map_err(|e| ErrorKind::Io(e))?;
100 }
101 }
102
103 Ok(())
104}
105
106pub fn unpack<P1, P2>(input_dir_path: P1, output_dir_path: P2) -> Result<()>
109where
110 P1: AsRef<Path>,
111 P2: AsRef<Path>,
112{
113 let mut source = OpenOptions::new()
114 .read(true)
115 .open(input_dir_path.as_ref())
116 .map_err(|e| ErrorKind::Io(e))?;
117
118 loop {
119 let mut len_bytes = [0u8; 2];
120 source
121 .read_exact(&mut len_bytes)
122 .map_err(|e| ErrorKind::Io(e))?;
123 let len = u16::from_be_bytes(len_bytes) as usize;
124 let mut metadata_bytes = vec![0u8; len];
125 source
126 .read_exact(&mut metadata_bytes)
127 .map_err(|e| ErrorKind::Io(e))?;
128 let metadata = metadata::decode::<PackEntryMetadata>(&metadata_bytes)
129 .map_err(|e| ErrorKind::MetadataError(e))?;
130
131 let mut file = OpenOptions::new()
132 .write(true)
133 .create(true)
134 .open(output_dir_path.as_ref().join(metadata.path()))
135 .map_err(|e| ErrorKind::Io(e))?;
136
137 let file_len = metadata.file_len();
138
139 let chunks = file_len.div_euclid(DEFAULT_BUF_LEN as u64);
142 let rem_len = file_len.rem_euclid(DEFAULT_BUF_LEN as u64) as usize;
145
146 let mut buffer = [0u8; DEFAULT_BUF_LEN];
149 for _ in 0..chunks {
150 source
151 .read_exact(&mut buffer)
152 .map_err(|e| ErrorKind::Io(e))?;
153 file.write_all(&buffer).map_err(|e| ErrorKind::Io(e))?;
154 }
155
156 let mut last = vec![0u8; rem_len];
159 source.read_exact(&mut last).map_err(|e| ErrorKind::Io(e))?;
160 file.write_all(&last).map_err(|e| ErrorKind::Io(e))?;
161 }
162}