Add script-vars

This commit is contained in:
elkowar 2021-07-19 11:46:52 +02:00
parent 5f164650e9
commit 2451f6fd49
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
12 changed files with 215 additions and 65 deletions

7
Cargo.lock generated
View file

@ -20,6 +20,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
[[package]]
name = "ascii-canvas"
version = "3.0.0"
@ -195,6 +201,7 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
name = "eww_config"
version = "0.1.0"
dependencies = [
"anyhow",
"codespan-reporting",
"derive_more",
"insta",

View file

@ -23,6 +23,8 @@ serde_json = "1.0"
lazy_static = "1.4"
pretty_assertions = "0.7"
anyhow = "1"
simplexpr = { path = "../../projects/simplexpr" }

View file

@ -2,12 +2,16 @@ use std::collections::HashMap;
use simplexpr::SimplExpr;
use super::{var::VarDefinition, widget_definition::WidgetDefinition, widget_use::WidgetUse};
use super::{
script_var_definition::ScriptVarDefinition, var_definition::VarDefinition, widget_definition::WidgetDefinition,
widget_use::WidgetUse,
};
use crate::{
config::script_var_definition::{PollScriptVar, TailScriptVar},
error::{AstError, AstResult, OptionAstErrorExt},
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst},
element::{Element, FromAst, FromAstElementContent},
},
spanned,
value::{AttrName, VarName},
@ -15,6 +19,7 @@ use crate::{
pub enum TopLevel {
VarDefinition(VarDefinition),
ScriptVarDefinition(ScriptVarDefinition),
WidgetDefinition(WidgetDefinition),
}
@ -22,11 +27,21 @@ impl FromAst for TopLevel {
fn from_ast(e: Ast) -> AstResult<Self> {
let span = e.span();
spanned!(e.span(), {
let list = e.as_list_ref()?;
match list.first().or_missing()?.as_symbol_ref()?.as_ref() {
"defwidget" => Self::WidgetDefinition(WidgetDefinition::from_ast(e)?),
"defvar" => Self::VarDefinition(VarDefinition::from_ast(e)?),
x => return Err(AstError::UnknownToplevel(Some(span), x.to_string())),
let list = e.as_list()?;
let mut iter = AstIterator::new(list.into_iter());
let (sym_span, element_name) = iter.expect_symbol()?;
match element_name.as_str() {
x if x == WidgetDefinition::get_element_name() => {
Self::WidgetDefinition(WidgetDefinition::from_tail(span, iter)?)
}
x if x == VarDefinition::get_element_name() => Self::VarDefinition(VarDefinition::from_tail(span, iter)?),
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 => return Err(AstError::UnknownToplevel(Some(sym_span), x.to_string())),
}
})
}
@ -36,17 +51,22 @@ impl FromAst for TopLevel {
pub struct Config {
widget_definitions: HashMap<String, WidgetDefinition>,
var_definitions: HashMap<VarName, VarDefinition>,
script_vars: HashMap<VarName, ScriptVarDefinition>,
}
impl FromAst for Config {
fn from_ast(e: Ast) -> AstResult<Self> {
let list = e.as_list()?;
let mut config = Self { widget_definitions: HashMap::new(), var_definitions: HashMap::new() };
let mut config =
Self { widget_definitions: HashMap::new(), var_definitions: HashMap::new(), script_vars: HashMap::new() };
for element in list {
match TopLevel::from_ast(element)? {
TopLevel::VarDefinition(x) => {
config.var_definitions.insert(x.name.clone(), x);
}
TopLevel::ScriptVarDefinition(x) => {
config.script_vars.insert(x.name().clone(), x);
}
TopLevel::WidgetDefinition(x) => {
config.widget_definitions.insert(x.name.clone(), x);
}

View file

@ -1,5 +1,6 @@
mod config;
pub mod script_var_definition;
pub mod validate;
pub mod var;
pub mod var_definition;
pub mod widget_definition;
pub mod widget_use;

View file

@ -0,0 +1,73 @@
use std::collections::HashMap;
use simplexpr::{dynval::DynVal, SimplExpr};
use crate::{
error::{AstError, AstResult},
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst, FromAstElementContent},
},
spanned,
value::{AttrName, VarName},
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ScriptVarDefinition {
Poll(PollScriptVar),
Tail(TailScriptVar),
}
impl ScriptVarDefinition {
pub fn name(&self) -> &VarName {
match self {
ScriptVarDefinition::Poll(x) => &x.name,
ScriptVarDefinition::Tail(x) => &x.name,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum VarSource {
// TODO allow for other executors? (python, etc)
Shell(String),
Function(fn() -> Result<DynVal, Box<dyn std::error::Error>>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PollScriptVar {
pub name: VarName,
pub command: VarSource,
pub interval: std::time::Duration,
}
impl FromAstElementContent for PollScriptVar {
fn get_element_name() -> &'static str {
"defpollvar"
}
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
let (_, name) = iter.expect_symbol()?;
let attrs: HashMap<String, String> = iter.expect_key_values()?;
let interval = attrs.get("interval").unwrap();
let interval = crate::util::parse_duration(interval).map_err(|e| AstError::Other(Some(span), e.into()))?;
let (_, script) = iter.expect_value()?;
Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TailScriptVar {
pub name: VarName,
pub command: String,
}
impl FromAstElementContent for TailScriptVar {
fn get_element_name() -> &'static str {
"deftailvar"
}
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
let (_, name) = iter.expect_symbol()?;
let (_, script) = iter.expect_value()?;
Ok(Self { name: VarName(name), command: script.to_string() })
}
}

View file

@ -1,34 +0,0 @@
use std::collections::HashMap;
use simplexpr::{dynval::DynVal, SimplExpr};
use crate::{
error::AstResult,
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst},
},
spanned,
value::{AttrName, VarName},
};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct VarDefinition {
pub name: VarName,
pub initial_value: DynVal,
pub span: Span,
}
impl FromAst for VarDefinition {
fn from_ast(e: Ast) -> AstResult<Self> {
let span = e.span();
spanned!(e.span(), {
let list = e.as_list()?;
let mut iter = AstIterator::new(list.into_iter());
let _ = iter.expect_symbol()?;
let (_, name) = iter.expect_symbol()?;
let (_, initial_value) = iter.expect_value()?;
Self { name: VarName(name), initial_value, span }
})
}
}

View file

@ -0,0 +1,32 @@
use std::collections::HashMap;
use simplexpr::{dynval::DynVal, SimplExpr};
use crate::{
error::AstResult,
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst, FromAstElementContent},
},
spanned,
value::{AttrName, VarName},
};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct VarDefinition {
pub name: VarName,
pub initial_value: DynVal,
pub span: Span,
}
impl FromAstElementContent for VarDefinition {
fn get_element_name() -> &'static str {
"defvar"
}
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
let (_, name) = iter.expect_symbol()?;
let (_, initial_value) = iter.expect_value()?;
Ok(Self { name: VarName(name), initial_value, span })
}
}

View file

@ -6,7 +6,7 @@ use crate::{
error::AstResult,
parser::{
ast::{Ast, AstIterator, Span},
element::{Element, FromAst},
element::{Element, FromAst, FromAstElementContent},
},
spanned,
value::{AttrName, VarName},
@ -22,23 +22,18 @@ pub struct WidgetDefinition {
pub args_span: Span,
}
impl FromAst for WidgetDefinition {
fn from_ast(e: Ast) -> AstResult<Self> {
let span = e.span();
spanned!(e.span(), {
let list = e.as_list()?;
let mut iter = AstIterator::new(list.into_iter());
impl FromAstElementContent for WidgetDefinition {
fn get_element_name() -> &'static str {
"defwidget"
}
let (_, def_type) = iter.expect_symbol()?;
assert!(def_type == "defwidget");
let (_, name) = iter.expect_symbol()?;
let (args_span, expected_args) = iter.expect_array()?;
let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::<AstResult<_>>()?;
let widget = iter.expect_any().and_then(WidgetUse::from_ast)?;
// TODO verify that this was the last element in the list
// iter.expect_done()?;
Self { name, expected_args, widget, span, args_span }
})
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
let (_, name) = iter.expect_symbol()?;
let (args_span, expected_args) = iter.expect_array()?;
let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::<AstResult<_>>()?;
let widget = iter.expect_any().and_then(WidgetUse::from_ast)?;
// TODO verify that this was the last element in the list
// iter.expect_done()?;
Ok(Self { name, expected_args, widget, span, args_span })
}
}

View file

@ -20,6 +20,10 @@ pub enum AstError {
WrongExprType(Option<Span>, AstType, AstType),
#[error("Expected to get a value, but got {1}")]
NotAValue(Option<Span>, AstType),
#[error("Expected element {1}, but read {2}")]
MismatchedElementName(Option<Span>, String, String),
#[error("{1}")]
Other(Option<Span>, Box<dyn std::error::Error>),
#[error(transparent)]
ValidationError(#[from] ValidationError),
@ -35,6 +39,8 @@ impl AstError {
AstError::MissingNode(span) => *span,
AstError::WrongExprType(span, ..) => *span,
AstError::NotAValue(span, ..) => *span,
AstError::MismatchedElementName(span, ..) => *span,
AstError::Other(span, ..) => *span,
AstError::ValidationError(error) => None, // TODO none here is stupid
AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)),
}
@ -67,9 +73,14 @@ fn get_parse_error_span(
pub fn spanned(span: Span, err: impl Into<AstError>) -> AstError {
use AstError::*;
match err.into() {
AstError::UnknownToplevel(None, x) => AstError::UnknownToplevel(Some(span), x),
AstError::MissingNode(None) => AstError::MissingNode(Some(span)),
AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y),
UnknownToplevel(None, x) => UnknownToplevel(Some(span), x),
MissingNode(None) => MissingNode(Some(span)),
WrongExprType(None, x, y) => WrongExprType(Some(span), x, y),
UnknownToplevel(None, x) => UnknownToplevel(Some(span), x),
MissingNode(None) => MissingNode(Some(span)),
NotAValue(None, x) => NotAValue(Some(span), x),
MismatchedElementName(None, x, y) => MismatchedElementName(Some(span), x, y),
Other(None, x) => Other(Some(span), x),
x => x,
}
}

View file

@ -6,4 +6,5 @@ pub mod config;
pub mod error;
pub mod format_diagnostic;
pub mod parser;
mod util;
pub mod value;

View file

@ -1,7 +1,7 @@
use super::ast::{Ast, AstIterator, AstType, Span};
use crate::{error::*, parser, spanned, value::AttrName};
use itertools::Itertools;
use simplexpr::ast::SimplExpr;
use simplexpr::{ast::SimplExpr, dynval::DynVal};
use std::{
collections::{HashMap, LinkedList},
iter::FromIterator,
@ -18,6 +18,34 @@ impl FromAst for Ast {
}
}
impl FromAst for String {
fn from_ast(e: Ast) -> AstResult<Self> {
Ok(e.as_value()?.to_string())
}
}
/// A trait that allows creating a type from the tail of a list-node.
/// I.e. to parse (foo [a b] (c d)), [from_tail] would just get [a b] (c d).
pub trait FromAstElementContent: Sized {
fn get_element_name() -> &'static str;
fn from_tail<I: Iterator<Item = Ast>>(span: Span, iter: AstIterator<I>) -> AstResult<Self>;
}
impl<T: FromAstElementContent> FromAst for T {
fn from_ast(e: Ast) -> AstResult<Self> {
let span = e.span();
spanned!(e.span(), {
let list = e.as_list()?;
let mut iter = AstIterator::new(list.into_iter());
let (_, element_name) = iter.expect_symbol()?;
if Self::get_element_name() != element_name {
return Err(AstError::MismatchedElementName(Some(span), Self::get_element_name().to_string(), element_name));
}
Self::from_tail(span, iter)?
})
}
}
impl FromAst for SimplExpr {
fn from_ast(e: Ast) -> AstResult<Self> {
match e {

14
src/util.rs Normal file
View file

@ -0,0 +1,14 @@
pub fn parse_duration(s: &str) -> anyhow::Result<std::time::Duration> {
use std::time::Duration;
if s.ends_with("ms") {
Ok(Duration::from_millis(s.trim_end_matches("ms").parse()?))
} else if s.ends_with('s') {
Ok(Duration::from_secs(s.trim_end_matches('s').parse()?))
} else if s.ends_with('m') {
Ok(Duration::from_secs(s.trim_end_matches('m').parse::<u64>()? * 60))
} else if s.ends_with('h') {
Ok(Duration::from_secs(s.trim_end_matches('h').parse::<u64>()? * 60 * 60))
} else {
Err(anyhow::anyhow!("Failed to parse duration `{}`", s))
}
}