cypat/util/
program.rs

1/*  
2*   SPDX-License-Identifier: GPL-3.0-only
3*   A cyberpatriots scoring engine library
4*   Copyright (C) 2023 Teresa Maria Rivera
5*/
6#![allow(unused_imports)]
7
8use std::{
9	fs::File,
10	process::{Command, Stdio},
11};
12use crate::engine::{AppData, InstallMethod};
13
14/// A bool but with three options, [`TripleBool::Known`], [`TripleBool::Unknown`]
15#[derive(Copy, Clone)]
16pub enum TripleBool {
17	Known(bool),
18	Unknown,
19}
20
21/// Check if a package is installed purely from the name
22/// 
23/// On Linux, `name` should be the package name, to query package managers for.
24/// On Windows, `name` should be the name of the exe file, to query the registry for.
25pub fn is_package_installed<T: ToString>(name: &T) -> bool {
26	#[cfg(target_os = "linux")]
27 	{
28		let pkg_name = name.to_string();
29		let dpkg_cmd = Command::new("dpkg").args(&["-l"]).stderr(Stdio::null()).stdout(Stdio::piped())
30			.output().expect("O no estamos en una distribución basada en Debian o algo está realmente mal.");
31
32		if String::from_utf8_lossy(&dpkg_cmd.stdout).contains(pkg_name.as_str()) {
33			return true;
34		}
35
36		match Command::new("flatpak").args(&["list"]).stderr(Stdio::null()).stdout(Stdio::piped()).output().ok() {
37			Some(output) => {
38				if String::from_utf8_lossy(&output.stdout).contains(pkg_name.as_str()) {
39					return true;
40				}
41			},
42			None => (),
43		}
44
45		match Command::new("snap").args(&["list"]).stderr(Stdio::null()).stdout(Stdio::piped()).output().ok() {
46			Some(output) => {
47				if String::from_utf8_lossy(&output.stdout).contains(pkg_name.as_str()) {
48					return true;
49				}
50			},
51			None => (),
52		}
53
54		false
55	}
56	#[cfg(target_os = "windows")]
57	{
58		let cmd = Command::new("reg")
59        .args(&["query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths", "-s"])
60		.stderr(Stdio::null()).stdout(Stdio::piped()).output();
61		match cmd.ok() {
62			Some(o) => {
63				let tmp = String::from_utf8_lossy(&o.stdout);
64	    		if tmp.contains(&name.to_string()) {
65					true
66				} else {
67					false
68				}
69			},
70			None => false,
71		}
72	}
73}
74
75impl AppData {
76	/// Create a new app data
77	pub fn new<T: ToString>(name: &T, install_method: InstallMethod) -> Self {
78		Self { name: name.to_string(), install_method: install_method }
79	}
80
81	/// Checks if a package is installed
82	/// 
83	/// For WinGet, APT ([`InstallMethod::Default`] on Linux, aka [`InstallMethod::PackageManager`]), [`InstallMethod::Flatpak`], and [`InstallMethod::Snap`] packages, it uses `self.name` to query said package managers. \
84	/// For [`InstallMethod::PackageManager`]/[`InstallMethod::Default`] on Windows, this just calls [`is_package_installed`]. \
85	/// For anything else, it default returns [`TripleBool::Unknown`]
86	pub fn is_installed(&self) -> TripleBool {
87
88		match self.install_method {
89			InstallMethod::Default | InstallMethod::PackageManager => {
90				#[cfg(target_os = "linux")]
91				{
92					let dpkg_cmd = Command::new("dpkg").args(&["-l"])
93					    .stderr(Stdio::null()).stdout(Stdio::piped())
94			            .output().expect("O no estamos en una distribución basada en Debian o algo está muy mal.");
95
96					TripleBool::Known(String::from_utf8_lossy(&dpkg_cmd.stdout).contains(&self.name))
97				}
98				#[cfg(target_os = "windows")]
99				{
100					TripleBool::Known(is_package_installed(&self.name))
101				}
102			},
103			#[cfg(target_os = "linux")]
104			InstallMethod::Flatpak => {
105				match Command::new("flatpak").args(&["list"]).stderr(Stdio::null()).stdout(Stdio::piped()).output().ok() {
106					Some(output) => {
107						TripleBool::Known(String::from_utf8_lossy(&output.stdout).contains(&self.name))
108					},
109					None => TripleBool::Unknown,
110				}
111			},
112			#[cfg(target_os = "linux")]
113			InstallMethod::Snap => {
114				match Command::new("snap").args(&["list"]).stdout(Stdio::piped()).output().ok() {
115					Some(output) => {
116						TripleBool::Known(String::from_utf8_lossy(&output.stdout).contains(&self.name))
117					},
118					None => TripleBool::Unknown,
119				}
120			},
121			#[cfg(target_os = "windows")]
122			InstallMethod::WinGet => {
123				match Command::new("winget").args(&["list", "--name"]).stderr(Stdio::null()).stdout(Stdio::piped()).output().ok() {
124					Some(output) => {
125						TripleBool::Known(String::from_utf8_lossy(&output.stdout).contains(&self.name))
126					},
127					None => TripleBool::Unknown,
128				}
129			}
130			_ => TripleBool::Unknown,
131		}
132	}
133}
134
135// TODO: Mucho más