1use std::{
2 fs,
3 io::{Error, ErrorKind},
4};
5
6use debcontrol::{parse_str, Paragraph};
7use glob::glob;
8
9use crate::{
10 file::{extract, Control, PathItem, Version},
11 shared::{paragraph_contains, PackageWithVersion},
12};
13
14pub struct Deb {
40 path: &'static str,
41 pub extracted_path: Option<String>,
42}
43
44impl Deb {
48 pub fn new(path: &'static str) -> Self {
49 Deb {
50 path,
51 extracted_path: None,
52 }
53 }
54
55 pub fn extract(&mut self) -> Result<&mut Self, Error> {
59 self.extracted_path = Some(extract(&self.path).unwrap());
60 Ok(self)
61 }
62
63 fn extract_check(&self) -> Result<(), Error> {
67 if self.extracted_path.is_none() {
68 return Err(Error::new(
69 ErrorKind::Other,
70 "This deb file has not been extracted. Please run `extract()` before calling `retrieve_control`",
71 ));
72 };
73
74 Ok(())
75 }
76
77 pub fn version(&self) -> Result<Version, Error> {
81 self.extract_check()?;
82
83 let version = fs::read_to_string(format!(
84 "{}debian-binary",
85 self.extracted_path.as_ref().unwrap()
86 ))?;
87
88 let version = match &(*version) {
89 "1.0\n" => Version::V1_0,
90 "2.0\n" => Version::V2_0,
91 _ => Version::VUnknown,
92 };
93
94 Ok(version)
95 }
96
97 pub fn install_tree(&self) -> Result<Vec<PathItem>, Error> {
101 let mut install_tree = Vec::new();
102
103 let root = format!("{}data/", self.extracted_path.as_ref().unwrap());
104
105 for entry in glob(&format!("{}**/*", root)).expect("Failed to read glob pattern") {
106 match entry {
107 Ok(path) => {
108 let path = path.as_path();
109 let path_str = path.to_str().unwrap();
110 let path_rel_to_root: Vec<&str> = path_str.split(&root).collect();
111 let path_rel_to_root = format!("/{}", path_rel_to_root[1]);
112
113 if path.is_file() {
114 install_tree.push(PathItem {
115 real: path_str.to_string(),
116 move_to: path_rel_to_root,
117 });
118 }
119 }
120 Err(e) => println!("{:?}", e),
121 }
122 }
123
124 Ok(install_tree)
125 }
126
127 fn get_control_string(&self, control: &Paragraph, query: &str) -> String {
128 paragraph_contains(control.clone(), query.to_string())
129 .unwrap()
130 .value
131 }
132
133 fn str_option_to_number(&self, option: Option<String>) -> Option<u64> {
134 if let Some(option) = option {
135 Some(option.parse().unwrap())
136 } else {
137 None
138 }
139 }
140
141 fn get_control_option_str(&self, control: &Paragraph, query: &str) -> Option<String> {
142 let item = paragraph_contains(control.clone(), query.to_string());
143
144 if let Some(item) = item {
145 Some(item.value)
146 } else {
147 None
148 }
149 }
150
151 fn get_package_name(&self, control: &Paragraph, query: &str) -> Vec<PackageWithVersion> {
152 let item = paragraph_contains(control.clone(), query.to_string());
153
154 let mut deps = Vec::new();
155
156 if let Some(item) = item {
157 let input: Vec<&str> = item.value.split(',').collect();
158
159 input
160 .into_iter()
161 .for_each(|dep| deps.push(PackageWithVersion::from_str(dep)));
162 }
163
164 deps
165 }
166
167 pub fn retrieve_control(&self) -> Result<Control, Error> {
171 self.extract_check()?;
172
173 let control_raw = fs::read_to_string(format!(
174 "{}control/control",
175 self.extracted_path.as_ref().unwrap()
176 ))?;
177 let control = parse_str(&control_raw).unwrap()[0].clone();
178
179 let package = self.get_control_string(&control, "Package");
180 let version = self.get_control_string(&control, "Version");
181 let architecture = self.get_control_string(&control, "Architecture");
182 let maintainer = self.get_control_string(&control, "Maintainer");
183 let description = self
184 .get_control_string(&control, "Description")
185 .replace('\n', " ");
186
187 let source = self.get_control_option_str(&control, "Source");
188 let section = self.get_control_option_str(&control, "Section");
189 let priority = self.get_control_option_str(&control, "Priority");
190 let essential = self.get_control_option_str(&control, "Essential");
191 let install_size = self.get_control_option_str(&control, "Installed-Size");
192 let homepage = self.get_control_option_str(&control, "Homepage");
193 let built_using = self.get_control_option_str(&control, "Built-Using");
194
195 let install_size = self.str_option_to_number(install_size);
196
197 let depends = self.get_package_name(&control, "Depends");
199 let pre_depends = self.get_package_name(&control, "Pre-Depends");
200 let recommends = self.get_package_name(&control, "Recommends");
201 let suggests = self.get_package_name(&control, "Suggests");
202 let enhances = self.get_package_name(&control, "Enhances");
203 let breaks = self.get_package_name(&control, "Breaks");
204 let conflicts = self.get_package_name(&control, "Conflicts");
205
206 Ok(Control {
207 package,
208 source,
209 version,
210 section,
211 priority,
212 architecture,
213 essential,
214 install_size,
215 maintainer,
216 description,
217 homepage,
218 built_using,
219 depends,
220 pre_depends,
221 recommends,
222 suggests,
223 breaks,
224 enhances,
225 conflicts,
226 })
227 }
228}