Add script-vars
This commit is contained in:
parent
5f164650e9
commit
2451f6fd49
12 changed files with 215 additions and 65 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -23,6 +23,8 @@ serde_json = "1.0"
|
|||
lazy_static = "1.4"
|
||||
pretty_assertions = "0.7"
|
||||
|
||||
anyhow = "1"
|
||||
|
||||
|
||||
simplexpr = { path = "../../projects/simplexpr" }
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
73
src/config/script_var_definition.rs
Normal file
73
src/config/script_var_definition.rs
Normal 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() })
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
})
|
||||
}
|
||||
}
|
32
src/config/var_definition.rs
Normal file
32
src/config/var_definition.rs
Normal 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 })
|
||||
}
|
||||
}
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
|
17
src/error.rs
17
src/error.rs
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ pub mod config;
|
|||
pub mod error;
|
||||
pub mod format_diagnostic;
|
||||
pub mod parser;
|
||||
mod util;
|
||||
pub mod value;
|
||||
|
|
|
@ -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
14
src/util.rs
Normal 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))
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue