From 2a7255c3d5ad850b10a5abe5fe84e19e27c29d59 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 1 Aug 2021 19:22:45 +0200 Subject: [PATCH] General refactors --- crates/eww/src/app.rs | 43 ++++---------------- crates/eww/src/client.rs | 4 +- crates/eww/src/config/eww_config.rs | 8 ++++ crates/eww/src/daemon_response.rs | 39 ++++++++++++++++++ crates/eww/src/error_handling_ctx.rs | 39 ++++++++++-------- crates/eww/src/main.rs | 4 +- crates/eww/src/opts.rs | 15 ++++--- crates/eww/src/server.rs | 19 +++------ crates/eww/src/widgets/widget_definitions.rs | 2 +- 9 files changed, 97 insertions(+), 76 deletions(-) create mode 100644 crates/eww/src/daemon_response.rs diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index d06482c..4158f6c 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,4 +1,4 @@ -use crate::{config, display_backend, error_handling_ctx, eww_state, script_var_handler::*, EwwPaths}; +use crate::{EwwPaths, config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state, script_var_handler::*}; use anyhow::*; use debug_stub_derive::*; use eww_shared_util::VarName; @@ -13,27 +13,6 @@ use yuck::{ value::Coords, }; -/// Response that the app may send as a response to a event. -/// This is used in `DaemonCommand`s that contain a response sender. -#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] -pub enum DaemonResponse { - Success(String), - Failure(String), -} - -impl DaemonResponse { - pub fn is_success(&self) -> bool { - matches!(self, DaemonResponse::Success(_)) - } - - pub fn is_failure(&self) -> bool { - !self.is_success() - } -} - -pub type DaemonResponseSender = tokio::sync::mpsc::UnboundedSender; -pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver; - #[derive(Debug)] pub enum DaemonCommand { NoOp, @@ -111,11 +90,7 @@ impl App { DaemonCommand::ReloadConfigAndCss(sender) => { let mut errors = Vec::new(); - error_handling_ctx::clear_files(); - let config_result = config::EwwConfig::read_from_file( - &mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), - &self.paths.get_yuck_path(), - ); + let config_result = config::read_from_file(&self.paths.get_yuck_path()); match config_result.and_then(|new_config| self.load_config(new_config)) { Ok(()) => {} Err(e) => errors.push(e), @@ -129,9 +104,9 @@ impl App { let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n"); if errors.is_empty() { - sender.send(DaemonResponse::Success(String::new()))?; + sender.send_success(String::new())?; } else { - sender.send(DaemonResponse::Failure(errors))?; + sender.send_failure(errors)?; } } DaemonCommand::UpdateConfig(config) => { @@ -176,7 +151,7 @@ impl App { .map(|(key, value)| format!("{}: {}", key, value)) .join("\n") }; - sender.send(DaemonResponse::Success(output)).context("sending response from main thread")? + sender.send_success(output)? } DaemonCommand::PrintWindows(sender) => { let output = self @@ -188,11 +163,11 @@ impl App { format!("{}{}", if is_open { "*" } else { "" }, window_name) }) .join("\n"); - sender.send(DaemonResponse::Success(output)).context("sending response from main thread")? + sender.send_success(output)? } DaemonCommand::PrintDebug(sender) => { let output = format!("state: {:#?}\n\nconfig: {:#?}", &self.eww_state, &self.eww_config); - sender.send(DaemonResponse::Success(output)).context("sending response from main thread")? + sender.send_success(output)? } } }; @@ -387,8 +362,8 @@ fn get_monitor_geometry(n: i32) -> gdk::Rectangle { /// In case of an Err, send the error message to a sender. fn respond_with_error(sender: DaemonResponseSender, result: Result) -> Result<()> { match result { - Ok(_) => sender.send(DaemonResponse::Success(String::new())), - Err(e) => sender.send(DaemonResponse::Failure(error_handling_ctx::format_error(&e))), + Ok(_) => sender.send_success(String::new()), + Err(e) => sender.send_failure(error_handling_ctx::format_error(&e)), } .context("sending response from main thread") } diff --git a/crates/eww/src/client.rs b/crates/eww/src/client.rs index 1297196..60bee3d 100644 --- a/crates/eww/src/client.rs +++ b/crates/eww/src/client.rs @@ -1,7 +1,7 @@ use std::process::Stdio; use crate::{ - app, + daemon_response::DaemonResponse, opts::{self, ActionClientOnly}, EwwPaths, }; @@ -24,7 +24,7 @@ pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) -> Ok(()) } -pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result> { +pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result> { log::info!("Forwarding options to server"); stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?; diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index ffc275b..722f019 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -7,8 +7,16 @@ use yuck::config::{ use simplexpr::dynval::DynVal; +use crate::error_handling_ctx; + use super::{script_var, EwwWindowDefinition}; +/// Load an [EwwConfig] from a given file, resetting and applying the global YuckFiles object in [error_handling_ctx]. +pub fn read_from_file(path: impl AsRef) -> Result { + error_handling_ctx::clear_files(); + EwwConfig::read_from_file(&mut error_handling_ctx::YUCK_FILES.write().unwrap(), path) +} + /// Eww configuration structure. #[derive(Debug, Clone)] pub struct EwwConfig { diff --git a/crates/eww/src/daemon_response.rs b/crates/eww/src/daemon_response.rs new file mode 100644 index 0000000..19e96e2 --- /dev/null +++ b/crates/eww/src/daemon_response.rs @@ -0,0 +1,39 @@ +use anyhow::*; + +/// Response that the app may send as a response to a event. +/// This is used in `DaemonCommand`s that contain a response sender. +#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] +pub enum DaemonResponse { + Success(String), + Failure(String), +} + +impl DaemonResponse { + pub fn is_success(&self) -> bool { + matches!(self, DaemonResponse::Success(_)) + } + + pub fn is_failure(&self) -> bool { + !self.is_success() + } +} + +#[derive(Debug)] +pub struct DaemonResponseSender(tokio::sync::mpsc::UnboundedSender); + +pub fn create_pair() -> (DaemonResponseSender, tokio::sync::mpsc::UnboundedReceiver) { + let (sender, recv) = tokio::sync::mpsc::unbounded_channel(); + (DaemonResponseSender(sender), recv) +} + +impl DaemonResponseSender { + pub fn send_success(&self, s: String) -> Result<()> { + self.0.send(DaemonResponse::Success(s)).context("Failed to send success response from application thread") + } + + pub fn send_failure(&self, s: String) -> Result<()> { + self.0.send(DaemonResponse::Failure(s)).context("Failed to send failure response from application thread") + } +} + +pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver; diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 0611e51..dd0d2fb 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -1,4 +1,7 @@ -use std::sync::{Arc, Mutex}; +//! Disgusting global state. +//! I hate this, but [buffet](https://github.com/buffet) told me that this is what I should do for peak maintainability! + +use std::sync::{Arc, RwLock}; use codespan_reporting::{ diagnostic::Diagnostic, @@ -11,24 +14,10 @@ use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic: use crate::error::DiagError; -pub static ERROR_HANDLING_CTX: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(YuckFiles::new()))); +pub static YUCK_FILES: Lazy>> = Lazy::new(|| Arc::new(RwLock::new(YuckFiles::new()))); pub fn clear_files() { - *ERROR_HANDLING_CTX.lock().unwrap() = YuckFiles::new(); -} - -pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { - if let Some(err) = err.downcast_ref::() { - err.diag.clone() - } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() - } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() - } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() - } else { - gen_diagnostic!(err) - } + *YUCK_FILES.write().unwrap() = YuckFiles::new(); } pub fn print_error(err: anyhow::Error) { @@ -52,6 +41,20 @@ pub fn format_error(err: &anyhow::Error) -> String { } } +pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { + if let Some(err) = err.downcast_ref::() { + err.diag.clone() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() + } else { + gen_diagnostic!(err) + } +} + pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diagnostic) -> anyhow::Result { diagnostic.labels.drain_filter(|label| Span(label.range.start, label.range.end, label.file_id).is_dummy()); @@ -61,7 +64,7 @@ pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diag config.chars = chars; let mut buf = Vec::new(); let mut writer = term::termcolor::Ansi::new(&mut buf); - let files = ERROR_HANDLING_CTX.lock().unwrap(); + let files = YUCK_FILES.read().unwrap(); term::emit(&mut writer, &config, &*files, &diagnostic)?; Ok(String::from_utf8(buf)?) } diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 956dbdd..ed7d758 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -13,6 +13,7 @@ extern crate gtk; extern crate gtk_layer_shell as gtk_layer_shell; use anyhow::*; +use daemon_response::DaemonResponseReceiver; use opts::ActionWithServer; use std::{ os::unix::net, @@ -37,6 +38,7 @@ pub mod script_var_handler; pub mod server; pub mod util; pub mod widgets; +mod daemon_response; fn main() { let eww_binary_name = std::env::args().next().unwrap(); @@ -114,7 +116,7 @@ fn main() { } } -fn listen_for_daemon_response(mut recv: app::DaemonResponseReceiver) { +fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) { let rt = tokio::runtime::Builder::new_current_thread().enable_time().build().expect("Failed to initialize tokio runtime"); rt.block_on(async { if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), recv.recv()).await { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 802c2d3..01550a3 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -5,7 +5,10 @@ use simplexpr::dynval::DynVal; use structopt::StructOpt; use yuck::{config::window_geometry::AnchorPoint, value::Coords}; -use crate::app; +use crate::{ + app, + daemon_response::{self, DaemonResponse, DaemonResponseSender}, +}; /// Struct that gets generated from `RawOpt`. #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -158,7 +161,7 @@ fn parse_var_update_arg(s: &str) -> Result<(VarName, DynVal)> { } impl ActionWithServer { - pub fn into_daemon_command(self) -> (app::DaemonCommand, Option) { + pub fn into_daemon_command(self) -> (app::DaemonCommand, Option) { let command = match self { ActionWithServer::Update { mappings } => app::DaemonCommand::UpdateVars(mappings), @@ -166,7 +169,7 @@ impl ActionWithServer { ActionWithServer::CloseAll => app::DaemonCommand::CloseAll, ActionWithServer::Ping => { let (send, recv) = tokio::sync::mpsc::unbounded_channel(); - let _ = send.send(app::DaemonResponse::Success("pong".to_owned())); + let _ = send.send(DaemonResponse::Success("pong".to_owned())); return (app::DaemonCommand::NoOp, Some(recv)); } ActionWithServer::OpenMany { windows } => { @@ -197,10 +200,10 @@ impl ActionWithServer { } } -fn with_response_channel(f: F) -> (O, Option>) +fn with_response_channel(f: F) -> (O, Option>) where - F: FnOnce(tokio::sync::mpsc::UnboundedSender) -> O, + F: FnOnce(DaemonResponseSender) -> O, { - let (sender, recv) = tokio::sync::mpsc::unbounded_channel(); + let (sender, recv) = daemon_response::create_pair(); (f(sender), Some(recv)) } diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 784e0a4..dbcae9f 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,9 +1,4 @@ -use crate::{ - app::{self, DaemonCommand}, - config, error_handling_ctx, - eww_state::*, - ipc_server, script_var_handler, util, EwwPaths, -}; +use crate::{EwwPaths, app::{self, DaemonCommand}, config, daemon_response, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util}; use anyhow::*; use std::{ @@ -22,11 +17,7 @@ pub fn initialize_server(paths: EwwPaths, action: Option) -> Resu log::info!("Loading paths: {}", &paths); - // disgusting global state, I hate this, but https://github.com/buffet told me that this is what I should do for peak maintainability - error_handling_ctx::clear_files(); - - let read_config = - config::EwwConfig::read_from_file(&mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &paths.get_yuck_path()); + let read_config = config::read_from_file(&paths.get_yuck_path()); let eww_config = match read_config { Ok(config) => config, @@ -167,12 +158,12 @@ async fn run_filewatch>(config_dir: P, evt_send: UnboundedSender< debounce_done.store(true, Ordering::SeqCst); }); - let (daemon_resp_sender, mut daemon_resp_response) = tokio::sync::mpsc::unbounded_channel(); + let (daemon_resp_sender, mut daemon_resp_response) = daemon_response::create_pair(); evt_send.send(app::DaemonCommand::ReloadConfigAndCss(daemon_resp_sender))?; tokio::spawn(async move { match daemon_resp_response.recv().await { - Some(app::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"), - Some(app::DaemonResponse::Failure(e)) => eprintln!("{}", e), + Some(daemon_response::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"), + Some(daemon_response::DaemonResponse::Failure(e)) => eprintln!("{}", e), None => log::error!("No response to reload configuration-reload request"), } }); diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 61e4744..755f3f2 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -525,7 +525,7 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { if !content.is_empty() { let widget_node_result: AstResult<_> = try { let ast = { - let mut yuck_files = error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(); + let mut yuck_files = error_handling_ctx::YUCK_FILES.write().unwrap(); let (span, asts) = yuck_files.load_str("".to_string(), content)?; if let Some(file_id) = literal_file_id.replace(Some(span.2)) { yuck_files.unload(file_id);