1use std::{
4 fs::create_dir_all,
5 path::{Path, PathBuf},
6};
7
8use alpm_compress::compression::CompressionSettings;
9#[cfg(doc)]
10use alpm_pkginfo::PackageInfo;
11use alpm_types::PackageFileName;
12use fluent_i18n::t;
13
14use crate::input::PackageInput;
15#[cfg(doc)]
16use crate::package::Package;
17
18#[derive(Clone, Debug)]
20pub struct OutputDir(PathBuf);
21
22impl OutputDir {
23 pub fn new(path: PathBuf) -> Result<Self, crate::Error> {
38 if !path.is_absolute() {
39 return Err(alpm_common::Error::NonAbsolutePaths {
40 paths: vec![path.clone()],
41 }
42 .into());
43 }
44
45 if !path.exists() {
46 create_dir_all(&path).map_err(|source| crate::Error::IoPath {
47 path: path.clone(),
48 context: t!("error-io-create-output-dir"),
49 source,
50 })?;
51 }
52
53 let metadata = path.metadata().map_err(|source| crate::Error::IoPath {
54 path: path.clone(),
55 context: t!("error-io-get-metadata"),
56 source,
57 })?;
58
59 if !metadata.is_dir() {
60 return Err(alpm_common::Error::NotADirectory { path: path.clone() }.into());
61 }
62
63 if metadata.permissions().readonly() {
64 return Err(crate::Error::PathIsReadOnly { path: path.clone() });
65 }
66
67 Ok(Self(path))
68 }
69
70 pub fn as_path(&self) -> &Path {
72 self.0.as_path()
73 }
74
75 pub fn to_path_buf(&self) -> PathBuf {
77 self.0.to_path_buf()
78 }
79
80 pub fn join(&self, path: impl AsRef<Path>) -> PathBuf {
82 self.0.join(path)
83 }
84}
85
86impl AsRef<Path> for OutputDir {
87 fn as_ref(&self) -> &Path {
88 &self.0
89 }
90}
91
92#[derive(Clone, Debug)]
100pub struct PackageCreationConfig {
101 package_input: PackageInput,
102 output_dir: OutputDir,
103 compression: CompressionSettings,
104}
105
106impl PackageCreationConfig {
107 pub fn new(
117 package_input: PackageInput,
118 output_dir: OutputDir,
119 compression: CompressionSettings,
120 ) -> Result<Self, crate::Error> {
121 if package_input.input_dir() == output_dir.as_path() {
122 return Err(crate::Error::InputDirIsOutputDir {
123 path: package_input.input_dir().to_path_buf(),
124 });
125 }
126 if output_dir.as_path().starts_with(package_input.input_dir()) {
127 return Err(crate::Error::OutputDirInInputDir {
128 input_path: package_input.input_dir().to_path_buf(),
129 output_path: output_dir.to_path_buf(),
130 });
131 }
132 if package_input.input_dir().starts_with(output_dir.as_path()) {
133 return Err(crate::Error::InputDirInOutputDir {
134 input_path: package_input.input_dir().to_path_buf(),
135 output_path: output_dir.to_path_buf(),
136 });
137 }
138
139 Ok(Self {
140 compression,
141 package_input,
142 output_dir,
143 })
144 }
145
146 pub fn package_input(&self) -> &PackageInput {
148 &self.package_input
149 }
150
151 pub fn output_dir(&self) -> &OutputDir {
153 &self.output_dir
154 }
155
156 pub fn compression(&self) -> &CompressionSettings {
158 &self.compression
159 }
160}
161
162impl From<&PackageCreationConfig> for PackageFileName {
163 fn from(value: &PackageCreationConfig) -> Self {
165 Self::new(
166 match value.package_input.package_info() {
167 alpm_pkginfo::PackageInfo::V1(package_info) => package_info.pkgname.clone(),
168 alpm_pkginfo::PackageInfo::V2(package_info) => package_info.pkgname.clone(),
169 },
170 match value.package_input.package_info() {
171 alpm_pkginfo::PackageInfo::V1(package_info) => package_info.pkgver.clone(),
172 alpm_pkginfo::PackageInfo::V2(package_info) => package_info.pkgver.clone(),
173 },
174 match value.package_input.package_info() {
175 alpm_pkginfo::PackageInfo::V1(package_info) => package_info.arch.clone(),
176 alpm_pkginfo::PackageInfo::V2(package_info) => package_info.arch.clone(),
177 },
178 (&value.compression).into(),
179 )
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use std::fs::File;
186
187 use tempfile::tempdir;
188 use testresult::TestResult;
189
190 use super::*;
191
192 #[test]
194 fn output_dir_new_creates_dir() -> TestResult {
195 let temp_dir = tempdir()?;
196 let non_existing_path = temp_dir.path().join("non-existing");
197 if let Err(error) = OutputDir::new(non_existing_path) {
198 panic!("Failed although it should have succeeded:\n{error}");
199 }
200
201 Ok(())
202 }
203
204 #[test]
206 fn output_dir_new_fails() -> TestResult {
207 assert!(matches!(
208 OutputDir::new(PathBuf::from("test")),
209 Err(crate::Error::AlpmCommon(
210 alpm_common::Error::NonAbsolutePaths { paths: _ }
211 ))
212 ));
213
214 let temp_dir = tempdir()?;
215 let file_path = temp_dir.path().join("non-existing");
216 let _file = File::create(&file_path)?;
217 assert!(matches!(
218 OutputDir::new(file_path),
219 Err(crate::Error::AlpmCommon(
220 alpm_common::Error::NotADirectory { path: _ }
221 ))
222 ));
223
224 Ok(())
225 }
226
227 #[test]
229 fn output_dir_as_ref() -> TestResult {
230 let temp_dir = tempdir()?;
231 let path = temp_dir.path();
232
233 let output_dir = OutputDir::new(path.to_path_buf())?;
234
235 assert_eq!(output_dir.as_ref(), path);
236
237 Ok(())
238 }
239}