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
- 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.
- Added this changelog.
- Updated `inst.sh` so that it no longer copies `style.css` to the `/usr/share/oreon-system-manager` directory.
Commit 2
- Moved `drivers` to it's own module folder
- Added `src/drivers/mod.rs` and `src/drivers/objects.rs`
- Moved `drivers` to it's own module folder.
- Added `src/drivers/mod.rs` and `src/drivers/objects.rs`.
Commit 3
- Fixed crash on package description fetch
- Fixed crash on package description fetch.
Commit 4
- Added very basic support for Docker images.
@ -20,3 +20,9 @@ Commit 4
Commit 5
- Added almost full support for Docker images.
- 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};
/**
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`
command.
@ -25,6 +57,10 @@ pub struct ImageInspectData {
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 {
fn default() -> Self {
ImageInspectData {
@ -47,6 +83,7 @@ impl Default for ImageInspectData {
}
}
/// The struct for the `config` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIConfig {
pub hostname: String,
@ -70,6 +107,10 @@ pub struct IIConfig {
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 {
fn default() -> Self {
IIConfig {
@ -94,14 +135,20 @@ impl Default for IIConfig {
}
}
/// Type for the `labels` property of `IIConfig`
pub type IIConfigLabels = BTreeMap<String, String>;
/// Struct for the `graph_driver` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIGraphDriver {
pub data: GraphDriverData,
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 {
fn default() -> Self {
IIGraphDriver {
@ -111,12 +158,18 @@ impl Default for IIGraphDriver {
}
}
/// The `data` property of `IIGraphDriver`
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct GraphDriverData {
pub merged_dir: String,
pub upper_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 {
fn default() -> GraphDriverData {
GraphDriverData {
@ -127,12 +180,17 @@ impl Default for GraphDriverData {
}
}
/// Struct for defining the `rootfs` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIRootFS {
pub r#type: 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 {
fn default() -> Self {
IIRootFS {
@ -142,11 +200,16 @@ impl Default for IIRootFS {
}
}
/// Struct for the `metadata` property of `ImageInspectData`
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IIMetadata {
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 {
fn default() -> Self {
IIMetadata {

View file

@ -4,13 +4,17 @@ use gtk::{
};
use crate::{
containers::{self, core::ImageInspectData},
containers::api::{change_keys, Image, ImageInspectData},
pkexec,
};
use super::Image;
/**
Subsection of the containers page to display
Docker imags provided from the `docker images`
command.
*/
pub fn images() -> gtk::Box {
// Box for displaying the list of images.
let images_box = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.build();
@ -26,6 +30,7 @@ pub fn images() -> gtk::Box {
.map(|x| x.parse::<Image>().unwrap())
.collect::<Vec<Image>>();
// Test image, remove in the main build.
images.push(Image {
repository: "Test".to_owned(),
tag: "something".to_owned(),
@ -34,12 +39,22 @@ pub fn images() -> gtk::Box {
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 {
let image_box = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.css_classes(["image-box"])
.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("Tag", &image.tag));
image_box.append(&gen_box("Image ID", &image.id));
@ -52,7 +67,12 @@ pub fn images() -> gtk::Box {
.build();
image_button.connect_clicked(|x| {
/*
Show the details for the image through a
function.
*/
show_image(
// Get the image's unique image ID.
x.child()
.expect("Failed to get image_button child!")
.downcast::<gtk::Box>()
@ -79,17 +99,37 @@ pub fn images() -> gtk::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> =
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)
.expect("This error should never occur"),
))
.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();
// 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()
.title(format!("Image ID {image_id}").as_str())
.default_width(600)
@ -101,6 +141,12 @@ fn show_image(image_id: &str) {
.css_classes(["image-showbox"])
.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("Comment", &image_details.comment));
main_box.append(&gen_box("Id", &image_details.id));
@ -117,6 +163,14 @@ fn show_image(image_id: &str) {
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 {
let return_box = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal)

View file

@ -1,16 +1,13 @@
use std::str::FromStr;
use gtk::{prelude::BoxExt, Stack, StackSidebar};
use images::images;
pub mod core;
pub mod api;
pub mod images;
/**
Page for managing Docker containers and images.
## TODO
- Move to it's own file/folder
- Add the following pages: "Containers", "Images", "Browse"
*/
pub fn containers() -> gtk::Box {
@ -20,6 +17,7 @@ pub fn containers() -> gtk::Box {
.css_classes(["containers-box"])
.build();
// Stack to contain each of the pages.
let containers_stack = Stack::builder()
.halign(gtk::Align::Fill)
.valign(gtk::Align::Fill)
@ -27,12 +25,14 @@ pub fn containers() -> gtk::Box {
.vexpand(true)
.build();
// Sidebar to allow the user to switch between pages.
let containers_sidebar = StackSidebar::builder()
.stack(&containers_stack)
.halign(gtk::Align::Start)
.valign(gtk::Align::Fill)
.build();
// Add each of the pages to the stack.
containers_stack.add_titled(&images(), Some("images"), "Images");
// Start the docker daemon if it isn't running.
@ -40,31 +40,13 @@ pub fn containers() -> gtk::Box {
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_stack);
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);
}
let scrollable = ScrolledWindow::builder()
ScrolledWindow::builder()
.child(&list)
.width_request(300)
.height_request(600)
.halign(Align::Start)
.css_classes(["installed_list"])
.build();
scrollable
.build()
}
pub fn get_driver_details() -> Vec<String> {
@ -175,7 +173,7 @@ pub fn get_driver_details() -> Vec<String> {
.lines()
.filter(|x| x.trim().contains("driver"))
.map(|x| {
x.split(":")
x.split(':')
.nth(1)
.expect("Failed to find colon in driver definition.")
.trim()

View file

@ -1,3 +1,5 @@
#![allow(clippy::assigning_clones)]
use gtk::{
prelude::{BoxExt, ButtonExt, EditableExt},
Button, PositionType,
@ -44,7 +46,7 @@ pub fn browse() -> gtk::Box {
List of names to skip when searching, this is to remove the
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
@ -75,14 +77,14 @@ pub fn browse() -> gtk::Box {
.split_whitespace()
.next()
.unwrap_or("")
.split(".")
.split('.')
.next()
.unwrap();
b.to_string()
})
.filter(|x| {
!x.trim().is_empty()
&& !x.contains("=")
&& !x.contains('=')
&& !skip_list.contains(&x.to_lowercase().trim())
})
.dedup()
@ -103,7 +105,7 @@ pub fn browse() -> gtk::Box {
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.
*/
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;
let pkg_btn = Button::builder()
.label(pkg)
@ -139,9 +141,11 @@ pub fn browse() -> gtk::Box {
.unwrap()
.connect_edge_reached(|_, edge| {
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()
.label(PKG_LIST[i].as_str())
.label(i.as_str())
.css_classes(["package-name"])
.build();
@ -202,7 +206,7 @@ unsafe fn search(f: &SearchEntry) {
x.to_lowercase()
.contains(f.text().to_string().to_lowercase().as_str())
})
.map(|x| x.clone())
.cloned()
.collect();
/*
@ -233,7 +237,7 @@ unsafe fn search(f: &SearchEntry) {
either 50 or the length of the list, whichever
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;
let pkg_btn = Button::builder()
.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
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
@ -88,14 +88,14 @@ pub fn installed() -> gtk::Box {
.split_whitespace()
.next()
.unwrap_or("")
.split(".")
.split('.')
.next()
.unwrap();
b.to_string()
})
.filter(|x| {
!x.trim().is_empty()
&& !x.contains("=")
&& !x.contains('=')
&& !skip_list.contains(&x.to_lowercase().trim())
})
.dedup()
@ -107,7 +107,7 @@ pub fn installed() -> gtk::Box {
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.
*/
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;
let pkg_btn = Button::builder()
.label(pkg)
@ -143,19 +143,23 @@ pub fn installed() -> gtk::Box {
.unwrap()
.connect_edge_reached(|_, edge| {
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(
|i| {
let pkg_btn = Button::builder()
.label(FULL_PKG_LIST[i].as_str())
.css_classes(["package-name"])
.build();
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);
PKG_LIST_INDEX += 1;
}
},
);
}
})
};
@ -204,7 +208,7 @@ unsafe fn search(f: &SearchEntry) {
x.to_lowercase()
.contains(f.text().to_string().to_lowercase().as_str())
})
.map(|x| x.clone())
.cloned()
.collect();
/*
@ -235,7 +239,7 @@ unsafe fn search(f: &SearchEntry) {
either 50 or the length of the list, whichever
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;
let pkg_btn = Button::builder()
.label(pkg)

View file

@ -112,11 +112,11 @@ fn show_package(pkg: String) {
*/
for line in info.lines() {
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") {
size = e.split_once(":").unwrap().1.trim();
size = e.split_once(':').unwrap().1.trim();
} 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") {
break;
@ -135,12 +135,12 @@ fn show_package(pkg: String) {
("", info)
})
.1
.split_once(":")
.split_once(':')
.unwrap()
.1
.trim()
.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>();
/*

View file

@ -51,7 +51,7 @@ pub fn updates() -> gtk::Box {
List of names to skip when searching, this is to remove the
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
@ -88,14 +88,14 @@ pub fn updates() -> gtk::Box {
.split_whitespace()
.next()
.unwrap_or("")
.split(".")
.split('.')
.next()
.unwrap();
b.to_string()
})
.filter(|x| {
!x.trim().is_empty()
&& !x.contains("=")
&& !x.contains('=')
&& !skip_list.contains(&x.to_lowercase().trim())
})
.dedup()
@ -107,7 +107,7 @@ pub fn updates() -> gtk::Box {
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.
*/
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;
let pkg_btn = Button::builder()
.label(pkg)
@ -143,19 +143,23 @@ pub fn updates() -> gtk::Box {
.unwrap()
.connect_edge_reached(|_, edge| {
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(
|i| {
let pkg_btn = Button::builder()
.label(FULL_PKG_LIST[i].as_str())
.css_classes(["package-name"])
.build();
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);
PKG_LIST_INDEX += 1;
}
},
);
}
})
};
@ -204,7 +208,7 @@ unsafe fn search(f: &SearchEntry) {
x.to_lowercase()
.contains(f.text().to_string().to_lowercase().as_str())
})
.map(|x| x.clone())
.cloned()
.collect();
/*
@ -235,7 +239,7 @@ unsafe fn search(f: &SearchEntry) {
either 50 or the length of the list, whichever
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;
let pkg_btn = Button::builder()
.label(pkg)