From 8dba5aca83fbb2c59d841cf9f1a2f3484572d688 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 2 Mar 2021 13:12:52 +0100 Subject: [PATCH] Add EwwPaths struct and refactor path handling in general --- src/app.rs | 12 ++--- src/client.rs | 5 +- src/main.rs | 130 +++++++++++++++++++++++++++++++++----------------- src/opts.rs | 2 +- src/server.rs | 49 +++++++------------ 5 files changed, 114 insertions(+), 84 deletions(-) diff --git a/src/app.rs b/src/app.rs index b319872..e69cc27 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,13 +4,14 @@ use crate::{ display_backend, eww_state, script_var_handler::*, value::{Coords, NumWithUnit, PrimitiveValue, VarName}, + EwwPaths, }; use anyhow::*; use debug_stub_derive::*; use gdk::WindowExt; use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt}; use itertools::Itertools; -use std::{collections::HashMap, path::PathBuf}; +use std::collections::HashMap; use tokio::sync::mpsc::UnboundedSender; /// Response that the app may send as a response to a event. @@ -91,8 +92,7 @@ pub struct App { #[debug_stub = "ScriptVarHandler(...)"] pub script_var_handler: ScriptVarHandlerHandle, - pub config_file_path: PathBuf, - pub scss_file_path: PathBuf, + pub paths: EwwPaths, } impl App { @@ -110,14 +110,14 @@ impl App { DaemonCommand::ReloadConfigAndCss(sender) => { let mut errors = Vec::new(); - let config_result = - config::RawEwwConfig::read_from_file(&self.config_file_path).and_then(config::EwwConfig::generate); + let config_result = config::RawEwwConfig::read_from_file(&self.paths.get_eww_xml_path()) + .and_then(config::EwwConfig::generate); match config_result { Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), Err(e) => errors.push(e), } - let css_result = crate::util::parse_scss_from_file(&self.scss_file_path); + let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); match css_result { Ok(new_css) => self.handle_command(DaemonCommand::UpdateCss(new_css)), Err(e) => errors.push(e), diff --git a/src/client.rs b/src/client.rs index fb3b758..7ccd245 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,6 +3,7 @@ use std::process::Stdio; use crate::{ app, opts::{self, ActionClientOnly}, + EwwPaths, }; use anyhow::*; use std::{ @@ -10,11 +11,11 @@ use std::{ os::unix::net::UnixStream, }; -pub fn handle_client_only_action(action: ActionClientOnly) -> Result<()> { +pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) -> Result<()> { match action { ActionClientOnly::Logs => { std::process::Command::new("tail") - .args(["-f", crate::LOG_FILE.to_string_lossy().as_ref()].iter()) + .args(["-f", paths.get_log_file().to_string_lossy().as_ref()].iter()) .stdin(Stdio::null()) .spawn()? .wait()?; diff --git a/src/main.rs b/src/main.rs index ad06baf..687e961 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,8 +12,10 @@ extern crate gio; extern crate gtk; use anyhow::*; - -use std::{os::unix::net, path::PathBuf}; +use std::{ + os::unix::net, + path::{Path, PathBuf}, +}; pub mod app; pub mod application_lifecycle; @@ -30,38 +32,6 @@ pub mod util; pub mod value; pub mod widgets; -lazy_static::lazy_static! { - static ref IPC_SOCKET_PATH: std::path::PathBuf = std::env::var("XDG_RUNTIME_DIR") - .map(std::path::PathBuf::from) - .unwrap_or_else(|_| std::path::PathBuf::from("/tmp")) - .join("eww-server"); - - pub static ref CONFIG_DIR: std::path::PathBuf = std::env::var("XDG_CONFIG_HOME") - .map(PathBuf::from) - .unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap()).join(".config")) - .join("eww"); - - pub static ref LOG_FILE: std::path::PathBuf = std::env::var("XDG_CACHE_HOME") - .map(PathBuf::from) - .unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap()).join(".cache")) - .join("eww.log"); -} - -pub struct Paths {} - -pub fn calculate_socket_path>(config_file_path: P) -> std::path::PathBuf { - let daemon_id = base64::encode(format!("{}", config_file_path.as_ref().display())); - let socket_filename = format!( - "{}_{}", - &*crate::IPC_SOCKET_PATH - .file_name() - .and_then(|x| x.to_str()) - .expect("Invalid socket path"), - daemon_id, - ); - crate::IPC_SOCKET_PATH.with_file_name(socket_filename).to_path_buf() -} - fn main() { let opts: opts::Opt = opts::Opt::from_env(); @@ -76,16 +46,20 @@ fn main() { .init(); let result: Result<_> = try { - let socket_path = calculate_socket_path(opts.config_path.clone().unwrap_or(CONFIG_DIR.join("eww.xml"))); + let paths = opts.config_path.map(EwwPaths::from_config_dir).unwrap_or_default(); + match opts.action { opts::Action::ClientOnly(action) => { - client::handle_client_only_action(action)?; + client::handle_client_only_action(&paths, action)?; } opts::Action::WithServer(action) => { - log::info!("Trying to find server process at socket {}", socket_path.display()); - match net::UnixStream::connect(&socket_path) { + log::info!( + "Trying to find server process at socket {}", + paths.get_ipc_socket_file().display() + ); + match net::UnixStream::connect(&paths.get_ipc_socket_file()) { Ok(stream) => { - log::info!("Connected to Eww server ({}).", &socket_path.display()); + log::info!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display()); let response = client::do_server_call(stream, action).context("Error while forwarding command to server")?; if let Some(response) = response { @@ -105,15 +79,15 @@ fn main() { opts::Action::Daemon => { // make sure that there isn't already a Eww daemon running. - if check_server_running(&socket_path) { + if check_server_running(paths.get_ipc_socket_file()) { eprintln!("Eww server already running."); std::process::exit(1); } else { - log::info!("Initializing Eww server. ({})", socket_path.display()); - let _ = std::fs::remove_file(socket_path); + log::info!("Initializing Eww server. ({})", paths.get_ipc_socket_file().display()); + let _ = std::fs::remove_file(paths.get_ipc_socket_file()); println!("Run `eww logs` to see any errors, warnings or information while editing your configuration."); - server::initialize_server(opts.config_path)?; + server::initialize_server(paths)?; } } } @@ -126,9 +100,77 @@ fn main() { } /// Check if a eww server is currently running by trying to send a ping message to it. -fn check_server_running(socket_path: &std::path::PathBuf) -> bool { +fn check_server_running(socket_path: impl AsRef) -> bool { let response = net::UnixStream::connect(socket_path) .ok() .and_then(|stream| client::do_server_call(stream, opts::ActionWithServer::Ping).ok()); response.is_some() } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EwwPaths { + log_file: PathBuf, + ipc_socket_file: PathBuf, + config_dir: PathBuf, +} + +impl EwwPaths { + pub fn from_config_dir>(config_dir: P) -> Self { + let daemon_id = base64::encode(format!("{}", config_dir.as_ref().display())); + + EwwPaths { + config_dir: config_dir.as_ref().to_path_buf(), + log_file: std::env::var("XDG_CACHE_HOME") + .map(PathBuf::from) + .unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap()).join(".cache")) + .join(format!("eww_{}.log", daemon_id)), + ipc_socket_file: std::env::var("XDG_RUNTIME_DIR") + .map(std::path::PathBuf::from) + .unwrap_or_else(|_| std::path::PathBuf::from("/tmp")) + .join(format!("eww-server_{}", daemon_id)), + } + } + + pub fn get_log_file(&self) -> &Path { + self.log_file.as_path() + } + + pub fn get_ipc_socket_file(&self) -> &Path { + self.ipc_socket_file.as_path() + } + + pub fn get_config_dir(&self) -> &Path { + self.config_dir.as_path() + } + + pub fn get_eww_xml_path(&self) -> PathBuf { + self.config_dir.join("eww.xml") + } + + pub fn get_eww_scss_path(&self) -> PathBuf { + self.config_dir.join("eww.scss") + } +} + +impl Default for EwwPaths { + fn default() -> Self { + let config_dir = std::env::var("XDG_CONFIG_HOME") + .map(PathBuf::from) + .unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap()).join(".config")) + .join("eww"); + + Self::from_config_dir(config_dir) + } +} + +impl std::fmt::Display for EwwPaths { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "config-dir: {}, ipc-socket: {}, log-file: {}", + self.config_dir.display(), + self.ipc_socket_file.display(), + self.log_file.display() + ) + } +} diff --git a/src/opts.rs b/src/opts.rs index 6aee70a..3fcf806 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -22,7 +22,7 @@ struct RawOpt { #[structopt(long = "debug", global = true)] log_debug: bool, - /// override config-file path (path to eww.xml) + /// override path to configuration directory (directory that contains eww.xml and eww.scss) #[structopt(short, long, global = true)] config: Option, diff --git a/src/server.rs b/src/server.rs index d0ff05f..ed1f760 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,15 +1,11 @@ -use crate::{app, config, eww_state::*, ipc_server, script_var_handler, try_logging_errors, util}; +use crate::{app, config, eww_state::*, ipc_server, script_var_handler, try_logging_errors, util, EwwPaths}; use anyhow::*; use futures_util::StreamExt; -use std::{ - collections::HashMap, - os::unix::io::AsRawFd, - path::{Path, PathBuf}, -}; +use std::{collections::HashMap, os::unix::io::AsRawFd, path::Path}; use tokio::sync::mpsc::*; -pub fn initialize_server(config_dir_override: Option) -> Result<()> { - do_detach()?; +pub fn initialize_server(paths: EwwPaths) -> Result<()> { + do_detach(&paths.get_log_file())?; println!( r#" @@ -28,18 +24,11 @@ pub fn initialize_server(config_dir_override: Option) -> Res }); let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel(); - let config_file_path = config_dir_override.unwrap_or(crate::CONFIG_DIR.join("eww.xml")); + std::env::set_current_dir(&paths.get_config_dir()) + .with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?; - let config_dir = config_file_path - .parent() - .context("config file did not have a parent?!")? - .to_owned(); - std::env::set_current_dir(&config_dir) - .with_context(|| format!("Failed to change working directory to {}", config_dir.display()))?; - let scss_file_path = config_dir.join("eww.scss"); - - log::info!("reading configuration from {:?}", &config_file_path); - let eww_config = config::EwwConfig::read_from_file(&config_file_path)?; + log::info!("Loading paths: {}", &paths); + let eww_config = config::EwwConfig::read_from_file(&paths.get_eww_xml_path())?; gtk::init()?; @@ -53,20 +42,19 @@ pub fn initialize_server(config_dir_override: Option) -> Res css_provider: gtk::CssProvider::new(), script_var_handler, app_evt_send: ui_send.clone(), - config_file_path, - scss_file_path, + paths, }; if let Some(screen) = gdk::Screen::get_default() { gtk::StyleContext::add_provider_for_screen(&screen, &app.css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION); } - if let Ok(eww_css) = util::parse_scss_from_file(&app.scss_file_path) { + if let Ok(eww_css) = util::parse_scss_from_file(&app.paths.get_eww_scss_path()) { app.load_css(&eww_css)?; } // initialize all the handlers and tasks running asyncronously - init_async_part(app.config_file_path.clone(), app.scss_file_path.clone(), ui_send); + init_async_part(app.paths.clone(), ui_send); glib::MainContext::default().spawn_local(async move { while let Some(event) = ui_recv.recv().await { @@ -80,7 +68,7 @@ pub fn initialize_server(config_dir_override: Option) -> Res Ok(()) } -fn init_async_part(config_file_path: PathBuf, scss_file_path: PathBuf, ui_send: UnboundedSender) { +fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender) { std::thread::spawn(move || { let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -89,14 +77,13 @@ fn init_async_part(config_file_path: PathBuf, scss_file_path: PathBuf, ui_send: rt.block_on(async { let filewatch_join_handle = { let ui_send = ui_send.clone(); - let config_file_path = config_file_path.clone(); - tokio::spawn(async move { run_filewatch(config_file_path, scss_file_path, ui_send).await }) + let paths = paths.clone(); + tokio::spawn(async move { run_filewatch(paths.get_eww_xml_path(), paths.get_eww_scss_path(), ui_send).await }) }; let ipc_server_join_handle = { let ui_send = ui_send.clone(); - let socket_path = crate::calculate_socket_path(config_file_path); - tokio::spawn(async move { ipc_server::run_server(ui_send, socket_path).await }) + tokio::spawn(async move { ipc_server::run_server(ui_send, paths.get_ipc_socket_file()).await }) }; let forward_exit_to_app_handle = { @@ -168,7 +155,7 @@ async fn run_filewatch>( } /// detach the process from the terminal, also redirecting stdout and stderr to LOG_FILE -fn do_detach() -> Result<()> { +fn do_detach(log_file_path: impl AsRef) -> Result<()> { // detach from terminal match unsafe { nix::unistd::fork()? } { nix::unistd::ForkResult::Parent { .. } => { @@ -180,10 +167,10 @@ fn do_detach() -> Result<()> { let file = std::fs::OpenOptions::new() .create(true) .append(true) - .open(&*crate::LOG_FILE) + .open(&log_file_path) .expect(&format!( "Error opening log file ({}), for writing", - &*crate::LOG_FILE.to_string_lossy() + log_file_path.as_ref().to_string_lossy() )); let fd = file.as_raw_fd();