mirror of
https://github.com/elkowar/eww.git
synced 2024-09-19 07:37:18 +03:00
Add ad-hoc diagnostic errors
This commit is contained in:
parent
9f70a22cf0
commit
cff2f6beb8
@ -1,8 +1,21 @@
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::*;
|
||||
use eww_shared_util::{Span, VarName};
|
||||
use simplexpr::dynval::DynVal;
|
||||
use yuck::config::script_var_definition::{ScriptVarDefinition, VarSource};
|
||||
use yuck::{
|
||||
config::script_var_definition::{ScriptVarDefinition, VarSource},
|
||||
gen_diagnostic,
|
||||
};
|
||||
|
||||
use crate::error::DiagError;
|
||||
|
||||
pub fn create_script_var_failed_error(span: Span, var_name: &VarName) -> DiagError {
|
||||
DiagError::new(gen_diagnostic! {
|
||||
msg = format!("Failed to compute value for `{}`", var_name),
|
||||
label = span => "Defined here",
|
||||
})
|
||||
}
|
||||
|
||||
pub fn initial_value(var: &ScriptVarDefinition) -> Result<DynVal> {
|
||||
match var {
|
||||
@ -10,15 +23,20 @@ pub fn initial_value(var: &ScriptVarDefinition) -> Result<DynVal> {
|
||||
VarSource::Function(f) => {
|
||||
f().map_err(|err| anyhow!(err)).with_context(|| format!("Failed to compute initial value for {}", &var.name()))
|
||||
}
|
||||
VarSource::Shell(f) => run_command(f).with_context(|| format!("Failed to compute initial value for {}", &var.name())),
|
||||
VarSource::Shell(span, f) => run_command(f).map_err(|_| anyhow!(create_script_var_failed_error(*span, var.name()))),
|
||||
},
|
||||
ScriptVarDefinition::Tail(_) => Ok(DynVal::from_string(String::new())),
|
||||
ScriptVarDefinition::Listen(_) => Ok(DynVal::from_string(String::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a command and get the output
|
||||
pub fn run_command(cmd: &str) -> Result<DynVal> {
|
||||
log::debug!("Running command: {}", cmd);
|
||||
let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?;
|
||||
let command = Command::new("/bin/sh").arg("-c").arg(cmd).output()?;
|
||||
if !command.status.success() {
|
||||
bail!("Execution of `{}` failed", cmd);
|
||||
}
|
||||
let output = String::from_utf8(command.stdout)?;
|
||||
let output = output.trim_matches('\n');
|
||||
Ok(DynVal::from(output))
|
||||
}
|
||||
|
20
crates/eww/src/error.rs
Normal file
20
crates/eww/src/error.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use codespan_reporting::diagnostic::Diagnostic;
|
||||
|
||||
/// An error that contains a [Diagnostic] for ad-hoc creation of diagnostics.
|
||||
#[derive(Debug)]
|
||||
pub struct DiagError {
|
||||
pub diag: Diagnostic<usize>,
|
||||
}
|
||||
|
||||
impl DiagError {
|
||||
pub fn new(diag: Diagnostic<usize>) -> Self {
|
||||
Self { diag }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for DiagError {}
|
||||
impl std::fmt::Display for DiagError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.diag.message)
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@ use yuck::{
|
||||
format_diagnostic::{eval_error_to_diagnostic, ToDiagnostic},
|
||||
};
|
||||
|
||||
use crate::error::DiagError;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref ERROR_HANDLING_CTX: Arc<Mutex<FsYuckFiles>> = Arc::new(Mutex::new(FsYuckFiles::new()));
|
||||
}
|
||||
@ -18,29 +20,25 @@ pub fn clear_files() {
|
||||
}
|
||||
|
||||
pub fn print_error(err: &anyhow::Error) {
|
||||
match err.downcast_ref::<AstError>() {
|
||||
Some(err) => {
|
||||
eprintln!("{:?}\n{}", err, stringify_diagnostic(err.to_diagnostic()));
|
||||
}
|
||||
None => match err.downcast_ref::<EvalError>() {
|
||||
Some(err) => {
|
||||
eprintln!("{:?}\n{}", err, stringify_diagnostic(eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN))));
|
||||
}
|
||||
None => {
|
||||
log::error!("{:?}", err);
|
||||
}
|
||||
},
|
||||
if let Some(err) = err.downcast_ref::<DiagError>() {
|
||||
eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.diag));
|
||||
} else if let Some(err) = err.downcast_ref::<AstError>() {
|
||||
eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.to_diagnostic()));
|
||||
} else if let Some(err) = err.downcast_ref::<EvalError>() {
|
||||
eprintln!("{:?}\n{}", err, stringify_diagnostic(&eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN))));
|
||||
} else {
|
||||
log::error!("{:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_error(err: &anyhow::Error) -> String {
|
||||
match err.downcast_ref::<AstError>() {
|
||||
Some(err) => stringify_diagnostic(err.to_diagnostic()),
|
||||
Some(err) => stringify_diagnostic(&err.to_diagnostic()),
|
||||
None => format!("{:?}", err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stringify_diagnostic(diagnostic: Diagnostic<usize>) -> String {
|
||||
pub fn stringify_diagnostic(diagnostic: &Diagnostic<usize>) -> String {
|
||||
use codespan_reporting::term;
|
||||
let config = term::Config::default();
|
||||
let mut buf = Vec::new();
|
||||
|
@ -32,6 +32,7 @@ pub mod script_var_handler;
|
||||
pub mod server;
|
||||
pub mod util;
|
||||
pub mod widgets;
|
||||
pub mod error;
|
||||
|
||||
fn main() {
|
||||
let opts: opts::Opt = opts::Opt::from_env();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::app;
|
||||
use crate::{app, config::create_script_var_failed_error};
|
||||
use anyhow::*;
|
||||
use app::DaemonCommand;
|
||||
|
||||
@ -11,7 +11,7 @@ use tokio::{
|
||||
sync::mpsc::UnboundedSender,
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use yuck::config::script_var_definition::{PollScriptVar, ScriptVarDefinition, TailScriptVar};
|
||||
use yuck::config::script_var_definition::{ListenScriptVar, PollScriptVar, ScriptVarDefinition, VarSource};
|
||||
|
||||
/// Initialize the script var handler, and return a handle to that handler, which can be used to control
|
||||
/// the script var execution.
|
||||
@ -23,7 +23,7 @@ pub fn init(evt_send: UnboundedSender<DaemonCommand>) -> ScriptVarHandlerHandle
|
||||
rt.block_on(async {
|
||||
let _: Result<_> = try {
|
||||
let mut handler = ScriptVarHandler {
|
||||
tail_handler: TailVarHandler::new(evt_send.clone())?,
|
||||
listen_handler: ListenVarHandler::new(evt_send.clone())?,
|
||||
poll_handler: PollVarHandler::new(evt_send)?,
|
||||
};
|
||||
crate::loop_select_exiting! {
|
||||
@ -87,7 +87,7 @@ enum ScriptVarHandlerMsg {
|
||||
|
||||
/// Handler that manages running and updating [ScriptVarDefinition]s
|
||||
struct ScriptVarHandler {
|
||||
tail_handler: TailVarHandler,
|
||||
listen_handler: ListenVarHandler,
|
||||
poll_handler: PollVarHandler,
|
||||
}
|
||||
|
||||
@ -95,14 +95,14 @@ impl ScriptVarHandler {
|
||||
async fn add(&mut self, script_var: ScriptVarDefinition) {
|
||||
match script_var {
|
||||
ScriptVarDefinition::Poll(var) => self.poll_handler.start(var).await,
|
||||
ScriptVarDefinition::Tail(var) => self.tail_handler.start(var).await,
|
||||
ScriptVarDefinition::Listen(var) => self.listen_handler.start(var).await,
|
||||
};
|
||||
}
|
||||
|
||||
/// Stop the handler that is responsible for a given variable.
|
||||
fn stop_for_variable(&mut self, name: &VarName) -> Result<()> {
|
||||
log::debug!("Stopping script var process for variable {}", name);
|
||||
self.tail_handler.stop_for_variable(name);
|
||||
self.listen_handler.stop_for_variable(name);
|
||||
self.poll_handler.stop_for_variable(name);
|
||||
Ok(())
|
||||
}
|
||||
@ -110,7 +110,7 @@ impl ScriptVarHandler {
|
||||
/// stop all running scripts and schedules
|
||||
fn stop_all(&mut self) {
|
||||
log::debug!("Stopping script-var-handlers");
|
||||
self.tail_handler.stop_all();
|
||||
self.listen_handler.stop_all();
|
||||
self.poll_handler.stop_all();
|
||||
}
|
||||
}
|
||||
@ -163,8 +163,10 @@ impl PollVarHandler {
|
||||
|
||||
fn run_poll_once(var: &PollScriptVar) -> Result<DynVal> {
|
||||
match &var.command {
|
||||
yuck::config::script_var_definition::VarSource::Shell(x) => crate::config::script_var::run_command(x),
|
||||
yuck::config::script_var_definition::VarSource::Function(x) => x().map_err(|e| anyhow!(e)),
|
||||
VarSource::Shell(span, x) => crate::config::script_var::run_command(x).map_err(|_| {
|
||||
anyhow!(create_script_var_failed_error(*span, &var.name))
|
||||
}),
|
||||
VarSource::Function(x) => x().map_err(|e| anyhow!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,21 +176,21 @@ impl Drop for PollVarHandler {
|
||||
}
|
||||
}
|
||||
|
||||
struct TailVarHandler {
|
||||
struct ListenVarHandler {
|
||||
evt_send: UnboundedSender<DaemonCommand>,
|
||||
tail_process_handles: HashMap<VarName, CancellationToken>,
|
||||
listen_process_handles: HashMap<VarName, CancellationToken>,
|
||||
}
|
||||
|
||||
impl TailVarHandler {
|
||||
impl ListenVarHandler {
|
||||
fn new(evt_send: UnboundedSender<DaemonCommand>) -> Result<Self> {
|
||||
let handler = TailVarHandler { evt_send, tail_process_handles: HashMap::new() };
|
||||
let handler = ListenVarHandler { evt_send, listen_process_handles: HashMap::new() };
|
||||
Ok(handler)
|
||||
}
|
||||
|
||||
async fn start(&mut self, var: TailScriptVar) {
|
||||
async fn start(&mut self, var: ListenScriptVar) {
|
||||
log::debug!("starting poll var {}", &var.name);
|
||||
let cancellation_token = CancellationToken::new();
|
||||
self.tail_process_handles.insert(var.name.clone(), cancellation_token.clone());
|
||||
self.listen_process_handles.insert(var.name.clone(), cancellation_token.clone());
|
||||
|
||||
let evt_send = self.evt_send.clone();
|
||||
tokio::spawn(async move {
|
||||
@ -215,18 +217,18 @@ impl TailVarHandler {
|
||||
}
|
||||
|
||||
fn stop_for_variable(&mut self, name: &VarName) {
|
||||
if let Some(token) = self.tail_process_handles.remove(name) {
|
||||
log::debug!("stopped tail var {}", name);
|
||||
if let Some(token) = self.listen_process_handles.remove(name) {
|
||||
log::debug!("stopped listen-var {}", name);
|
||||
token.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_all(&mut self) {
|
||||
self.tail_process_handles.drain().for_each(|(_, token)| token.cancel());
|
||||
self.listen_process_handles.drain().for_each(|(_, token)| token.cancel());
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TailVarHandler {
|
||||
impl Drop for ListenVarHandler {
|
||||
fn drop(&mut self) {
|
||||
self.stop_all();
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use super::{
|
||||
window_definition::WindowDefinition,
|
||||
};
|
||||
use crate::{
|
||||
config::script_var_definition::{PollScriptVar, TailScriptVar},
|
||||
config::script_var_definition::{ListenScriptVar, PollScriptVar},
|
||||
error::{AstError, AstResult, OptionAstErrorExt},
|
||||
parser::{
|
||||
ast::Ast,
|
||||
@ -59,8 +59,8 @@ impl FromAst for TopLevel {
|
||||
x if x == PollScriptVar::get_element_name() => {
|
||||
Self::ScriptVarDefinition(ScriptVarDefinition::Poll(PollScriptVar::from_tail(span, iter)?))
|
||||
}
|
||||
x if x == TailScriptVar::get_element_name() => {
|
||||
Self::ScriptVarDefinition(ScriptVarDefinition::Tail(TailScriptVar::from_tail(span, iter)?))
|
||||
x if x == ListenScriptVar::get_element_name() => {
|
||||
Self::ScriptVarDefinition(ScriptVarDefinition::Listen(ListenScriptVar::from_tail(span, iter)?))
|
||||
}
|
||||
x if x == WindowDefinition::get_element_name() => Self::WindowDefinition(WindowDefinition::from_tail(span, iter)?),
|
||||
x => return Err(AstError::UnknownToplevel(sym_span, x.to_string())),
|
||||
|
@ -15,14 +15,24 @@ use eww_shared_util::{AttrName, Span, VarName};
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
|
||||
pub enum ScriptVarDefinition {
|
||||
Poll(PollScriptVar),
|
||||
Tail(TailScriptVar),
|
||||
Listen(ListenScriptVar),
|
||||
}
|
||||
|
||||
impl ScriptVarDefinition {
|
||||
pub fn name(&self) -> &VarName {
|
||||
match self {
|
||||
ScriptVarDefinition::Poll(x) => &x.name,
|
||||
ScriptVarDefinition::Tail(x) => &x.name,
|
||||
ScriptVarDefinition::Listen(x) => &x.name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command_span(&self) -> Option<Span> {
|
||||
match self {
|
||||
ScriptVarDefinition::Poll(x) => match x.command {
|
||||
VarSource::Shell(span, _) => Some(span),
|
||||
VarSource::Function(_) => None,
|
||||
},
|
||||
ScriptVarDefinition::Listen(x) => Some(x.command_span),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,10 +40,11 @@ impl ScriptVarDefinition {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
|
||||
pub enum VarSource {
|
||||
// TODO allow for other executors? (python, etc)
|
||||
Shell(String),
|
||||
Shell(Span, String),
|
||||
#[serde(skip)]
|
||||
Function(fn() -> Result<DynVal, Box<dyn std::error::Error + Sync + Send + 'static>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct PollScriptVar {
|
||||
pub name: VarName,
|
||||
@ -43,32 +54,32 @@ pub struct PollScriptVar {
|
||||
|
||||
impl FromAstElementContent for PollScriptVar {
|
||||
fn get_element_name() -> &'static str {
|
||||
"defpollvar"
|
||||
"defpoll"
|
||||
}
|
||||
|
||||
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
|
||||
let (_, name) = iter.expect_symbol()?;
|
||||
let mut attrs = iter.expect_key_values()?;
|
||||
let interval = attrs.primitive_required::<DynVal, _>("interval")?.as_duration()?;
|
||||
// let interval = interval.as_duration()?;
|
||||
let (_, script) = iter.expect_literal()?;
|
||||
Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval })
|
||||
let (script_span, script) = iter.expect_literal()?;
|
||||
Ok(Self { name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct TailScriptVar {
|
||||
pub struct ListenScriptVar {
|
||||
pub name: VarName,
|
||||
pub command: String,
|
||||
pub command_span: Span,
|
||||
}
|
||||
impl FromAstElementContent for TailScriptVar {
|
||||
impl FromAstElementContent for ListenScriptVar {
|
||||
fn get_element_name() -> &'static str {
|
||||
"deftailvar"
|
||||
"deflisten"
|
||||
}
|
||||
|
||||
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
|
||||
let (_, name) = iter.expect_symbol()?;
|
||||
let (_, script) = iter.expect_literal()?;
|
||||
Ok(Self { name: VarName(name), command: script.to_string() })
|
||||
let (command_span, script) = iter.expect_literal()?;
|
||||
Ok(Self { name: VarName(name), command: script.to_string(), command_span })
|
||||
}
|
||||
}
|
||||
|
@ -12,24 +12,25 @@ fn span_to_secondary_label(span: Span) -> Label<usize> {
|
||||
Label::secondary(span.2, span.0..span.1)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! gen_diagnostic {
|
||||
(
|
||||
$(msg = $msg:expr)?
|
||||
$(, label = $span:expr $(=> $label:expr)?)?
|
||||
$(, note = $note:expr)? $(,)?
|
||||
) => {
|
||||
Diagnostic::error()
|
||||
::codespan_reporting::diagnostic::Diagnostic::error()
|
||||
$(.with_message($msg.to_string()))?
|
||||
$(.with_labels(vec![
|
||||
Label::primary($span.2, $span.0..$span.1)
|
||||
::codespan_reporting::diagnostic::Label::primary($span.2, $span.0..$span.1)
|
||||
$(.with_message($label))?
|
||||
]))?
|
||||
$(.with_notes(vec![$note]))?
|
||||
};
|
||||
($msg:expr $(, $span:expr $(,)?)?) => {{
|
||||
Diagnostic::error()
|
||||
::codespan_reporting::diagnostic::Diagnostic::error()
|
||||
.with_message($msg.to_string())
|
||||
$(.with_labels(vec![Label::primary($span.2, $span.0..$span.1)]))?
|
||||
$(.with_labels(vec![::codespan_reporting::diagnostic::Label::primary($span.2, $span.0..$span.1)]))?
|
||||
}};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user