1pub mod arch_package {
2
3 #[derive(Default)]
5 pub struct Package {
6 pub name: String,
7 pub dependencies: Vec<String>,
8 pub opt_dependencies: Vec<String>,
9 }
10
11 impl Package {
12 pub fn new(database_content: String) -> Self {
13 let mut package = Package::default();
14
15 let mut lines_iter = database_content.lines();
16
17 while let Some(current_line) = lines_iter.next() {
18 let current_line = current_line.trim();
19
20 if current_line == "%NAME%" {
21 if let Some(name) = lines_iter.next() {
22 package.name.push_str(name);
23 }
24 }
25
26 if current_line == "%DEPENDS%" {
27 while let Some(dependency) = lines_iter.next() {
28 let dependency = dependency.trim();
29
30 if dependency.is_empty() {
31 break;
32 } else {
33 package.dependencies.push(dependency.to_string());
34 }
35 }
36 }
37
38 if current_line == "%OPTDEPENDS%" {
39 while let Some(dependency) = lines_iter.next() {
40 let dependency = dependency.trim();
41
42 if dependency.is_empty() {
43 break;
44 } else if let Some(colon_index) = dependency.find(':') {
45 let dependency = &dependency[..colon_index];
47 package.opt_dependencies.push(dependency.to_string());
48 } else {
49 package.opt_dependencies.push(dependency.to_string());
50 }
51 }
52 }
53 }
54
55 package
56 }
57 }
58}
59
60pub mod package_database_reader {
62 use super::arch_package::Package;
63 use std::error::Error;
64 use std::fs;
65 use std::path::Path;
66
67 pub fn packages_reader(databases_path: &str) -> Result<Vec<Package>, Box<dyn Error>> {
69 let paths = fs::read_dir(databases_path)?;
70
71 let mut folders: Vec<String> = Vec::new();
72
73 for path in paths {
74 folders.push(format!(
75 "{}/{}",
76 path?.path().to_str().unwrap().to_owned(),
77 "desc"
78 ))
79 }
80
81 let mut packages: Vec<Package> = Vec::new();
82
83 for file in folders {
84 if Path::new(&file).exists() {
85 let content = fs::read_to_string(file)?;
86 let package = Package::new(content);
87 packages.push(package);
88 }
89 }
90
91 Ok(packages)
92 }
93}
94
95pub mod commandline_functions {
97 use super::arch_package::Package;
98 use crate::package_database_reader::packages_reader;
99 use std::collections::HashSet;
100 use std::process;
101
102 const DEFAULT_DATABASE_PATH: &str = "/var/lib/pacman/local/";
103
104 pub fn populate_packages() -> Vec<Package> {
106 let packages = match packages_reader(DEFAULT_DATABASE_PATH) {
107 Ok(packages) => packages,
108 Err(err) => {
109 eprintln!("Error: {}", err);
110 process::exit(1);
111 }
112 };
113 packages
114 }
115
116 pub fn get_packages_with_same_dependencies<'a>(
118 package_name: &str,
119 packages: &'a Vec<Package>,
120 ) -> Option<HashSet<&'a str>> {
121 let mut package_dependencies: &Vec<String> = &Vec::new();
122 let mut packages_with_same_dependencies: HashSet<&str> = HashSet::new();
123 let mut other_packages_found = false;
124
125 for package in packages {
127 if package.name == package_name {
128 package_dependencies = &package.dependencies;
129 break;
130 }
131 }
132
133 for package in packages {
135 if package.name == package_name {
136 continue;
137 }
138
139 for dep in &package.dependencies {
140 if package_dependencies.contains(dep) {
141 packages_with_same_dependencies.insert(&package.name);
142 other_packages_found = true;
143 }
144 }
145 }
146
147 if other_packages_found {
148 return Some(packages_with_same_dependencies);
149 }
150
151 None
152 }
153
154 pub fn get_unique_dependencies(
156 package_name: &str,
157 packages: Vec<Package>,
158 ) -> Option<Vec<String>> {
159 let mut package_dependencies: &Vec<String> = &Vec::new();
160 let mut unique_dependencies_found = false;
161
162 for package in &packages {
164 if package.name == package_name {
165 package_dependencies = &package.dependencies;
166 break;
167 }
168 }
169
170 let mut packages_dependencies_copy = package_dependencies.clone();
171
172 for package in &packages {
174 if package.name == package_name {
175 continue;
176 }
177
178 for dep in &package.dependencies {
179 if package_dependencies.contains(dep) {
180 if let Some(pos) = packages_dependencies_copy.iter().position(|x| x == dep) {
181 packages_dependencies_copy.remove(pos);
182 }
183 unique_dependencies_found = true;
184 }
185 }
186 }
187
188 if unique_dependencies_found && !packages_dependencies_copy.is_empty() {
189 return Some(packages_dependencies_copy);
190 }
191
192 None
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use std::collections::HashSet;
199
200 use super::arch_package::Package;
201 use super::commandline_functions::{
202 get_packages_with_same_dependencies, get_unique_dependencies,
203 };
204
205 #[test]
207 fn package_creation_test() {
208 let package_database = "
209%NAME%
210linux-kernel
211
212%DEPENDS%
213coreutils
214kmod
215initramfs
216
217%OPTDEPENDS%
218wireless-regdb: to set the correct wireless channels of your country
219linux-firmware: firmware images needed for some devices
220";
221
222 let package = Package::new(package_database.to_string());
223
224 assert_eq!(package.name, "linux-kernel");
225 assert_eq!(package.dependencies, vec!["coreutils", "kmod", "initramfs"]);
226 assert_eq!(
227 package.opt_dependencies,
228 vec!["wireless-regdb", "linux-firmware"]
229 );
230 }
231
232 fn get_packages() -> Vec<Package> {
233 vec![
234 Package {
235 name: String::from("a"),
236 dependencies: vec![
237 String::from("bottle"),
238 String::from("cup"),
239 String::from("plate"),
240 ],
241 opt_dependencies: vec![],
242 },
243 Package {
244 name: String::from("b"),
245 dependencies: vec![
246 String::from("bulb"),
247 String::from("wire"),
248 String::from("plate"),
249 ],
250 opt_dependencies: vec![],
251 },
252 Package {
253 name: String::from("c"),
254 dependencies: vec![
255 String::from("pluto"),
256 String::from("mars"),
257 String::from("cup"),
258 ],
259 opt_dependencies: vec![],
260 },
261 ]
262 }
263
264 #[test]
266 fn fetching_unique_dependencies_test() {
267 assert_eq!(
268 get_unique_dependencies("a", get_packages()),
269 Some(vec!["bottle".to_owned()])
270 );
271 }
272
273 #[test]
275 fn fetching_packages_with_same_deps_test() {
276 let mut packages_with_same_deps = HashSet::new();
277 packages_with_same_deps.insert("b");
278 packages_with_same_deps.insert("c");
279
280 assert_eq!(
281 get_packages_with_same_dependencies("a", &get_packages()),
282 Some(packages_with_same_deps)
283 )
284 }
285}