diff --git a/needed.txt b/needed.txt index 5f609b2..2fa6756 100644 --- a/needed.txt +++ b/needed.txt @@ -1,6 +1,3 @@ cargo gtk4-devel - - -I didn't need to install this? - Deyo -cairo-gobject-devel \ No newline at end of file +docker diff --git a/src/containers/images.rs b/src/containers/images.rs new file mode 100644 index 0000000..0927af3 --- /dev/null +++ b/src/containers/images.rs @@ -0,0 +1,91 @@ +use gtk::{prelude::BoxExt, Label}; + +use super::Image; + +pub fn images() -> gtk::Box { + let images_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .build(); + + /* + Using unwrap here is allowed since this will always + return a Some when the second param is false + */ + let images = crate::pkexec("docker images".to_owned(), false) + .unwrap() + .lines() + .map(|x| x.parse::().unwrap()) + .collect::>(); + + for image in images { + let image_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .css_classes(["image-box"]) + .build(); + + let repo_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + repo_box.append( + &Label::builder() + .label("Repository: ") + .css_classes(["repo-label"]) + .build(), + ); + repo_box.append(&Label::builder().label(&image.repository).build()); + + let tag_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + tag_box.append( + &Label::builder() + .label("Tag: ") + .css_classes(["tag-label"]) + .build(), + ); + tag_box.append(&Label::builder().label(&image.tag).build()); + + let id_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + id_box.append( + &Label::builder() + .label("Image ID: ") + .css_classes(["id-label"]) + .build(), + ); + id_box.append(&Label::builder().label(&image.id).build()); + + let created_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + created_box.append( + &Label::builder() + .label("Created at: ") + .css_classes(["created-label"]) + .build(), + ); + created_box.append(&Label::builder().label(&image.created).build()); + + let size_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + size_box.append( + &Label::builder() + .label("Size: ") + .css_classes(["size-label"]) + .build(), + ); + size_box.append(&Label::builder().label(&image.size).build()); + + image_box.append(&repo_box); + image_box.append(&tag_box); + image_box.append(&id_box); + image_box.append(&created_box); + image_box.append(&size_box); + + images_box.append(&image_box); + } + + images_box +} diff --git a/src/containers/mod.rs b/src/containers/mod.rs new file mode 100644 index 0000000..da9d9b5 --- /dev/null +++ b/src/containers/mod.rs @@ -0,0 +1,69 @@ +use std::str::FromStr; + +use gtk::{prelude::BoxExt, Stack, StackSidebar}; +use images::images; + +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 { + // Initialize the box that will be used as a page. + let containers_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .css_classes(["containers-box"]) + .build(); + + let containers_stack = Stack::builder() + .halign(gtk::Align::Fill) + .valign(gtk::Align::Fill) + .hexpand(true) + .vexpand(true) + .build(); + + let containers_sidebar = StackSidebar::builder() + .stack(&containers_stack) + .halign(gtk::Align::Start) + .valign(gtk::Align::Fill) + .build(); + + containers_stack.add_titled(&images(), Some("images"), "Images"); + + // Start the docker daemon if it isn't running. + if std::fs::metadata("/var/run/docker.pid").is_err() { + crate::pkexec("dockerd".to_owned(), true); + } + + // Append a new label as a title and return the box. + 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 mut parts = value.split_whitespace().map(|x| x.to_owned()); + Ok(Self { + repository: parts.next().expect("Failed to get repository."), + tag: parts.next().expect("Failed to get tag."), + id: parts.next().expect("Failed to get ID."), + created: parts.next().expect("Failed to get created time."), + size: parts.next().expect("Failed to get size."), + }) + } +} diff --git a/src/main.rs b/src/main.rs index f6e96fc..cf90699 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use gtk::{ Stack, StackSwitcher, }; +pub mod containers; pub mod drivers; pub mod packages; @@ -65,7 +66,7 @@ fn on_activate(app: &Application) { */ main_stack.add_titled(&packages::packages(), Some("packages"), "Packages"); main_stack.add_titled(&drivers::drivers(), Some("drivers"), "Drivers"); - main_stack.add_titled(&containers(), Some("containers"), "Containers"); + main_stack.add_titled(&containers::containers(), Some("containers"), "Containers"); main_stack.add_titled(&virtualization(), Some("virtualization"), "Virtualization"); // Append the switcher and stack to the main box @@ -77,26 +78,6 @@ fn on_activate(app: &Application) { main_window.present(); } -/** -Page for managing Docker containers and images. - -## TODO -- Move to it's own file/folder -- Add the following pages: "Containers", "Images", "Browse" -*/ -fn containers() -> gtk::Box { - // Initialize the box that will be used as a page. - let containers_box = gtk::Box::builder() - .orientation(Horizontal) - .css_classes(["containers-box"]) - .build(); - - // Append a new label as a title and return the box. - containers_box.append(>k::Label::new(Some("Containers"))); - - containers_box -} - /** For managing virtual machines. @@ -116,6 +97,28 @@ fn virtualization() -> gtk::Box { virtualization_box } +pub fn pkexec(cmd: String, spawn: bool) -> Option { + dbg!(cmd.split_whitespace().next()); + if spawn { + std::process::Command::new("pkexec") + .args(cmd.split_whitespace()) + .spawn() + .expect("Failed to spawn child process!"); + None + } else { + Some( + String::from_utf8( + std::process::Command::new("pkexec") + .args(cmd.split_whitespace()) + .output() + .expect("Failed to get output of command.") + .stdout, + ) + .expect("Failed to convert command stdout to string!"), + ) + } +} + /// Function to link `style.css` to the application. fn on_startup(_: &Application) { let css_provider = gtk::CssProvider::new(); diff --git a/src/style.css b/src/style.css index 1c1c7dd..92af911 100644 --- a/src/style.css +++ b/src/style.css @@ -26,9 +26,23 @@ padding: 10px; } +/* Packages */ .license-label, .version-label, .size-label, -.description-label { +.description-label, + +/* Containers */ +.repo-label, +.tag-label, +.id-label, +.created-label, +.size-label { font-weight: bold; } + +.image-box { + border-radius: 10px; + box-shadow: -1px -1px #ff0000; + margin: 10px; +}