Updated CHANGELOG.md, documented a bunch of stuff and fixed a lot of warnings.

This commit is contained in:
Hellx2 2024-08-21 18:29:12 +10:00
parent be2f789652
commit af8365675d
9 changed files with 204 additions and 89 deletions

View file

@ -2,17 +2,17 @@
Commit 1 Commit 1
- Removed `on_shutdown` function from `src/main.rs`, as it was uselessly dropping memory that was automatically dropped right after the function ended. - Removed `on_shutdown` function from `src/main.rs`, as it was uselessly dropping memory that was automatically dropped right after the function ended.
- Finished the documentation for `src/main.rs`, `src/packages/installed.rs`, `src/packages/browse.rs`, and `src/packages/mod.rs` - Finished the documentation for `src/main.rs`, `src/packages/installed.rs`, `src/packages/browse.rs`, and `src/packages/mod.rs`.
- Renamed `show_installed_package` to `show_package`, and merged it into `src/packages/mod.rs` instead of having copies in two files. - Renamed `show_installed_package` to `show_package`, and merged it into `src/packages/mod.rs` instead of having copies in two files.
- Added this changelog. - Added this changelog.
- Updated `inst.sh` so that it no longer copies `style.css` to the `/usr/share/oreon-system-manager` directory. - Updated `inst.sh` so that it no longer copies `style.css` to the `/usr/share/oreon-system-manager` directory.
Commit 2 Commit 2
- Moved `drivers` to it's own module folder - Moved `drivers` to it's own module folder.
- Added `src/drivers/mod.rs` and `src/drivers/objects.rs` - Added `src/drivers/mod.rs` and `src/drivers/objects.rs`.
Commit 3 Commit 3
- Fixed crash on package description fetch - Fixed crash on package description fetch.
Commit 4 Commit 4
- Added very basic support for Docker images. - Added very basic support for Docker images.
@ -20,3 +20,9 @@ Commit 4
Commit 5 Commit 5
- Added almost full support for Docker images. - Added almost full support for Docker images.
- Created an interface between Rust and the `docker image inspect` command using Serde. - Created an interface between Rust and the `docker image inspect` command using Serde.
Commit 6
- Documented everything in the containers module.
- Switched to using `cargo clippy` and fixed all of the warnings that it showed.
- Renamed `src/containers/core.rs` to `src/containers/api.rs`.
- Moved the `Image` struct to the `api` submodule instead of having it in the main `containers` module.

View file

@ -2,6 +2,38 @@ use std::collections::BTreeMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/**
Struct for a docker image, with the basic
details from the `docker images` command.
*/
pub struct Image {
pub repository: String,
pub tag: String,
pub id: String,
pub created: String,
pub size: String,
}
/*
Implement `FromStr` for `Image` so that
the output of the `docker images` command
can be parsed into computer-interpretable
data.
*/
impl std::str::FromStr for Image {
type Err = String;
fn from_str(value: &str) -> Result<Self, String> {
let parts: Vec<String> = value.split_whitespace().map(|x| x.to_owned()).collect();
Ok(Self {
repository: parts[0].clone(),
tag: parts[1].clone(),
id: parts[2].clone(),
created: parts[3..(parts.len() - 1)].join(" "),
size: parts[parts.len() - 1].clone(),
})
}
}
/** /**
A struct for handling the output of the `docker image inspect` A struct for handling the output of the `docker image inspect`
command. command.
@ -25,6 +57,10 @@ pub struct ImageInspectData {
pub metadata: IIMetadata, pub metadata: IIMetadata,
} }
/*
Implementation to allow for testing data, should be removed
in release versions to reduce the file size and line count.
*/
impl Default for ImageInspectData { impl Default for ImageInspectData {
fn default() -> Self { fn default() -> Self {
ImageInspectData { ImageInspectData {
@ -47,6 +83,7 @@ impl Default for ImageInspectData {
} }
} }
/// The struct for the `config` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIConfig { pub struct IIConfig {
pub hostname: String, pub hostname: String,
@ -70,6 +107,10 @@ pub struct IIConfig {
pub labels: Option<IIConfigLabels>, pub labels: Option<IIConfigLabels>,
} }
/*
Implementation to allow for testing data, should be removed
in release versions to reduce the file size and line count.
*/
impl Default for IIConfig { impl Default for IIConfig {
fn default() -> Self { fn default() -> Self {
IIConfig { IIConfig {
@ -94,14 +135,20 @@ impl Default for IIConfig {
} }
} }
/// Type for the `labels` property of `IIConfig`
pub type IIConfigLabels = BTreeMap<String, String>; pub type IIConfigLabels = BTreeMap<String, String>;
/// Struct for the `graph_driver` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIGraphDriver { pub struct IIGraphDriver {
pub data: GraphDriverData, pub data: GraphDriverData,
pub name: String, pub name: String,
} }
/*
Implementation to allow for testing data, should be removed
in release versions to reduce the file size and line count.
*/
impl Default for IIGraphDriver { impl Default for IIGraphDriver {
fn default() -> Self { fn default() -> Self {
IIGraphDriver { IIGraphDriver {
@ -111,12 +158,18 @@ impl Default for IIGraphDriver {
} }
} }
/// The `data` property of `IIGraphDriver`
#[derive(Clone, Serialize, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub struct GraphDriverData { pub struct GraphDriverData {
pub merged_dir: String, pub merged_dir: String,
pub upper_dir: String, pub upper_dir: String,
pub work_dir: String, pub work_dir: String,
} }
/*
Implementation to allow for testing data, should be removed
in release versions to reduce the file size and line count.
*/
impl Default for GraphDriverData { impl Default for GraphDriverData {
fn default() -> GraphDriverData { fn default() -> GraphDriverData {
GraphDriverData { GraphDriverData {
@ -127,12 +180,17 @@ impl Default for GraphDriverData {
} }
} }
/// Struct for defining the `rootfs` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIRootFS { pub struct IIRootFS {
pub r#type: String, pub r#type: String,
pub layers: Vec<String>, pub layers: Vec<String>,
} }
/*
Implementation to allow for testing data, should be removed
in release versions to reduce the file size and line count.
*/
impl Default for IIRootFS { impl Default for IIRootFS {
fn default() -> Self { fn default() -> Self {
IIRootFS { IIRootFS {
@ -142,11 +200,16 @@ impl Default for IIRootFS {
} }
} }
/// Struct for the `metadata` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIMetadata { pub struct IIMetadata {
pub last_tag_time: String, pub last_tag_time: String,
} }
/*
Implementation to allow for testing data, should be removed
in release versions to reduce the file size and line count.
*/
impl Default for IIMetadata { impl Default for IIMetadata {
fn default() -> Self { fn default() -> Self {
IIMetadata { IIMetadata {

View file

@ -4,13 +4,17 @@ use gtk::{
}; };
use crate::{ use crate::{
containers::{self, core::ImageInspectData}, containers::api::{change_keys, Image, ImageInspectData},
pkexec, pkexec,
}; };
use super::Image; /**
Subsection of the containers page to display
Docker imags provided from the `docker images`
command.
*/
pub fn images() -> gtk::Box { pub fn images() -> gtk::Box {
// Box for displaying the list of images.
let images_box = gtk::Box::builder() let images_box = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical) .orientation(gtk::Orientation::Vertical)
.build(); .build();
@ -26,6 +30,7 @@ pub fn images() -> gtk::Box {
.map(|x| x.parse::<Image>().unwrap()) .map(|x| x.parse::<Image>().unwrap())
.collect::<Vec<Image>>(); .collect::<Vec<Image>>();
// Test image, remove in the main build.
images.push(Image { images.push(Image {
repository: "Test".to_owned(), repository: "Test".to_owned(),
tag: "something".to_owned(), tag: "something".to_owned(),
@ -34,12 +39,22 @@ pub fn images() -> gtk::Box {
size: "100MB".to_owned(), size: "100MB".to_owned(),
}); });
/*
Loop through each of the images listed
from the `docker images` command and
create buttons and details for each of
them.
*/
for image in images { for image in images {
let image_box = gtk::Box::builder() let image_box = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical) .orientation(gtk::Orientation::Vertical)
.css_classes(["image-box"]) .css_classes(["image-box"])
.build(); .build();
/*
Generate boxes for each of these that show
[property name]: [value]
*/
image_box.append(&gen_box("Repository", &image.repository)); image_box.append(&gen_box("Repository", &image.repository));
image_box.append(&gen_box("Tag", &image.tag)); image_box.append(&gen_box("Tag", &image.tag));
image_box.append(&gen_box("Image ID", &image.id)); image_box.append(&gen_box("Image ID", &image.id));
@ -52,7 +67,12 @@ pub fn images() -> gtk::Box {
.build(); .build();
image_button.connect_clicked(|x| { image_button.connect_clicked(|x| {
/*
Show the details for the image through a
function.
*/
show_image( show_image(
// Get the image's unique image ID.
x.child() x.child()
.expect("Failed to get image_button child!") .expect("Failed to get image_button child!")
.downcast::<gtk::Box>() .downcast::<gtk::Box>()
@ -79,17 +99,37 @@ pub fn images() -> gtk::Box {
images_box images_box
} }
fn show_image(image_id: &str) { /**
Function for showing the details of a Docker
image as a new window.
*/
pub fn show_image(image_id: &str) {
/*
Parse the details of the docker image inspect
command output through the `core.rs` interface.
*/
let image_details: Vec<ImageInspectData> = let image_details: Vec<ImageInspectData> =
serde_json::from_str::<Vec<ImageInspectData>>(&containers::core::change_keys( serde_json::from_str::<Vec<ImageInspectData>>(&change_keys(
pkexec("docker image inspect ".to_owned() + image_id, false) pkexec("docker image inspect ".to_owned() + image_id, false)
.expect("This error should never occur"), .expect("This error should never occur"),
)) ))
.expect("Failed to get data"); .expect("Failed to get data");
/*
Shadow it with the first value, since there
will only be one item in the list when using
the image's ID.
TODO: Check that this is true.
*/
let image_details = image_details[0].clone(); let image_details = image_details[0].clone();
// TODO: Display the data. /*
Create a window to store all of the image's
data from the inspect command.
TODO: Display the data.
*/
let image_window = Window::builder() let image_window = Window::builder()
.title(format!("Image ID {image_id}").as_str()) .title(format!("Image ID {image_id}").as_str())
.default_width(600) .default_width(600)
@ -101,6 +141,12 @@ fn show_image(image_id: &str) {
.css_classes(["image-showbox"]) .css_classes(["image-showbox"])
.build(); .build();
/*
Generate boxes for each of the components
of the image in the following format:
[component name]: [value]
*/
main_box.append(&gen_box("Author", &image_details.author)); main_box.append(&gen_box("Author", &image_details.author));
main_box.append(&gen_box("Comment", &image_details.comment)); main_box.append(&gen_box("Comment", &image_details.comment));
main_box.append(&gen_box("Id", &image_details.id)); main_box.append(&gen_box("Id", &image_details.id));
@ -117,6 +163,14 @@ fn show_image(image_id: &str) {
image_window.present(); image_window.present();
} }
/**
Function to generate a box for a property
of some object, in the following format:
[property name]: [value]
Where [property name] is in bold.
*/
pub fn gen_box(name: &str, content: &str) -> gtk::Box { pub fn gen_box(name: &str, content: &str) -> gtk::Box {
let return_box = gtk::Box::builder() let return_box = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal) .orientation(gtk::Orientation::Horizontal)

View file

@ -1,16 +1,13 @@
use std::str::FromStr;
use gtk::{prelude::BoxExt, Stack, StackSidebar}; use gtk::{prelude::BoxExt, Stack, StackSidebar};
use images::images; use images::images;
pub mod core; pub mod api;
pub mod images; pub mod images;
/** /**
Page for managing Docker containers and images. Page for managing Docker containers and images.
## TODO ## TODO
- Move to it's own file/folder
- Add the following pages: "Containers", "Images", "Browse" - Add the following pages: "Containers", "Images", "Browse"
*/ */
pub fn containers() -> gtk::Box { pub fn containers() -> gtk::Box {
@ -20,6 +17,7 @@ pub fn containers() -> gtk::Box {
.css_classes(["containers-box"]) .css_classes(["containers-box"])
.build(); .build();
// Stack to contain each of the pages.
let containers_stack = Stack::builder() let containers_stack = Stack::builder()
.halign(gtk::Align::Fill) .halign(gtk::Align::Fill)
.valign(gtk::Align::Fill) .valign(gtk::Align::Fill)
@ -27,12 +25,14 @@ pub fn containers() -> gtk::Box {
.vexpand(true) .vexpand(true)
.build(); .build();
// Sidebar to allow the user to switch between pages.
let containers_sidebar = StackSidebar::builder() let containers_sidebar = StackSidebar::builder()
.stack(&containers_stack) .stack(&containers_stack)
.halign(gtk::Align::Start) .halign(gtk::Align::Start)
.valign(gtk::Align::Fill) .valign(gtk::Align::Fill)
.build(); .build();
// Add each of the pages to the stack.
containers_stack.add_titled(&images(), Some("images"), "Images"); containers_stack.add_titled(&images(), Some("images"), "Images");
// Start the docker daemon if it isn't running. // Start the docker daemon if it isn't running.
@ -40,31 +40,13 @@ pub fn containers() -> gtk::Box {
crate::pkexec("dockerd".to_owned(), true); crate::pkexec("dockerd".to_owned(), true);
} }
// Append a new label as a title and return the box. /*
Append the page switcher sidebar and the stack, in that order.
This is necessary, since otherwise the sidebar will be on the
wrong side.
*/
containers_box.append(&containers_sidebar); containers_box.append(&containers_sidebar);
containers_box.append(&containers_stack); containers_box.append(&containers_stack);
containers_box containers_box
} }
pub struct Image {
pub repository: String,
pub tag: String,
pub id: String,
pub created: String,
pub size: String,
}
impl FromStr for Image {
type Err = String;
fn from_str(value: &str) -> Result<Self, String> {
let parts: Vec<String> = value.split_whitespace().map(|x| x.to_owned()).collect();
Ok(Self {
repository: parts[0].clone(),
tag: parts[1].clone(),
id: parts[2].clone(),
created: parts[3..(parts.len() - 2)].join(" "),
size: parts[(parts.len() - 2)..parts.len()].join(" "),
})
}
}

View file

@ -126,15 +126,13 @@ pub fn installed_list() -> ScrolledWindow {
list.append(&btn); list.append(&btn);
} }
let scrollable = ScrolledWindow::builder() ScrolledWindow::builder()
.child(&list) .child(&list)
.width_request(300) .width_request(300)
.height_request(600) .height_request(600)
.halign(Align::Start) .halign(Align::Start)
.css_classes(["installed_list"]) .css_classes(["installed_list"])
.build(); .build()
scrollable
} }
pub fn get_driver_details() -> Vec<String> { pub fn get_driver_details() -> Vec<String> {
@ -175,7 +173,7 @@ pub fn get_driver_details() -> Vec<String> {
.lines() .lines()
.filter(|x| x.trim().contains("driver")) .filter(|x| x.trim().contains("driver"))
.map(|x| { .map(|x| {
x.split(":") x.split(':')
.nth(1) .nth(1)
.expect("Failed to find colon in driver definition.") .expect("Failed to find colon in driver definition.")
.trim() .trim()

View file

@ -1,3 +1,5 @@
#![allow(clippy::assigning_clones)]
use gtk::{ use gtk::{
prelude::{BoxExt, ButtonExt, EditableExt}, prelude::{BoxExt, ButtonExt, EditableExt},
Button, PositionType, Button, PositionType,
@ -44,7 +46,7 @@ pub fn browse() -> gtk::Box {
List of names to skip when searching, this is to remove the List of names to skip when searching, this is to remove the
labels "Available Packages", "Last refreshed at ..." and "Installed Packages" labels "Available Packages", "Last refreshed at ..." and "Installed Packages"
*/ */
let skip_list = vec!["available", "installed", "last"]; let skip_list = ["available", "installed", "last"];
/* /*
Initialize the packages list, needs to be a static Initialize the packages list, needs to be a static
@ -75,14 +77,14 @@ pub fn browse() -> gtk::Box {
.split_whitespace() .split_whitespace()
.next() .next()
.unwrap_or("") .unwrap_or("")
.split(".") .split('.')
.next() .next()
.unwrap(); .unwrap();
b.to_string() b.to_string()
}) })
.filter(|x| { .filter(|x| {
!x.trim().is_empty() !x.trim().is_empty()
&& !x.contains("=") && !x.contains('=')
&& !skip_list.contains(&x.to_lowercase().trim()) && !skip_list.contains(&x.to_lowercase().trim())
}) })
.dedup() .dedup()
@ -103,7 +105,7 @@ pub fn browse() -> gtk::Box {
package and a button for it in the memory at the same time, package and a button for it in the memory at the same time,
which has caused as much as 1.4GB of RAM usage during tests. which has caused as much as 1.4GB of RAM usage during tests.
*/ */
for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].to_vec() { for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].iter() {
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder() let pkg_btn = Button::builder()
.label(pkg) .label(pkg)
@ -139,9 +141,11 @@ pub fn browse() -> gtk::Box {
.unwrap() .unwrap()
.connect_edge_reached(|_, edge| { .connect_edge_reached(|_, edge| {
if edge == PositionType::Bottom { if edge == PositionType::Bottom {
for i in PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(PKG_LIST.len())) { for i in
PKG_LIST[PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(PKG_LIST.len()))].iter()
{
let pkg_btn = Button::builder() let pkg_btn = Button::builder()
.label(PKG_LIST[i].as_str()) .label(i.as_str())
.css_classes(["package-name"]) .css_classes(["package-name"])
.build(); .build();
@ -202,7 +206,7 @@ unsafe fn search(f: &SearchEntry) {
x.to_lowercase() x.to_lowercase()
.contains(f.text().to_string().to_lowercase().as_str()) .contains(f.text().to_string().to_lowercase().as_str())
}) })
.map(|x| x.clone()) .cloned()
.collect(); .collect();
/* /*
@ -233,7 +237,7 @@ unsafe fn search(f: &SearchEntry) {
either 50 or the length of the list, whichever either 50 or the length of the list, whichever
is less, and make buttons in the list for each is less, and make buttons in the list for each
*/ */
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() { for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].iter() {
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder() let pkg_btn = Button::builder()
.label(pkg) .label(pkg)

View file

@ -51,7 +51,7 @@ pub fn installed() -> gtk::Box {
List of names to skip when searching, this is to remove the List of names to skip when searching, this is to remove the
labels "Available Packages", "Last refreshed at ..." and "Installed Packages" labels "Available Packages", "Last refreshed at ..." and "Installed Packages"
*/ */
let skip_list = vec!["available", "installed", "last"]; let skip_list = ["available", "installed", "last"];
/* /*
Initialize the packages list, needs to be a static Initialize the packages list, needs to be a static
@ -88,14 +88,14 @@ pub fn installed() -> gtk::Box {
.split_whitespace() .split_whitespace()
.next() .next()
.unwrap_or("") .unwrap_or("")
.split(".") .split('.')
.next() .next()
.unwrap(); .unwrap();
b.to_string() b.to_string()
}) })
.filter(|x| { .filter(|x| {
!x.trim().is_empty() !x.trim().is_empty()
&& !x.contains("=") && !x.contains('=')
&& !skip_list.contains(&x.to_lowercase().trim()) && !skip_list.contains(&x.to_lowercase().trim())
}) })
.dedup() .dedup()
@ -107,7 +107,7 @@ pub fn installed() -> gtk::Box {
package and a button for it in the memory at the same time, package and a button for it in the memory at the same time,
which has caused as much as 1.4GB of RAM usage during tests. which has caused as much as 1.4GB of RAM usage during tests.
*/ */
for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].to_vec() { for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].iter() {
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder() let pkg_btn = Button::builder()
.label(pkg) .label(pkg)
@ -143,19 +143,23 @@ pub fn installed() -> gtk::Box {
.unwrap() .unwrap()
.connect_edge_reached(|_, edge| { .connect_edge_reached(|_, edge| {
if edge == PositionType::Bottom { if edge == PositionType::Bottom {
for i in PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(FULL_PKG_LIST.len())) { (PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(FULL_PKG_LIST.len()))).for_each(
let pkg_btn = Button::builder() |i| {
.label(FULL_PKG_LIST[i].as_str()) let pkg_btn = Button::builder()
.css_classes(["package-name"]) .label(FULL_PKG_LIST[i].as_str())
.build(); .css_classes(["package-name"])
.build();
pkg_btn.connect_clicked(|x| { pkg_btn.connect_clicked(|x| {
show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned())); show_package(
}); x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn); );
});
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn);
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
} },
);
} }
}) })
}; };
@ -204,7 +208,7 @@ unsafe fn search(f: &SearchEntry) {
x.to_lowercase() x.to_lowercase()
.contains(f.text().to_string().to_lowercase().as_str()) .contains(f.text().to_string().to_lowercase().as_str())
}) })
.map(|x| x.clone()) .cloned()
.collect(); .collect();
/* /*
@ -235,7 +239,7 @@ unsafe fn search(f: &SearchEntry) {
either 50 or the length of the list, whichever either 50 or the length of the list, whichever
is less, and make buttons in the list for each is less, and make buttons in the list for each
*/ */
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() { for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].iter() {
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder() let pkg_btn = Button::builder()
.label(pkg) .label(pkg)

View file

@ -112,11 +112,11 @@ fn show_package(pkg: String) {
*/ */
for line in info.lines() { for line in info.lines() {
if let Some(e) = line.trim().strip_prefix("Version") { if let Some(e) = line.trim().strip_prefix("Version") {
version = e.split_once(":").unwrap().1.trim(); version = e.split_once(':').unwrap().1.trim();
} else if let Some(e) = line.trim().strip_prefix("Size") { } else if let Some(e) = line.trim().strip_prefix("Size") {
size = e.split_once(":").unwrap().1.trim(); size = e.split_once(':').unwrap().1.trim();
} else if let Some(e) = line.trim().strip_prefix("License") { } else if let Some(e) = line.trim().strip_prefix("License") {
license = e.split_once(":").unwrap().1.trim(); license = e.split_once(':').unwrap().1.trim();
} }
if line.trim().starts_with("Description") { if line.trim().starts_with("Description") {
break; break;
@ -135,12 +135,12 @@ fn show_package(pkg: String) {
("", info) ("", info)
}) })
.1 .1
.split_once(":") .split_once(':')
.unwrap() .unwrap()
.1 .1
.trim() .trim()
.lines() .lines()
.map(|x| x.split_once(":").unwrap_or(("", x)).1.trim().to_owned() + "\n") .map(|x| x.split_once(':').unwrap_or(("", x)).1.trim().to_owned() + "\n")
.collect::<String>(); .collect::<String>();
/* /*

View file

@ -51,7 +51,7 @@ pub fn updates() -> gtk::Box {
List of names to skip when searching, this is to remove the List of names to skip when searching, this is to remove the
labels "Available Packages", "Last refreshed at ..." and "Installed Packages" labels "Available Packages", "Last refreshed at ..." and "Installed Packages"
*/ */
let skip_list = vec!["available", "installed", "last"]; let skip_list = ["available", "installed", "last"];
/* /*
Initialize the packages list, needs to be a static Initialize the packages list, needs to be a static
@ -88,14 +88,14 @@ pub fn updates() -> gtk::Box {
.split_whitespace() .split_whitespace()
.next() .next()
.unwrap_or("") .unwrap_or("")
.split(".") .split('.')
.next() .next()
.unwrap(); .unwrap();
b.to_string() b.to_string()
}) })
.filter(|x| { .filter(|x| {
!x.trim().is_empty() !x.trim().is_empty()
&& !x.contains("=") && !x.contains('=')
&& !skip_list.contains(&x.to_lowercase().trim()) && !skip_list.contains(&x.to_lowercase().trim())
}) })
.dedup() .dedup()
@ -107,7 +107,7 @@ pub fn updates() -> gtk::Box {
package and a button for it in the memory at the same time, package and a button for it in the memory at the same time,
which has caused as much as 1.4GB of RAM usage during tests. which has caused as much as 1.4GB of RAM usage during tests.
*/ */
for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].to_vec() { for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].iter() {
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder() let pkg_btn = Button::builder()
.label(pkg) .label(pkg)
@ -143,19 +143,23 @@ pub fn updates() -> gtk::Box {
.unwrap() .unwrap()
.connect_edge_reached(|_, edge| { .connect_edge_reached(|_, edge| {
if edge == PositionType::Bottom { if edge == PositionType::Bottom {
for i in PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(FULL_PKG_LIST.len())) { (PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(FULL_PKG_LIST.len()))).for_each(
let pkg_btn = Button::builder() |i| {
.label(FULL_PKG_LIST[i].as_str()) let pkg_btn = Button::builder()
.css_classes(["package-name"]) .label(FULL_PKG_LIST[i].as_str())
.build(); .css_classes(["package-name"])
.build();
pkg_btn.connect_clicked(|x| { pkg_btn.connect_clicked(|x| {
show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned())); show_package(
}); x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn); );
});
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn);
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
} },
);
} }
}) })
}; };
@ -204,7 +208,7 @@ unsafe fn search(f: &SearchEntry) {
x.to_lowercase() x.to_lowercase()
.contains(f.text().to_string().to_lowercase().as_str()) .contains(f.text().to_string().to_lowercase().as_str())
}) })
.map(|x| x.clone()) .cloned()
.collect(); .collect();
/* /*
@ -235,7 +239,7 @@ unsafe fn search(f: &SearchEntry) {
either 50 or the length of the list, whichever either 50 or the length of the list, whichever
is less, and make buttons in the list for each is less, and make buttons in the list for each
*/ */
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() { for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].iter() {
PKG_LIST_INDEX += 1; PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder() let pkg_btn = Button::builder()
.label(pkg) .label(pkg)