Skip to main content

unin_bin/
registry.rs

1//unPack ver 0.0.1
2
3use 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(&registry_path).parent().unwrap();
112
113    let _ = create_dir_all(registry_dir);
114
115    let existing_content = fs::read_to_string(&registry_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(&registry_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(&registry_path).is_ok() {
180            // Create the DIRECTORY, not the file path
181            println!("Failed to create registry for unPack ver 0.0.1");
182            false //the directory didn't get created, so it doesn't exist.
183        } else {
184            // Create the empty registry file
185            if let Ok(mut file) = fs::File::create(&registry_file) {
186                let _ = file.write_all(b"[]"); // Write empty JSON array
187                true //it exists now
188            } else {
189                println!("Failed to create registry file");
190                false //It didn't get created, so it doesn't exist.
191            }
192        }
193    } else {
194        true //It existed and it will exist forever.
195    }
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    // Work on a mutable copy for removal
223    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            //on case of confirmation also unreachable!()
332            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}