diff --git a/Cargo.lock b/Cargo.lock index 160b64d..47215d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,6 +309,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "bitflags" version = "1.3.2" @@ -320,6 +326,9 @@ name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] [[package]] name = "block" @@ -442,6 +451,7 @@ dependencies = [ "freedesktop-icon-lookup", "log", "regex", + "ron", "serde", "serde_json", "walkdir", @@ -1188,6 +1198,18 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags 2.5.0", + "serde", + "serde_derive", +] + [[package]] name = "rustix" version = "0.37.27" diff --git a/Cargo.toml b/Cargo.toml index 6e4d406..3d6ae6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ freedesktop-desktop-entry = "0.5.2" freedesktop-icon-lookup = "0.1.3" log = "0.4.21" regex = "1.10.4" +ron = "0.8.1" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" walkdir = "2.5.0" diff --git a/src/container_type.rs b/src/container_type.rs new file mode 100644 index 0000000..3d3736c --- /dev/null +++ b/src/container_type.rs @@ -0,0 +1,95 @@ +use std::path::Path; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, Deserialize, Serialize)] +pub enum ContainerType { + Podman, + Docker, + Toolbox, + Unknown, +} + +impl ContainerType { + pub fn not_supported(self) -> bool { + matches!( + self, + ContainerType::Docker | ContainerType::Podman | ContainerType::Unknown + ) + } + + pub fn format_copy(self, container_name: &str, from: &Path, to: &Path) -> String { + match self { + ContainerType::Toolbox => { + format!( + "podman container cp {}:{}/. {}/", + container_name, + from.to_str().unwrap(), + to.to_str().unwrap() + ) + } + _ => "".to_string(), // TODO: Support more container types + } + } + + pub fn format_exec(self, container_name: &str, command: &str) -> String { + match self { + ContainerType::Toolbox => { + format!("toolbox run -c {} {}", container_name, command) + } + _ => "".to_string(), // TODO: Support more container types + } + } + + pub fn format_exec_regex_pattern(self) -> String { + match self { + ContainerType::Toolbox | ContainerType::Podman | ContainerType::Docker => { + r"(Exec=\s?)(.*)".to_string() + } + _ => "".to_string(), + } + } + + pub fn format_desktop_exec(self, container_name: &str) -> String { + match self { + ContainerType::Toolbox => { + format!(r"Exec=toolbox run -c {} ${{2}}", container_name) + } + ContainerType::Podman => { + // TODO: Currently not always functional + format!( + r"Exec=sh -c 'podman container start {} && podman container exec {} ${{2}}'", + container_name, container_name + ) + } + _ => "".to_string(), // TODO: Support more container types + } + } + + pub fn format_name_regex_pattern(self) -> String { + match self { + ContainerType::Toolbox | ContainerType::Podman | ContainerType::Docker => { + r"(Name=\s?)(.*)".to_string() + } + _ => "".to_string(), + } + } + + pub fn format_desktop_name(self, container_name: &str) -> String { + match self { + ContainerType::Toolbox => { + format!(r"Name=${{2}} ({})", container_name) + } + _ => "".to_string(), // TODO: Support more container types + } + } + + pub fn format_start(self, container_name: &str) -> String { + match self { + ContainerType::Toolbox => { + format!("toolbox run -c {} echo 'Started'", container_name) + } + _ => "".to_string(), // TODO: Support more container types + } + } +} diff --git a/src/main.rs b/src/main.rs index 482fbd3..80e8b56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,14 @@ use clap::Parser; -use server::ServerError; +use container_type::ContainerType; +use ron::de::SpannedError; +use serde::{Deserialize, Serialize}; +use std::error::Error; +use std::fmt::Display; use std::{env, fs, io}; use std::{fs::read_to_string, path::Path}; +mod container_type; mod desktop_entry; mod server; @@ -13,150 +18,66 @@ mod server; struct Args { #[arg(short, long, requires = "server", value_name = "CONFIG_PATH")] /// [AS SERVER] Path to an alternate config for the program. - /// Default is $HOME/.config/container-desktop-entries/containers.conf + /// Default is $HOME/.config/container-desktop-entries/containers.ron config: Option, } -#[derive(Debug, Clone, Copy)] -enum ContainerType { - Podman, - Docker, - Toolbox, - Unknown, +#[derive(Debug, Clone, Deserialize, Serialize)] +struct ContainerList { + pub containers: Vec<(String, ContainerType)>, } -impl From for ContainerType { - fn from(value: String) -> Self { - match value.as_str() { - "toolbox" => ContainerType::Toolbox, - "docker" => ContainerType::Docker, - "podman" => ContainerType::Podman, - _ => ContainerType::Unknown, - } - } -} -impl From for String { - fn from(value: ContainerType) -> Self { - match value { - ContainerType::Toolbox => "toolbox".to_string(), - ContainerType::Docker => "docker".to_string(), - ContainerType::Podman => "podman".to_string(), - ContainerType::Unknown => "".to_string(), - } - } +#[derive(Debug)] +enum CDEError { + IO(io::Error), + NoEnv(std::env::VarError), + Ron(SpannedError), } -impl ContainerType { - fn not_supported(self) -> bool { - matches!( - self, - ContainerType::Docker | ContainerType::Podman | ContainerType::Unknown - ) - } - - fn format_copy(self, container_name: &str, from: &Path, to: &Path) -> String { - match self { - ContainerType::Toolbox => { - format!( - "podman container cp {}:{}/. {}/", - container_name, - from.to_str().unwrap(), - to.to_str().unwrap() - ) - } - _ => "".to_string(), // TODO: Support more container types - } - } - - fn format_exec(self, container_name: &str, command: &str) -> String { - match self { - ContainerType::Toolbox => { - format!("toolbox run -c {} {}", container_name, command) - } - _ => "".to_string(), // TODO: Support more container types - } +impl Error for CDEError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None } - fn format_exec_regex_pattern(self) -> String { - match self { - ContainerType::Toolbox | ContainerType::Podman | ContainerType::Docker => { - r"(Exec=\s?)(.*)".to_string() - } - _ => "".to_string(), - } + fn description(&self) -> &str { + "description() is deprecated; use Display" } - fn format_desktop_exec(self, container_name: &str) -> String { - match self { - ContainerType::Toolbox => { - format!(r"Exec=toolbox run -c {} ${{2}}", container_name) - } - ContainerType::Podman => { - // TODO: Currently not always functional - format!( - r"Exec=sh -c 'podman container start {} && podman container exec {} ${{2}}'", - container_name, container_name - ) - } - _ => "".to_string(), // TODO: Support more container types - } - } - - fn format_name_regex_pattern(self) -> String { - match self { - ContainerType::Toolbox | ContainerType::Podman | ContainerType::Docker => { - r"(Name=\s?)(.*)".to_string() - } - _ => "".to_string(), - } - } - - fn format_desktop_name(self, container_name: &str) -> String { - match self { - ContainerType::Toolbox => { - format!(r"Name=${{2}} ({})", container_name) - } - _ => "".to_string(), // TODO: Support more container types - } + fn cause(&self) -> Option<&dyn Error> { + self.source() } +} - fn format_start(self, container_name: &str) -> String { +impl Display for CDEError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ContainerType::Toolbox => { - format!("toolbox run -c {} echo 'Started'", container_name) - } - _ => "".to_string(), // TODO: Support more container types + Self::IO(e) => e.fmt(f), + Self::NoEnv(e) => e.fmt(f), + Self::Ron(e) => e.fmt(f), } } } -#[derive(Debug)] -enum Error { - Server(ServerError), - IO(io::Error), - NoEnv(std::env::VarError), -} - -impl From for Error { - fn from(value: ServerError) -> Self { - Error::Server(value) +impl From for CDEError { + fn from(value: io::Error) -> Self { + Self::IO(value) } } -impl From for Error { - fn from(value: io::Error) -> Self { - Error::IO(value) +impl From for CDEError { + fn from(value: std::env::VarError) -> Self { + Self::NoEnv(value) } } -impl From for Error { - fn from(value: std::env::VarError) -> Self { - Error::NoEnv(value) +impl From for CDEError { + fn from(value: SpannedError) -> Self { + Self::Ron(value) } } #[async_std::main] -async fn main() -> Result<(), Error> { +async fn main() -> Result<(), CDEError> { env_logger::init(); if !cfg!(target_os = "linux") { @@ -167,7 +88,7 @@ async fn main() -> Result<(), Error> { let args = Args::parse(); let default_path_str = format!( - "{}/.config/container-desktop-entries/containers.conf", + "{}/.config/container-desktop-entries/containers.ron", env::var("HOME")? ); let conf_path = match args.config.as_ref() { @@ -181,18 +102,9 @@ async fn main() -> Result<(), Error> { } _ => {} } - log::info!("Running as server! Getting config at '{:?}'", conf_path); - let config_data = read_to_string(conf_path)? - .lines() - .map(|s| { - let ss = s - .split_once(" ") - .expect("Config invalid. make sure all lines are <> <>"); - (ss.0.to_string(), ContainerType::from(ss.1.to_string())) - }) - .collect::>(); - - server::server(config_data).await?; + let config_data: ContainerList = ron::from_str(&read_to_string(conf_path)?)?; + + server::server(config_data).await; Ok(()) } diff --git a/src/server.rs b/src/server.rs index 0087270..f9601a8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -3,7 +3,7 @@ use std::{ fs::{self, create_dir, read, read_to_string}, io, path::{Path, PathBuf}, - process::{self, Command}, + process::Command, }; use freedesktop_desktop_entry::DesktopEntry; @@ -11,10 +11,7 @@ use regex::Regex; use walkdir::WalkDir; use zbus::Connection; -use crate::{desktop_entry::DesktopEntryProxy, ContainerType}; - -#[derive(Debug)] -pub enum ServerError {} +use crate::{container_type::ContainerType, desktop_entry::DesktopEntryProxy, ContainerList}; #[derive(Debug)] pub enum ClientSetupError { @@ -34,7 +31,7 @@ impl From for ClientSetupError { } } -pub async fn server(containers: Vec<(String, ContainerType)>) -> Result<(), ServerError> { +pub async fn server(containers: ContainerList) { let home = match env::var("RUNTIME_DIRECTORY") { Ok(h) => h, Err(_) => { @@ -43,7 +40,7 @@ pub async fn server(containers: Vec<(String, ContainerType)>) -> Result<(), Serv } }; let to_path = Path::new(&home).join(Path::new(".cache/container-desktop-entries/")); - for (container_name, container_type) in containers { + for (container_name, container_type) in containers.containers { if container_type.not_supported() { log::error!( "Container type {:?} is currently not supported!", @@ -55,11 +52,6 @@ pub async fn server(containers: Vec<(String, ContainerType)>) -> Result<(), Serv log::error!("Error setting up client {}: {:?}", container_name, kind); } } - // let _ = fs::remove_dir_all(&to_path); - loop { - // Busy wait until logging off, keeping the desktop entries alive - std::future::pending::<()>().await; - } } async fn set_up_client(