1use colored::Colorize;
4use core::clone::Clone;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::fmt::{Debug, Display};
8use std::fs::{self, OpenOptions, create_dir_all};
9use std::io::Write;
10use std::path::{Path, PathBuf};
11use std::process::exit;
12use time::{OffsetDateTime, PrimitiveDateTime};
13
14#[derive(Serialize, Deserialize, PartialEq)]
15pub struct UninPackage {
16 pub name: String,
17 pub paths: Vec<PathBuf>,
18 pub change_date: String,
19 pub updated: bool,
20}
21
22impl Display for UninPackage {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 write!(
25 f,
26 "\tName: {}\n\tPaths: {:?}\n\tChange Date: {}\n",
27 self.name, self.paths, self.change_date
28 )
29 }
30}
31impl Clone for UninPackage {
32 fn clone(&self) -> Self {
33 Self {
34 name: self.name.clone(),
35 paths: self.paths.clone(),
36 change_date: self.change_date.clone(),
37 updated: self.updated,
38 }
39 }
40}
41impl UninPackage {
42 pub fn new(name: String) -> Self {
43 UninPackage {
44 name,
45 paths: Vec::new(),
46 change_date: "1970-01-01 00:00:00.0".to_string(),
47 updated: false,
48 }
49 }
50}
51pub struct DebuggableOptionUninPackage(pub Option<UninPackage>);
52
53impl Debug for DebuggableOptionUninPackage {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match &self.0 {
56 Some(p) => write!(f, "{}", p),
57 None => write!(f, "None"),
58 }
59 }
60}
61
62pub fn get_registry() {
63 if !registry_exists() {
64 println!("{}", "Registry couldn't be found nor created. Consider creating it manually at ~/.unin/registry/registry.json".red());
65 }
66 let home = std::env::var("HOME").unwrap();
67 let registry_path = PathBuf::from(format!("{}/.unin/registry/", home));
68 let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
69 let data = fs::read_to_string(registry_file).unwrap();
70 let packages: Vec<UninPackage> =
71 serde_json::from_str(&data).unwrap_or_else(|e| panic!("Shit happened: {:?}", e));
72 for p in packages {
73 println!("\n{}", p);
74 }
75}
76pub fn time_create() -> String {
77 let now_utc = OffsetDateTime::now_utc();
78 let primitive_now_utc = PrimitiveDateTime::new(
79 now_utc.date(),
80 now_utc
81 .time()
82 .truncate_to_second()
83 .replace_nanosecond(0)
84 .unwrap(),
85 );
86 PrimitiveDateTime::to_string(&primitive_now_utc)
87}
88
89pub fn time_read() -> PrimitiveDateTime {
90 let registry_path = PathBuf::from(format!(
91 "{}/.unin/registry/",
92 std::env::var("HOME").unwrap_or_else(|_| "/root/".to_string())
93 ));
94 let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
95 let data = fs::read_to_string(registry_file).unwrap();
96 let p: UninPackage =
97 serde_json::from_str(&data).unwrap_or_else(|e| panic!("Shit happened: {:?}", e));
98 let time_string: String = p.change_date.clone();
99 let x: PrimitiveDateTime =
100 PrimitiveDateTime::parse(&time_string, &time::format_description::well_known::Rfc3339)
101 .unwrap();
102 println!("{}", x);
103 x
104}
105
106pub fn registry_write(package: &UninPackage, print_console: bool) {
107 let registry_path = format!(
108 "{}/.unin/registry/registry.json",
109 std::env::var("HOME").unwrap()
110 );
111 let registry_dir = Path::new(®istry_path).parent().unwrap();
112
113 let _ = create_dir_all(registry_dir);
114
115 let existing_content = fs::read_to_string(®istry_path).unwrap_or_else(|_| String::new());
116 let mut is_new = true;
117 for line in existing_content.lines() {
118 if line.contains(package.name.trim()) {
119 is_new = false;
120 break;
121 }
122 }
123
124 let mut packages: Vec<UninPackage> =
125 serde_json::from_str(&existing_content).unwrap_or_else(|_| Vec::new());
126
127 let package_name = package.name.clone();
128
129 if let Some(pos) = packages.iter().position(|p| p.name == package.name) {
130 packages[pos].updated = true;
131 packages[pos].change_date = time_create();
132 packages[pos].paths = package.paths.clone();
133 } else {
134 packages.push((*package).clone())
135 }
136
137 let mut file = OpenOptions::new()
138 .write(true)
139 .create(true)
140 .truncate(true)
141 .open(®istry_path)
142 .unwrap();
143
144 serde_json::to_writer(&mut file, &packages).unwrap();
145 file.flush().unwrap();
146
147 if is_new && print_console {
148 println!(
149 "Registry for entry {} created successfully!",
150 package_name.green().underline()
151 );
152 } else if !is_new && print_console {
153 println!(
154 "Registry for entry {} updated successfully!",
155 package_name.green().underline()
156 );
157 }
158}
159pub fn registry_get_package(package_name: String) -> Option<UninPackage> {
160 if !registry_exists() {
161 println!("{}", "Registry couldn't be found nor created. Consider creating it manually at ~/.unin/registry/registry.json".red());
162 }
163 let home = std::env::var("HOME").unwrap();
164 let registry_path = PathBuf::from(format!("{}/.unin/registry/", home));
165 let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
166 let data = fs::read_to_string(registry_file).unwrap();
167 let packages: Vec<UninPackage> =
168 serde_json::from_str(&data).unwrap_or_else(|e| panic!("Shit happened: {:?}", e));
169
170 packages.into_iter().find(|p| p.name == package_name)
171}
172pub fn registry_exists() -> bool {
173 let registry_path = PathBuf::from(format!(
174 "{}/.unin/registry/",
175 std::env::var("HOME").unwrap()
176 ));
177 let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
178 if !registry_file.exists() {
179 if !create_dir_all(®istry_path).is_ok() {
180 println!("Failed to create registry for unPack ver 0.0.1");
182 false } else {
184 if let Ok(mut file) = fs::File::create(®istry_file) {
186 let _ = file.write_all(b"[]"); true } else {
189 println!("Failed to create registry file");
190 false }
192 }
193 } else {
194 true }
196}
197pub fn registry_uninstall(package_name: String) {
198 let home = std::env::var("HOME").unwrap();
199 let registry_path: PathBuf = PathBuf::from(format!("{}/.unin/registry/registry.json", home));
200 let registry_json: Value =
201 serde_json::from_str(&fs::read_to_string(registry_path.clone()).unwrap()).unwrap();
202 let mut packages: Vec<UninPackage> = serde_json::from_value(registry_json).unwrap();
203
204 let pkg = match packages.iter().find(|p| p.name == package_name) {
205 Some(p) => p,
206 None => {
207 eprintln!("No package named '{}' in registry", package_name);
208 return;
209 }
210 };
211
212 println!("\n{}", pkg);
213
214 let confirmation = dialoguer::Confirm::new()
215 .with_prompt("Are you sure you want to delete this application?")
216 .interact()
217 .unwrap();
218 if !confirmation {
219 return;
220 }
221
222 let mut remaining = packages.clone();
224
225 let path = match pkg.paths.first() {
226 Some(p) => p,
227 None => {
228 eprintln!("Package '{}' has no installed paths", pkg.name);
229 let ask = dialoguer::Confirm::new()
230 .with_prompt("Do you want to delete the entry anyway?")
231 .interact()
232 .unwrap();
233 if ask {
234 remaining.retain(|p| p.name != pkg.name);
235 let _ = fs::write(registry_path, serde_json::to_string(&remaining).unwrap());
236 println!("Registry entry for {} deleted", package_name);
237 }
238 return;
239 }
240 };
241
242 let path_str = path.to_str().unwrap_or_else(|| {
243 eprintln!("Path is not valid UTF-8");
244 std::process::exit(1);
245 });
246
247 let delete_status = std::process::Command::new("sudo")
248 .arg("rm")
249 .arg("-f")
250 .arg(path_str)
251 .output()
252 .unwrap_or_else(|e| panic!("Failed to delete the file: {}", e))
253 .status;
254
255 if !delete_status.success() {
256 println!("Failed to delete the file for {}", pkg.name);
257 let confirmation = dialoguer::Confirm::new()
258 .with_prompt("Do you want to delete the registry entry anyway?")
259 .interact()
260 .unwrap();
261 if confirmation {
262 remaining.retain(|p| p.name != package_name);
263 let _ = fs::write(registry_path, serde_json::to_string(&remaining).unwrap());
264 println!("Registry entry for {} deleted", package_name);
265 } else {
266 println!("Aborting");
267 }
268 } else {
269 packages.retain(|p| p.name != package_name);
270 let _ = fs::write(registry_path, serde_json::to_string(&packages).unwrap());
271 println!("Registry entry for {} deleted", package_name);
272 }
273}
274
275pub fn temp_test() {
276 let _x: UninPackage = UninPackage {
277 name: String::from("dev"),
278 paths: vec![PathBuf::from("/root/ad"), PathBuf::from("/root/da")],
279 change_date: time_create(),
280 updated: false,
281 };
282 get_registry();
283}
284pub fn return_registry_path() -> PathBuf {
285 PathBuf::from(format!(
286 "{}/.unin/registry/registry.json",
287 std::env::var("HOME").unwrap()
288 ))
289}
290
291pub fn update_check_registry() {
292 let registry_path = PathBuf::from(format!(
293 "{}/.unin/registry/registry.json",
294 std::env::var("HOME").unwrap()
295 ));
296 if !registry_exists() {
297 println!("{}", "Registry couldn't be found nor created. Consider creating it manually at ~/.unin/registry/registry.json".red());
298 }
299 let contents =
300 fs::read_to_string(registry_path).expect("Something went wrong reading the file");
301 let packages: Vec<UninPackage> = serde_json::from_str(&contents).unwrap();
302 for package in packages {
303 let mut new_paths: Vec<String> = Vec::new();
304 package.paths.iter().for_each(|path| {
305 if path.exists() {
306 new_paths.push(path.to_str().unwrap().to_string());
307 } else {
308 println!(
309 "{}",
310 "The binaries of this registry entry are missing. Do you want to remove it?"
311 .red()
312 );
313 registry_uninstall(package.name.clone())
314 }
315 });
316
317 if package.paths.is_empty() {
318 let confirmation = dialoguer::Confirm::new()
319 .with_prompt(format!(
320 "{}",
321 "The binaries of this registry entry are missing. Do you want to remove it?"
322 .red()
323 ))
324 .interact()
325 .unwrap();
326
327 if !confirmation {
328 exit(0);
329 unreachable!()
330 }
331 registry_uninstall(package.name.clone());
333 }
334
335 let new_package = UninPackage {
336 name: package.name.clone(),
337 paths: new_paths.iter().map(PathBuf::from).collect(),
338 change_date: package.change_date.clone(),
339 updated: package.updated,
340 };
341 registry_write(&new_package, false);
342 }
343}