From af8365675d33dd49c7317df4cf889533fd46e84d Mon Sep 17 00:00:00 2001 From: Hellx2 Date: Wed, 21 Aug 2024 18:29:12 +1000 Subject: [PATCH] Updated CHANGELOG.md, documented a bunch of stuff and fixed a lot of warnings. --- CHANGELOG.md | 14 +++++-- src/containers/{core.rs => api.rs} | 63 ++++++++++++++++++++++++++++ src/containers/images.rs | 66 +++++++++++++++++++++++++++--- src/containers/mod.rs | 36 ++++------------ src/drivers/mod.rs | 8 ++-- src/packages/browse.rs | 20 +++++---- src/packages/installed.rs | 38 +++++++++-------- src/packages/mod.rs | 10 ++--- src/packages/updates.rs | 38 +++++++++-------- 9 files changed, 204 insertions(+), 89 deletions(-) rename src/containers/{core.rs => api.rs} (74%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77f8205..bdbc00f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/containers/core.rs b/src/containers/api.rs similarity index 74% rename from src/containers/core.rs rename to src/containers/api.rs index 289d4e1..c9b7a44 100644 --- a/src/containers/core.rs +++ b/src/containers/api.rs @@ -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 { + let parts: Vec = 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, } +/* +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; +/// 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, } +/* +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 { diff --git a/src/containers/images.rs b/src/containers/images.rs index bcc62af..0f84c2c 100644 --- a/src/containers/images.rs +++ b/src/containers/images.rs @@ -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::().unwrap()) .collect::>(); + // 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::() @@ -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 = - serde_json::from_str::>(&containers::core::change_keys( + serde_json::from_str::>(&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) diff --git a/src/containers/mod.rs b/src/containers/mod.rs index 10d3530..6d94d2e 100644 --- a/src/containers/mod.rs +++ b/src/containers/mod.rs @@ -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 { - let parts: Vec = 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(" "), - }) - } -} diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index 150ca49..48dcb67 100644 --- a/src/drivers/mod.rs +++ b/src/drivers/mod.rs @@ -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 { @@ -175,7 +173,7 @@ pub fn get_driver_details() -> Vec { .lines() .filter(|x| x.trim().contains("driver")) .map(|x| { - x.split(":") + x.split(':') .nth(1) .expect("Failed to find colon in driver definition.") .trim() diff --git a/src/packages/browse.rs b/src/packages/browse.rs index 44be840..ac96a42 100644 --- a/src/packages/browse.rs +++ b/src/packages/browse.rs @@ -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) diff --git a/src/packages/installed.rs b/src/packages/installed.rs index 542ba48..6c07ba4 100644 --- a/src/packages/installed.rs +++ b/src/packages/installed.rs @@ -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())) { - let pkg_btn = Button::builder() - .label(FULL_PKG_LIST[i].as_str()) - .css_classes(["package-name"]) - .build(); + (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())); - }); - PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn); + pkg_btn.connect_clicked(|x| { + 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; - } + 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) diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 5461aed..f790964 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -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::(); /* diff --git a/src/packages/updates.rs b/src/packages/updates.rs index a873265..5791c99 100644 --- a/src/packages/updates.rs +++ b/src/packages/updates.rs @@ -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())) { - let pkg_btn = Button::builder() - .label(FULL_PKG_LIST[i].as_str()) - .css_classes(["package-name"]) - .build(); + (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())); - }); - PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn); + pkg_btn.connect_clicked(|x| { + 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; - } + 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)