mirror of
https://github.com/arabianq/rpmi.git
synced 2026-04-28 06:31:23 +00:00
initial commit
change project name to egui_rpm_installer
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+3268
File diff suppressed because it is too large
Load Diff
+34
@@ -0,0 +1,34 @@
|
|||||||
|
[package]
|
||||||
|
name = "egui_rpm_installer"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2024"
|
||||||
|
authors = ["arabianq"]
|
||||||
|
description = "Simple graphical utility that installs/upgrades/removes .rpm files built with Rust and EGUI."
|
||||||
|
keywords = ["linux", "rpm", "dnf", "package", "utility"]
|
||||||
|
homepage = "https://rpmi.arabianq.ru/"
|
||||||
|
repository = "https://github.com/arabianq/rpmi"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rpmi"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
egui = { version = "0.33.2", default-features = false, features = [
|
||||||
|
"default_fonts",
|
||||||
|
] }
|
||||||
|
eframe = { version = "0.33.2", default-features = false, features = [
|
||||||
|
"default_fonts",
|
||||||
|
"glow",
|
||||||
|
"wayland",
|
||||||
|
"x11",
|
||||||
|
] }
|
||||||
|
rpm = { version = "0.18.4", default-features = false, features = [] }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = "z"
|
||||||
|
panic = "abort"
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
use std::{
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
os::unix::process::CommandExt,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
sync::mpsc::{Receiver, channel},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
pub enum DNFAction {
|
||||||
|
Install,
|
||||||
|
Upgrade,
|
||||||
|
Remove,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
pub enum PackageState {
|
||||||
|
NewPackage,
|
||||||
|
OldVersion,
|
||||||
|
NewVersion(PackageEntry),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
pub struct PackageEntry {
|
||||||
|
pub name: String,
|
||||||
|
pub arch: String,
|
||||||
|
pub version: String,
|
||||||
|
pub release: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_package_state(pkg: &rpm::Package) -> PackageState {
|
||||||
|
let pkg_name = pkg.metadata.get_name().unwrap_or_default();
|
||||||
|
let pkg_arch = pkg.metadata.get_arch().unwrap_or_default();
|
||||||
|
let pkg_version = pkg.metadata.get_version().unwrap_or_default();
|
||||||
|
let pkg_release = pkg.metadata.get_release().unwrap_or_default();
|
||||||
|
|
||||||
|
let mut child = Command::new("/usr/bin/dnf")
|
||||||
|
.args(["list", "--installed"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.process_group(0)
|
||||||
|
.spawn()
|
||||||
|
.expect("Couldn't spawn child thread");
|
||||||
|
|
||||||
|
let child_stdout = child.stdout.take().expect("Couldn't take stdout");
|
||||||
|
|
||||||
|
for line in BufReader::new(child_stdout).lines() {
|
||||||
|
if let Ok(line) = line {
|
||||||
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
|
|
||||||
|
if parts.len() != 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_splitted: Vec<&str> = parts[0].split(".").collect();
|
||||||
|
let (name, arch) = (name_splitted[0], name_splitted[1]);
|
||||||
|
|
||||||
|
let version_prepared = parts[1]
|
||||||
|
.split_once(':')
|
||||||
|
.map(|(_epoch, version)| version)
|
||||||
|
.unwrap_or(parts[1]);
|
||||||
|
let version_splitted: Vec<&str> = version_prepared.split("-").collect();
|
||||||
|
let (version, release) = (version_splitted[0], version_splitted[1]);
|
||||||
|
|
||||||
|
if pkg_name.eq(name) && pkg_arch.eq(arch) {
|
||||||
|
child.kill().unwrap();
|
||||||
|
child.wait().unwrap();
|
||||||
|
|
||||||
|
let package_entry = PackageEntry {
|
||||||
|
name: name.to_string(),
|
||||||
|
arch: arch.to_string(),
|
||||||
|
version: version.to_string(),
|
||||||
|
release: release.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if pkg_version.eq(version) && pkg_release.eq(release) {
|
||||||
|
return PackageState::OldVersion;
|
||||||
|
}
|
||||||
|
return PackageState::NewVersion(package_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
child.kill().ok();
|
||||||
|
child.wait().ok();
|
||||||
|
|
||||||
|
return PackageState::NewPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dnf_start_action(
|
||||||
|
package_path: &str,
|
||||||
|
action_type: DNFAction,
|
||||||
|
) -> (JoinHandle<()>, Receiver<String>) {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
let package_path = package_path.to_string().clone();
|
||||||
|
|
||||||
|
let action_thread = thread::spawn(move || {
|
||||||
|
let mut child = Command::new("/usr/bin/pkexec")
|
||||||
|
.arg("--disable-internal-agent")
|
||||||
|
.arg("/usr/bin/dnf")
|
||||||
|
.arg(match action_type {
|
||||||
|
DNFAction::Install => "install",
|
||||||
|
DNFAction::Remove => "remove",
|
||||||
|
DNFAction::Upgrade => "upgrade",
|
||||||
|
})
|
||||||
|
.args(["-y", &package_path])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.process_group(0)
|
||||||
|
.spawn()
|
||||||
|
.expect("Couldn't spawn child thread");
|
||||||
|
|
||||||
|
let child_stdout = child.stdout.take().expect("Couldn't take stdout");
|
||||||
|
let child_stderr = child.stderr.take().expect("Couldn't take stdout");
|
||||||
|
let (stdout_tx, stdout_rx) = channel();
|
||||||
|
let (stderr_tx, stderr_rx) = channel();
|
||||||
|
|
||||||
|
let stdout_thread = thread::spawn(move || {
|
||||||
|
let stdout_lines = BufReader::new(child_stdout).lines();
|
||||||
|
for line in stdout_lines {
|
||||||
|
if let Ok(line) = line {
|
||||||
|
stdout_tx.send(line).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let stderr_thread = thread::spawn(move || {
|
||||||
|
let stderr_lines = BufReader::new(child_stderr).lines();
|
||||||
|
for line in stderr_lines {
|
||||||
|
if let Ok(line) = line {
|
||||||
|
stderr_tx.send(line).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
while let Ok(None) = child.try_wait() {
|
||||||
|
if let Ok(msg) = stdout_rx.try_recv() {
|
||||||
|
tx.send(msg).ok();
|
||||||
|
}
|
||||||
|
if let Ok(msg) = stderr_rx.try_recv() {
|
||||||
|
tx.send(msg).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout_thread.join().ok();
|
||||||
|
stderr_thread.join().ok();
|
||||||
|
child.kill().ok();
|
||||||
|
child.wait().ok();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (action_thread, rx);
|
||||||
|
}
|
||||||
+372
@@ -0,0 +1,372 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
use crate::dnf::*;
|
||||||
|
use crate::utils::*;
|
||||||
|
|
||||||
|
use eframe::{App, CreationContext, Frame, HardwareAcceleration, NativeOptions, run_native};
|
||||||
|
use egui::{
|
||||||
|
Align, Button, CentralPanel, Color32, Context, Direction, FontFamily, Label, Layout, RichText,
|
||||||
|
ScrollArea, TextWrapMode, Ui, Vec2, ViewportBuilder,
|
||||||
|
text::{LayoutJob, TextFormat, TextWrapping},
|
||||||
|
};
|
||||||
|
|
||||||
|
use rpm::Package;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, Mutex, mpsc::Receiver},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
enum AppStep {
|
||||||
|
Intro,
|
||||||
|
Process,
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Application {
|
||||||
|
pkg_path: PathBuf,
|
||||||
|
pkg: Package,
|
||||||
|
step: AppStep,
|
||||||
|
process_log: String,
|
||||||
|
pkg_state: Option<PackageState>,
|
||||||
|
pkg_state_shared: Arc<Mutex<Option<PackageState>>>,
|
||||||
|
process_rx: Option<Receiver<String>>,
|
||||||
|
pkg_state_loading_thread: Option<JoinHandle<()>>,
|
||||||
|
process_thread: Option<JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Application {
|
||||||
|
fn new(_cc: &CreationContext, pkg_path: PathBuf) -> Self {
|
||||||
|
let pkg = Package::open(&pkg_path).expect("Failed to read rpm package =(");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pkg_path,
|
||||||
|
pkg,
|
||||||
|
step: AppStep::Intro,
|
||||||
|
process_log: String::new(),
|
||||||
|
pkg_state: None,
|
||||||
|
pkg_state_shared: Arc::new(Mutex::new(None)),
|
||||||
|
process_rx: None,
|
||||||
|
pkg_state_loading_thread: None,
|
||||||
|
process_thread: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_package_state(&mut self) -> JoinHandle<()> {
|
||||||
|
let pkg_state_shared = self.pkg_state_shared.clone();
|
||||||
|
let pkg = self.pkg.clone();
|
||||||
|
let thread = thread::spawn(move || {
|
||||||
|
let pkg_state = get_package_state(&pkg);
|
||||||
|
let mut guard = pkg_state_shared.lock().unwrap();
|
||||||
|
*guard = Some(pkg_state);
|
||||||
|
});
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_process(&mut self) {
|
||||||
|
self.step = AppStep::Process;
|
||||||
|
let (process_thread, process_rx) = match self.pkg_state.as_ref().unwrap() {
|
||||||
|
PackageState::NewPackage => {
|
||||||
|
dnf_start_action(self.pkg_path.to_str().unwrap(), DNFAction::Install)
|
||||||
|
}
|
||||||
|
PackageState::OldVersion => dnf_start_action(
|
||||||
|
self.pkg.metadata.get_name().unwrap_or_default(),
|
||||||
|
DNFAction::Remove,
|
||||||
|
),
|
||||||
|
PackageState::NewVersion(_) => {
|
||||||
|
dnf_start_action(self.pkg_path.to_str().unwrap(), DNFAction::Upgrade)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.process_thread = Some(process_thread);
|
||||||
|
self.process_rx = Some(process_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_intro(&mut self, ui: &mut Ui) {
|
||||||
|
ui.with_layout(Layout::top_down(Align::Min), |ui| {
|
||||||
|
fn add_info_entry(
|
||||||
|
ui: &mut Ui,
|
||||||
|
key: &str,
|
||||||
|
value: &str,
|
||||||
|
max_rows: usize,
|
||||||
|
hyperlink: bool,
|
||||||
|
) {
|
||||||
|
ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
|
||||||
|
let key_label = Label::new(
|
||||||
|
RichText::new(format!("• {}\t→\t", key))
|
||||||
|
.color(Color32::LIGHT_GRAY)
|
||||||
|
.family(FontFamily::Monospace),
|
||||||
|
);
|
||||||
|
ui.add(key_label);
|
||||||
|
|
||||||
|
if hyperlink {
|
||||||
|
ui.hyperlink(value);
|
||||||
|
} else {
|
||||||
|
let mut value_layout_job = LayoutJob {
|
||||||
|
wrap: TextWrapping {
|
||||||
|
max_rows: max_rows,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
value_layout_job.append(value, 0.0, TextFormat::default());
|
||||||
|
ui.add(Label::new(value_layout_job).wrap_mode(TextWrapMode::Wrap));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ScrollArea::vertical()
|
||||||
|
.max_height(ui.available_height() - 30.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.take_available_width();
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"Name ",
|
||||||
|
self.pkg.metadata.get_name().unwrap_or("-"),
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
match self.pkg_state.as_ref().unwrap() {
|
||||||
|
PackageState::NewVersion(old_pkg) => {
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"Old Version ",
|
||||||
|
&format!("{}-{}", old_pkg.version, old_pkg.release),
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"New Version ",
|
||||||
|
&format!(
|
||||||
|
"{}-{}",
|
||||||
|
self.pkg.metadata.get_version().unwrap_or("-"),
|
||||||
|
self.pkg.metadata.get_release().unwrap_or("-")
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"Version ",
|
||||||
|
&format!(
|
||||||
|
"{}-{}",
|
||||||
|
self.pkg.metadata.get_version().unwrap_or("-"),
|
||||||
|
self.pkg.metadata.get_release().unwrap_or("-")
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"Architecture",
|
||||||
|
&self.pkg.metadata.get_arch().unwrap_or("-"),
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"Size ",
|
||||||
|
&size_to_string(self.pkg.metadata.get_installed_size().unwrap_or(0) as f64),
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"Summary ",
|
||||||
|
&self.pkg.metadata.get_summary().unwrap_or("-"),
|
||||||
|
3,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"URL ",
|
||||||
|
&self.pkg.metadata.get_url().unwrap_or("-"),
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"License ",
|
||||||
|
&self.pkg.metadata.get_license().unwrap_or("-"),
|
||||||
|
2,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
add_info_entry(
|
||||||
|
ui,
|
||||||
|
"Description ",
|
||||||
|
&self.pkg.metadata.get_description().unwrap_or("-"),
|
||||||
|
5,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.with_layout(Layout::bottom_up(Align::Center), |ui| {
|
||||||
|
ui.with_layout(Layout::right_to_left(Align::BOTTOM), |ui| {
|
||||||
|
if ui
|
||||||
|
.button(
|
||||||
|
RichText::new(match self.pkg_state.as_ref().unwrap() {
|
||||||
|
PackageState::NewPackage => "Install",
|
||||||
|
PackageState::OldVersion => "Remove",
|
||||||
|
PackageState::NewVersion(_) => "Upgrade",
|
||||||
|
})
|
||||||
|
.size(18.0)
|
||||||
|
.family(FontFamily::Monospace),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.start_process();
|
||||||
|
};
|
||||||
|
if ui
|
||||||
|
.button(
|
||||||
|
RichText::new("Cancel")
|
||||||
|
.size(18.0)
|
||||||
|
.family(FontFamily::Monospace),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
std::process::exit(0);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_process(&mut self, ui: &mut Ui) {
|
||||||
|
ScrollArea::vertical()
|
||||||
|
.stick_to_bottom(true)
|
||||||
|
.max_height(ui.available_height() - 30.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.take_available_width();
|
||||||
|
ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
|
||||||
|
ui.add(Label::new(&self.process_log).wrap_mode(TextWrapMode::Wrap));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if self.step == AppStep::Finished {
|
||||||
|
ui.with_layout(Layout::bottom_up(Align::Center), |ui| {
|
||||||
|
ui.with_layout(Layout::right_to_left(Align::BOTTOM), |ui| {
|
||||||
|
let close_button = Button::new(
|
||||||
|
RichText::new("Close")
|
||||||
|
.size(18.0)
|
||||||
|
.family(FontFamily::Monospace),
|
||||||
|
);
|
||||||
|
let back_button = Button::new(
|
||||||
|
RichText::new("Back")
|
||||||
|
.size(18.0)
|
||||||
|
.family(FontFamily::Monospace),
|
||||||
|
);
|
||||||
|
|
||||||
|
if ui.add(close_button).clicked() {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
if ui.add(back_button).clicked() {
|
||||||
|
self.step = AppStep::Intro;
|
||||||
|
self.process_log = String::new();
|
||||||
|
self.pkg_state = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App for Application {
|
||||||
|
fn update(&mut self, ctx: &Context, _: &mut Frame) {
|
||||||
|
if let Some(process_thread) = &self.process_thread
|
||||||
|
&& let Some(process_rx) = &self.process_rx
|
||||||
|
{
|
||||||
|
if process_thread.is_finished() {
|
||||||
|
if let Ok(msg) = process_rx.try_recv() {
|
||||||
|
self.process_log.push_str(&msg);
|
||||||
|
self.process_log.push('\n');
|
||||||
|
}
|
||||||
|
self.process_thread = None;
|
||||||
|
self.process_rx = None;
|
||||||
|
self.step = AppStep::Finished;
|
||||||
|
} else if let Ok(msg) = process_rx.try_recv() {
|
||||||
|
self.process_log.push_str(&msg);
|
||||||
|
self.process_log.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CentralPanel::default().show(ctx, |ui| {
|
||||||
|
if self.pkg_state.is_none() {
|
||||||
|
if self.pkg_state_loading_thread.is_none() {
|
||||||
|
self.pkg_state_loading_thread = Some(self.get_package_state());
|
||||||
|
} else if let Some(t) = &self.pkg_state_loading_thread
|
||||||
|
&& t.is_finished()
|
||||||
|
{
|
||||||
|
let mut guard = self.pkg_state_shared.lock().unwrap();
|
||||||
|
self.pkg_state = guard.clone();
|
||||||
|
*guard = None;
|
||||||
|
self.pkg_state_loading_thread = None;
|
||||||
|
}
|
||||||
|
ui.with_layout(Layout::centered_and_justified(Direction::TopDown), |ui| {
|
||||||
|
ui.spinner()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
|
||||||
|
ui.add(Label::new(
|
||||||
|
RichText::new(format!(
|
||||||
|
"{} {}-{}-{}.{}.rpm",
|
||||||
|
match self.pkg_state.as_ref().unwrap() {
|
||||||
|
PackageState::NewPackage => "Install",
|
||||||
|
PackageState::OldVersion => "Remove",
|
||||||
|
PackageState::NewVersion(_) => "Upgrade",
|
||||||
|
},
|
||||||
|
self.pkg.metadata.get_name().unwrap_or("unknown"),
|
||||||
|
self.pkg.metadata.get_version().unwrap_or("0.0.0"),
|
||||||
|
self.pkg.metadata.get_release().unwrap_or("1"),
|
||||||
|
self.pkg.metadata.get_arch().unwrap_or("unknown"),
|
||||||
|
))
|
||||||
|
.size(14.0)
|
||||||
|
.color(Color32::LIGHT_GRAY)
|
||||||
|
.family(FontFamily::Monospace),
|
||||||
|
));
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
match self.step {
|
||||||
|
AppStep::Intro => self.draw_intro(ui),
|
||||||
|
_ => self.draw_process(ui),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(arg: PathBuf) -> Result<(), Box<dyn Error>> {
|
||||||
|
let opts = NativeOptions {
|
||||||
|
vsync: true,
|
||||||
|
centered: true,
|
||||||
|
hardware_acceleration: HardwareAcceleration::Preferred,
|
||||||
|
|
||||||
|
viewport: ViewportBuilder::default()
|
||||||
|
.with_app_id("ru.arabianq.rpmi")
|
||||||
|
.with_resizable(false)
|
||||||
|
.with_inner_size(Vec2::new(600.0, 300.0)),
|
||||||
|
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
match run_native(
|
||||||
|
"RPM Installer",
|
||||||
|
opts,
|
||||||
|
Box::new(|cc| Ok(Box::new(Application::new(cc, arg)))),
|
||||||
|
) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => Err(err.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
mod dnf;
|
||||||
|
mod gui;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use std::{env, error::Error, fs::canonicalize, path::Path, process::Command};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
if args.len() == 1 {
|
||||||
|
Err("Provide at least one package".into())
|
||||||
|
} else if args.len() == 2 {
|
||||||
|
let arg = &args[1];
|
||||||
|
if let Ok(path) = canonicalize(Path::new(arg)) {
|
||||||
|
gui::run(path)
|
||||||
|
} else {
|
||||||
|
Err("Failed to open {arg}".into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let binary_path = canonicalize(Path::new(&args[0]))?;
|
||||||
|
|
||||||
|
for arg in args.iter().skip(1) {
|
||||||
|
if let Ok(pkg_path) = canonicalize(Path::new(arg)) {
|
||||||
|
Command::new(&binary_path).arg(pkg_path).spawn().ok();
|
||||||
|
} else {
|
||||||
|
eprintln!("Failed to open {arg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
pub fn size_to_string(size: f64) -> String {
|
||||||
|
if size <= 0.0 {
|
||||||
|
return "-".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNITS: [&str; 4] = ["B", "KB", "MB", "GB"];
|
||||||
|
|
||||||
|
let i = if size < 1.0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
size.log(1024.0).floor() as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let i = i.min(UNITS.len().saturating_sub(1));
|
||||||
|
|
||||||
|
let p = 1024_f64.powf(i as f64);
|
||||||
|
let s = size / p;
|
||||||
|
|
||||||
|
let mut buffer = String::with_capacity(10);
|
||||||
|
write!(&mut buffer, "{:.2} {}", s, UNITS[i]).unwrap();
|
||||||
|
|
||||||
|
buffer
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user