Add docs for json values and make value related names shorter

This commit is contained in:
elkowar 2021-04-14 14:28:22 +02:00
parent ce4c22e2a6
commit aaac4c3b43
16 changed files with 223 additions and 221 deletions

View file

@ -32,4 +32,6 @@ The expression language supports:
- conditionals (`if condition then 'value' else 'other value'`) - conditionals (`if condition then 'value' else 'other value'`)
- numbers, strings, booleans and variable references (`12`, `'hi'`, `true`, `some_variable`) - numbers, strings, booleans and variable references (`12`, `'hi'`, `true`, `some_variable`)
- strings can contain other expressions again: `'foo {{some_variable}} bar'` - strings can contain other expressions again: `'foo {{some_variable}} bar'`
- json access (`object.field`, `array[12]`, `object["field"]`)
- for this, the object/array value needs to refer to a variable that contains a valid json string.

View file

@ -3,7 +3,7 @@ use crate::{
config::{window_definition::WindowName, AnchorPoint, WindowStacking}, config::{window_definition::WindowName, AnchorPoint, WindowStacking},
display_backend, eww_state, display_backend, eww_state,
script_var_handler::*, script_var_handler::*,
value::{Coords, NumWithUnit, PrimitiveValue, VarName}, value::{Coords, NumWithUnit, PrimVal, VarName},
EwwPaths, EwwPaths,
}; };
use anyhow::*; use anyhow::*;
@ -41,7 +41,7 @@ pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver<DaemonRes
#[derive(Debug)] #[derive(Debug)]
pub enum DaemonCommand { pub enum DaemonCommand {
NoOp, NoOp,
UpdateVars(Vec<(VarName, PrimitiveValue)>), UpdateVars(Vec<(VarName, PrimVal)>),
ReloadConfigAndCss(DaemonResponseSender), ReloadConfigAndCss(DaemonResponseSender),
UpdateConfig(config::EwwConfig), UpdateConfig(config::EwwConfig),
UpdateCss(String), UpdateCss(String),
@ -193,7 +193,7 @@ impl App {
gtk::main_quit(); gtk::main_quit();
} }
fn update_state(&mut self, fieldname: VarName, value: PrimitiveValue) { fn update_state(&mut self, fieldname: VarName, value: PrimVal) {
self.eww_state.update_variable(fieldname, value) self.eww_state.update_variable(fieldname, value)
} }

View file

@ -4,7 +4,7 @@ use regex::Regex;
use std::ops::Range; use std::ops::Range;
use crate::{ use crate::{
value::{AttrName, AttrValue}, value::{AttrName, AttrVal},
with_text_pos_context, with_text_pos_context,
}; };
use maplit::hashmap; use maplit::hashmap;
@ -41,7 +41,7 @@ impl WidgetDefinition {
pub struct WidgetUse { pub struct WidgetUse {
pub name: String, pub name: String,
pub children: Vec<WidgetUse>, pub children: Vec<WidgetUse>,
pub attrs: HashMap<AttrName, AttrValue>, pub attrs: HashMap<AttrName, AttrVal>,
pub text_pos: Option<TextPos>, pub text_pos: Option<TextPos>,
} }
@ -67,7 +67,7 @@ impl WidgetUse {
}; };
let text_pos = xml.text_pos(); let text_pos = xml.text_pos();
let widget_use = match xml { let widget_use = match xml {
XmlNode::Text(text) => WidgetUse::simple_text(AttrValue::parse_string(&text.text())), XmlNode::Text(text) => WidgetUse::simple_text(AttrVal::parse_string(&text.text())),
XmlNode::Element(elem) => WidgetUse { XmlNode::Element(elem) => WidgetUse {
name: elem.tag_name().to_owned(), name: elem.tag_name().to_owned(),
children: with_text_pos_context! { elem => elem.children().map(WidgetUse::from_xml_node).collect::<Result<_>>()?}?, children: with_text_pos_context! { elem => elem.children().map(WidgetUse::from_xml_node).collect::<Result<_>>()?}?,
@ -77,7 +77,7 @@ impl WidgetUse {
.map(|attr| { .map(|attr| {
( (
AttrName(attr.name().to_owned()), AttrName(attr.name().to_owned()),
AttrValue::parse_string(&xml_ext::resolve_escaped_symbols(&attr.value())), AttrVal::parse_string(&xml_ext::resolve_escaped_symbols(&attr.value())),
) )
}) })
.collect::<HashMap<_, _>>(), .collect::<HashMap<_, _>>(),
@ -88,7 +88,7 @@ impl WidgetUse {
Ok(widget_use.at_pos(text_pos)) Ok(widget_use.at_pos(text_pos))
} }
pub fn simple_text(text: AttrValue) -> Self { pub fn simple_text(text: AttrVal) -> Self {
WidgetUse { WidgetUse {
name: "label".to_owned(), name: "label".to_owned(),
children: vec![], children: vec![],
@ -111,7 +111,7 @@ mod test {
#[test] #[test]
fn test_simple_text() { fn test_simple_text() {
let expected_attr_value = AttrValue::from_primitive("my text"); let expected_attr_value = AttrVal::from_primitive("my text");
let widget = WidgetUse::simple_text(expected_attr_value.clone()); let widget = WidgetUse::simple_text(expected_attr_value.clone());
assert_eq!( assert_eq!(
widget, widget,
@ -138,12 +138,12 @@ mod test {
let expected = WidgetUse { let expected = WidgetUse {
name: "widget_name".to_owned(), name: "widget_name".to_owned(),
attrs: hashmap! { attrs: hashmap! {
AttrName("attr1".to_owned()) => AttrValue::from_primitive("hi"), AttrName("attr1".to_owned()) => AttrVal::from_primitive("hi"),
AttrName("attr2".to_owned()) => AttrValue::from_primitive("12"), AttrName("attr2".to_owned()) => AttrVal::from_primitive("12"),
}, },
children: vec![ children: vec![
WidgetUse::new("child_widget".to_owned(), Vec::new()), WidgetUse::new("child_widget".to_owned(), Vec::new()),
WidgetUse::simple_text(AttrValue::from_primitive("foo".to_owned())), WidgetUse::simple_text(AttrVal::from_primitive("foo".to_owned())),
], ],
..WidgetUse::default() ..WidgetUse::default()
}; };
@ -165,7 +165,7 @@ mod test {
size: Some((12, 20)), size: Some((12, 20)),
structure: WidgetUse { structure: WidgetUse {
name: "layout".to_owned(), name: "layout".to_owned(),
children: vec![WidgetUse::simple_text(AttrValue::from_primitive("test"))], children: vec![WidgetUse::simple_text(AttrVal::from_primitive("test"))],
attrs: HashMap::new(), attrs: HashMap::new(),
..WidgetUse::default() ..WidgetUse::default()
}, },

View file

@ -3,7 +3,7 @@ use std::collections::HashMap;
use crate::{ use crate::{
util, util,
value::{PrimitiveValue, VarName}, value::{PrimVal, VarName},
}; };
use super::{ use super::{
@ -18,7 +18,7 @@ use std::path::PathBuf;
pub struct EwwConfig { pub struct EwwConfig {
widgets: HashMap<String, WidgetDefinition>, widgets: HashMap<String, WidgetDefinition>,
windows: HashMap<WindowName, EwwWindowDefinition>, windows: HashMap<WindowName, EwwWindowDefinition>,
initial_variables: HashMap<VarName, PrimitiveValue>, initial_variables: HashMap<VarName, PrimVal>,
script_vars: HashMap<VarName, ScriptVar>, script_vars: HashMap<VarName, ScriptVar>,
pub filepath: PathBuf, pub filepath: PathBuf,
} }
@ -44,7 +44,7 @@ impl EwwConfig {
} }
// TODO this is kinda ugly // TODO this is kinda ugly
pub fn generate_initial_state(&self) -> Result<HashMap<VarName, PrimitiveValue>> { pub fn generate_initial_state(&self) -> Result<HashMap<VarName, PrimVal>> {
let mut vars = let mut vars =
self.script_vars.iter().map(|var| Ok((var.0.clone(), var.1.initial_value()?))).collect::<Result<HashMap<_, _>>>()?; self.script_vars.iter().map(|var| Ok((var.0.clone(), var.1.initial_value()?))).collect::<Result<HashMap<_, _>>>()?;
vars.extend(self.initial_variables.clone()); vars.extend(self.initial_variables.clone());
@ -73,7 +73,7 @@ impl EwwConfig {
pub struct RawEwwConfig { pub struct RawEwwConfig {
widgets: HashMap<String, WidgetDefinition>, widgets: HashMap<String, WidgetDefinition>,
windows: HashMap<WindowName, RawEwwWindowDefinition>, windows: HashMap<WindowName, RawEwwWindowDefinition>,
initial_variables: HashMap<VarName, PrimitiveValue>, initial_variables: HashMap<VarName, PrimVal>,
script_vars: HashMap<VarName, ScriptVar>, script_vars: HashMap<VarName, ScriptVar>,
pub filepath: PathBuf, pub filepath: PathBuf,
} }
@ -181,14 +181,14 @@ impl RawEwwConfig {
} }
} }
fn parse_variables_block(xml: XmlElement) -> Result<(HashMap<VarName, PrimitiveValue>, HashMap<VarName, ScriptVar>)> { fn parse_variables_block(xml: XmlElement) -> Result<(HashMap<VarName, PrimVal>, HashMap<VarName, ScriptVar>)> {
let mut normal_vars = HashMap::new(); let mut normal_vars = HashMap::new();
let mut script_vars = HashMap::new(); let mut script_vars = HashMap::new();
for node in xml.child_elements() { for node in xml.child_elements() {
match node.tag_name() { match node.tag_name() {
"var" => { "var" => {
let value = node.only_child().map(|c| c.as_text_or_sourcecode()).unwrap_or_else(|_| String::new()); let value = node.only_child().map(|c| c.as_text_or_sourcecode()).unwrap_or_else(|_| String::new());
normal_vars.insert(VarName(node.attr("name")?.to_owned()), PrimitiveValue::from_string(value)); normal_vars.insert(VarName(node.attr("name")?.to_owned()), PrimVal::from_string(value));
} }
"script-var" => { "script-var" => {
let script_var = ScriptVar::from_xml_element(node)?; let script_var = ScriptVar::from_xml_element(node)?;

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
util, util,
value::{PrimitiveValue, VarName}, value::{PrimVal, VarName},
}; };
use anyhow::*; use anyhow::*;

View file

@ -14,7 +14,7 @@ pub struct PollScriptVar {
} }
impl PollScriptVar { impl PollScriptVar {
pub fn run_once(&self) -> Result<PrimitiveValue> { pub fn run_once(&self) -> Result<PrimVal> {
run_command(&self.command) run_command(&self.command)
} }
} }
@ -39,12 +39,12 @@ impl ScriptVar {
} }
} }
pub fn initial_value(&self) -> Result<PrimitiveValue> { pub fn initial_value(&self) -> Result<PrimVal> {
match self { match self {
ScriptVar::Poll(x) => { ScriptVar::Poll(x) => {
run_command(&x.command).with_context(|| format!("Failed to compute initial value for {}", &self.name())) run_command(&x.command).with_context(|| format!("Failed to compute initial value for {}", &self.name()))
} }
ScriptVar::Tail(_) => Ok(PrimitiveValue::from_string(String::new())), ScriptVar::Tail(_) => Ok(PrimVal::from_string(String::new())),
} }
} }
@ -63,9 +63,9 @@ impl ScriptVar {
} }
/// Run a command and get the output /// Run a command and get the output
fn run_command(cmd: &str) -> Result<PrimitiveValue> { fn run_command(cmd: &str) -> Result<PrimVal> {
log::debug!("Running command: {}", cmd); log::debug!("Running command: {}", cmd);
let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?; let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?;
let output = output.trim_matches('\n'); let output = output.trim_matches('\n');
Ok(PrimitiveValue::from(output)) Ok(PrimVal::from(output))
} }

View file

@ -1,17 +1,17 @@
use crate::{ use crate::{
config::window_definition::WindowName, config::window_definition::WindowName,
value::{AttrName, AttrValueElement, VarName}, value::{AttrName, AttrValElement, VarName},
}; };
use anyhow::*; use anyhow::*;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use crate::value::{AttrValue, PrimitiveValue}; use crate::value::{AttrVal, PrimVal};
/// Handler that gets executed to apply the necessary parts of the eww state to /// Handler that gets executed to apply the necessary parts of the eww state to
/// a gtk widget. These are created and initialized in EwwState::resolve. /// a gtk widget. These are created and initialized in EwwState::resolve.
pub struct StateChangeHandler { pub struct StateChangeHandler {
func: Box<dyn Fn(HashMap<AttrName, PrimitiveValue>) -> Result<()> + 'static>, func: Box<dyn Fn(HashMap<AttrName, PrimVal>) -> Result<()> + 'static>,
unresolved_values: HashMap<AttrName, AttrValue>, unresolved_values: HashMap<AttrName, AttrVal>,
} }
impl StateChangeHandler { impl StateChangeHandler {
@ -21,7 +21,7 @@ impl StateChangeHandler {
/// Run the StateChangeHandler. /// Run the StateChangeHandler.
/// [`state`] should be the global [EwwState::state]. /// [`state`] should be the global [EwwState::state].
fn run_with_state(&self, state: &HashMap<VarName, PrimitiveValue>) { fn run_with_state(&self, state: &HashMap<VarName, PrimVal>) {
let resolved_attrs = self let resolved_attrs = self
.unresolved_values .unresolved_values
.clone() .clone()
@ -61,7 +61,7 @@ impl EwwWindowState {
#[derive(Default)] #[derive(Default)]
pub struct EwwState { pub struct EwwState {
windows: HashMap<WindowName, EwwWindowState>, windows: HashMap<WindowName, EwwWindowState>,
variables_state: HashMap<VarName, PrimitiveValue>, variables_state: HashMap<VarName, PrimVal>,
} }
impl std::fmt::Debug for EwwState { impl std::fmt::Debug for EwwState {
@ -71,11 +71,11 @@ impl std::fmt::Debug for EwwState {
} }
impl EwwState { impl EwwState {
pub fn from_default_vars(defaults: HashMap<VarName, PrimitiveValue>) -> Self { pub fn from_default_vars(defaults: HashMap<VarName, PrimVal>) -> Self {
EwwState { variables_state: defaults, ..EwwState::default() } EwwState { variables_state: defaults, ..EwwState::default() }
} }
pub fn get_variables(&self) -> &HashMap<VarName, PrimitiveValue> { pub fn get_variables(&self) -> &HashMap<VarName, PrimVal> {
&self.variables_state &self.variables_state
} }
@ -91,7 +91,7 @@ impl EwwState {
/// Update the value of a variable, running all registered /// Update the value of a variable, running all registered
/// [StateChangeHandler]s. /// [StateChangeHandler]s.
pub fn update_variable(&mut self, key: VarName, value: PrimitiveValue) { pub fn update_variable(&mut self, key: VarName, value: PrimVal) {
self.variables_state.insert(key.clone(), value); self.variables_state.insert(key.clone(), value);
// run all of the handlers // run all of the handlers
@ -103,27 +103,27 @@ impl EwwState {
} }
/// Look up a single variable in the eww state, returning an `Err` when the value is not found. /// Look up a single variable in the eww state, returning an `Err` when the value is not found.
pub fn lookup(&self, var_name: &VarName) -> Result<&PrimitiveValue> { pub fn lookup(&self, var_name: &VarName) -> Result<&PrimVal> {
self.variables_state.get(var_name).with_context(|| format!("Unknown variable '{}' referenced", var_name)) self.variables_state.get(var_name).with_context(|| format!("Unknown variable '{}' referenced", var_name))
} }
/// resolves a value if possible, using the current eww_state. /// resolves a value if possible, using the current eww_state.
pub fn resolve_once<'a>(&'a self, value: &'a AttrValue) -> Result<PrimitiveValue> { pub fn resolve_once<'a>(&'a self, value: &'a AttrVal) -> Result<PrimVal> {
value value
.iter() .iter()
.map(|element| match element { .map(|element| match element {
AttrValueElement::Primitive(primitive) => Ok(primitive.clone()), AttrValElement::Primitive(primitive) => Ok(primitive.clone()),
AttrValueElement::Expr(expr) => expr.clone().eval(&self.variables_state), AttrValElement::Expr(expr) => expr.clone().eval(&self.variables_state),
}) })
.collect() .collect()
} }
/// Resolve takes a function that applies a set of fully resolved attribute /// Resolve takes a function that applies a set of fully resolved attribute
/// values to it's gtk widget. /// values to it's gtk widget.
pub fn resolve<F: Fn(HashMap<AttrName, PrimitiveValue>) -> Result<()> + 'static + Clone>( pub fn resolve<F: Fn(HashMap<AttrName, PrimVal>) -> Result<()> + 'static + Clone>(
&mut self, &mut self,
window_name: &WindowName, window_name: &WindowName,
required_attributes: HashMap<AttrName, AttrValue>, required_attributes: HashMap<AttrName, AttrVal>,
set_value: F, set_value: F,
) { ) {
let handler = StateChangeHandler { func: Box::new(set_value), unresolved_values: required_attributes }; let handler = StateChangeHandler { func: Box::new(set_value), unresolved_values: required_attributes };

View file

@ -5,7 +5,7 @@ use structopt::StructOpt;
use crate::{ use crate::{
app, app,
config::{AnchorPoint, WindowName}, config::{AnchorPoint, WindowName},
value::{Coords, PrimitiveValue, VarName}, value::{Coords, PrimVal, VarName},
}; };
/// Struct that gets generated from `RawOpt`. /// Struct that gets generated from `RawOpt`.
@ -61,7 +61,7 @@ pub enum ActionWithServer {
Update { Update {
/// variable_name="new_value"-pairs that will be updated /// variable_name="new_value"-pairs that will be updated
#[structopt(parse(try_from_str = parse_var_update_arg))] #[structopt(parse(try_from_str = parse_var_update_arg))]
mappings: Vec<(VarName, PrimitiveValue)>, mappings: Vec<(VarName, PrimVal)>,
}, },
/// open a window /// open a window
@ -138,11 +138,11 @@ impl From<RawOpt> for Opt {
} }
} }
fn parse_var_update_arg(s: &str) -> Result<(VarName, PrimitiveValue)> { fn parse_var_update_arg(s: &str) -> Result<(VarName, PrimVal)> {
let (name, value) = s let (name, value) = s
.split_once('=') .split_once('=')
.with_context(|| format!("arguments must be in the shape `variable_name=\"new_value\"`, but got: {}", s))?; .with_context(|| format!("arguments must be in the shape `variable_name=\"new_value\"`, but got: {}", s))?;
Ok((name.into(), PrimitiveValue::from_string(value.to_owned()))) Ok((name.into(), PrimVal::from_string(value.to_owned())))
} }
impl ActionWithServer { impl ActionWithServer {

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::{ use crate::{
app, config, app, config,
value::{PrimitiveValue, VarName}, value::{PrimVal, VarName},
}; };
use anyhow::*; use anyhow::*;
use app::DaemonCommand; use app::DaemonCommand;
@ -197,7 +197,7 @@ impl TailVarHandler {
_ = handle.wait() => break, _ = handle.wait() => break,
_ = cancellation_token.cancelled() => break, _ = cancellation_token.cancelled() => break,
Ok(Some(line)) = stdout_lines.next_line() => { Ok(Some(line)) = stdout_lines.next_line() => {
let new_value = PrimitiveValue::from_string(line.to_owned()); let new_value = PrimVal::from_string(line.to_owned());
evt_send.send(DaemonCommand::UpdateVars(vec![(var.name.to_owned(), new_value)]))?; evt_send.send(DaemonCommand::UpdateVars(vec![(var.name.to_owned(), new_value)]))?;
} }
else => break, else => break,

View file

@ -9,45 +9,45 @@ use super::super::*;
/// This can be a primitive String that contains any amount of variable /// This can be a primitive String that contains any amount of variable
/// references, as would be generated by the string "foo {{var}} bar". /// references, as would be generated by the string "foo {{var}} bar".
#[derive(Serialize, Deserialize, Clone, PartialEq, derive_more::Into, derive_more::From, Default)] #[derive(Serialize, Deserialize, Clone, PartialEq, derive_more::Into, derive_more::From, Default)]
pub struct AttrValue(Vec<AttrValueElement>); pub struct AttrVal(Vec<AttrValElement>);
impl fmt::Display for AttrValue { impl fmt::Display for AttrVal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.iter().map(|x| format!("{}", x)).join("")) write!(f, "{}", self.iter().map(|x| format!("{}", x)).join(""))
} }
} }
impl fmt::Debug for AttrValue { impl fmt::Debug for AttrVal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AttrValue({:?})", self.0) write!(f, "AttrValue({:?})", self.0)
} }
} }
impl IntoIterator for AttrValue { impl IntoIterator for AttrVal {
type IntoIter = std::vec::IntoIter<Self::Item>; type IntoIter = std::vec::IntoIter<Self::Item>;
type Item = AttrValueElement; type Item = AttrValElement;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.0.into_iter() self.0.into_iter()
} }
} }
impl FromIterator<AttrValueElement> for AttrValue { impl FromIterator<AttrValElement> for AttrVal {
fn from_iter<T: IntoIterator<Item = AttrValueElement>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = AttrValElement>>(iter: T) -> Self {
AttrValue(iter.into_iter().collect()) AttrVal(iter.into_iter().collect())
} }
} }
impl AttrValue { impl AttrVal {
pub fn from_primitive<T: Into<PrimitiveValue>>(v: T) -> Self { pub fn from_primitive<T: Into<PrimVal>>(v: T) -> Self {
AttrValue(vec![AttrValueElement::Primitive(v.into())]) AttrVal(vec![AttrValElement::Primitive(v.into())])
} }
pub fn from_var_ref<T: Into<VarName>>(v: T) -> Self { pub fn from_var_ref<T: Into<VarName>>(v: T) -> Self {
AttrValue(vec![AttrValueElement::Expr(AttrValueExpr::VarRef(v.into()))]) AttrVal(vec![AttrValElement::Expr(AttrValExpr::VarRef(v.into()))])
} }
pub fn iter(&self) -> std::slice::Iter<AttrValueElement> { pub fn iter(&self) -> std::slice::Iter<AttrValElement> {
self.0.iter() self.0.iter()
} }
@ -58,13 +58,13 @@ impl AttrValue {
/// resolve partially. /// resolve partially.
/// If a var-ref links to another var-ref, that other var-ref is used. /// If a var-ref links to another var-ref, that other var-ref is used.
/// If a referenced variable is not found in the given hashmap, returns the var-ref unchanged. /// If a referenced variable is not found in the given hashmap, returns the var-ref unchanged.
pub fn resolve_one_level(self, variables: &HashMap<VarName, AttrValue>) -> AttrValue { pub fn resolve_one_level(self, variables: &HashMap<VarName, AttrVal>) -> AttrVal {
self.into_iter() self.into_iter()
.map(|entry| match entry { .map(|entry| match entry {
AttrValueElement::Expr(expr) => AttrValueElement::Expr(expr.map_terminals_into(|child_expr| match child_expr { AttrValElement::Expr(expr) => AttrValElement::Expr(expr.map_terminals_into(|child_expr| match child_expr {
AttrValueExpr::VarRef(var_name) => match variables.get(&var_name) { AttrValExpr::VarRef(var_name) => match variables.get(&var_name) {
Some(value) => AttrValueExpr::Literal(value.clone()), Some(value) => AttrValExpr::Literal(value.clone()),
None => AttrValueExpr::VarRef(var_name), None => AttrValExpr::VarRef(var_name),
}, },
other => other, other => other,
})), })),
@ -77,17 +77,17 @@ impl AttrValue {
/// resolve fully. /// resolve fully.
/// As the variables here have to be primitive values, /// As the variables here have to be primitive values,
/// this enforces that var-refs are not linking to other variables. /// this enforces that var-refs are not linking to other variables.
pub fn resolve_fully(self, variables: &HashMap<VarName, PrimitiveValue>) -> Result<PrimitiveValue> { pub fn resolve_fully(self, variables: &HashMap<VarName, PrimVal>) -> Result<PrimVal> {
self.into_iter() self.into_iter()
.map(|element| match element { .map(|element| match element {
AttrValueElement::Primitive(x) => Ok(x), AttrValElement::Primitive(x) => Ok(x),
AttrValueElement::Expr(expr) => expr.eval(variables), AttrValElement::Expr(expr) => expr.eval(variables),
}) })
.collect() .collect()
} }
// TODO this could be a fancy Iterator implementation, ig // TODO this could be a fancy Iterator implementation, ig
pub fn parse_string(s: &str) -> AttrValue { pub fn parse_string(s: &str) -> AttrVal {
let mut elements = Vec::new(); let mut elements = Vec::new();
let mut cur_word = "".to_owned(); let mut cur_word = "".to_owned();
@ -98,7 +98,7 @@ impl AttrValue {
if c == '}' { if c == '}' {
curly_count -= 1; curly_count -= 1;
if curly_count == 0 { if curly_count == 0 {
elements.push(AttrValueElement::Expr(AttrValueExpr::parse(varref).unwrap())); elements.push(AttrValElement::Expr(AttrValExpr::parse(varref).unwrap()));
cur_varref = None cur_varref = None
} }
} else { } else {
@ -109,7 +109,7 @@ impl AttrValue {
curly_count += 1; curly_count += 1;
if curly_count == 2 { if curly_count == 2 {
if !cur_word.is_empty() { if !cur_word.is_empty() {
elements.push(AttrValueElement::primitive(std::mem::take(&mut cur_word))); elements.push(AttrValElement::primitive(std::mem::take(&mut cur_word)));
} }
cur_varref = Some(String::new()) cur_varref = Some(String::new())
} }
@ -122,52 +122,52 @@ impl AttrValue {
} }
} }
if let Some(unfinished_varref) = cur_varref.take() { if let Some(unfinished_varref) = cur_varref.take() {
elements.push(AttrValueElement::primitive(unfinished_varref)); elements.push(AttrValElement::primitive(unfinished_varref));
} else if !cur_word.is_empty() { } else if !cur_word.is_empty() {
elements.push(AttrValueElement::primitive(cur_word)); elements.push(AttrValElement::primitive(cur_word));
} }
AttrValue(elements) AttrVal(elements)
} }
} }
#[derive(Clone, PartialEq, Serialize, Deserialize)] #[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum AttrValueElement { pub enum AttrValElement {
Primitive(PrimitiveValue), Primitive(PrimVal),
Expr(AttrValueExpr), Expr(AttrValExpr),
} }
impl fmt::Display for AttrValueElement { impl fmt::Display for AttrValElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
AttrValueElement::Primitive(x) => write!(f, "{}", x), AttrValElement::Primitive(x) => write!(f, "{}", x),
AttrValueElement::Expr(x) => write!(f, "{{{{{}}}}}", x), AttrValElement::Expr(x) => write!(f, "{{{{{}}}}}", x),
} }
} }
} }
impl fmt::Debug for AttrValueElement { impl fmt::Debug for AttrValElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
AttrValueElement::Primitive(x) => write!(f, "Primitive({:?})", x), AttrValElement::Primitive(x) => write!(f, "Primitive({:?})", x),
AttrValueElement::Expr(x) => write!(f, "Expr({:?})", x), AttrValElement::Expr(x) => write!(f, "Expr({:?})", x),
} }
} }
} }
impl AttrValueElement { impl AttrValElement {
pub fn primitive(s: String) -> Self { pub fn primitive(s: String) -> Self {
AttrValueElement::Primitive(PrimitiveValue::from_string(s)) AttrValElement::Primitive(PrimVal::from_string(s))
} }
pub fn as_expr(&self) -> Option<&AttrValueExpr> { pub fn as_expr(&self) -> Option<&AttrValExpr> {
match self { match self {
AttrValueElement::Expr(x) => Some(&x), AttrValElement::Expr(x) => Some(&x),
_ => None, _ => None,
} }
} }
pub fn as_primitive(&self) -> Option<&PrimitiveValue> { pub fn as_primitive(&self) -> Option<&PrimVal> {
match self { match self {
AttrValueElement::Primitive(x) => Some(&x), AttrValElement::Primitive(x) => Some(&x),
_ => None, _ => None,
} }
} }
@ -180,30 +180,30 @@ mod test {
#[test] #[test]
fn test_parse_string_or_var_ref_list() { fn test_parse_string_or_var_ref_list() {
let input = "{{foo}}{{bar}}b{}azb{a}z{{bat}}{}quok{{test}}"; let input = "{{foo}}{{bar}}b{}azb{a}z{{bat}}{}quok{{test}}";
let output = AttrValue::parse_string(input); let output = AttrVal::parse_string(input);
assert_eq!( assert_eq!(
output, output,
AttrValue(vec![ AttrVal(vec![
AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("foo".to_owned()))), AttrValElement::Expr(AttrValExpr::VarRef(VarName("foo".to_owned()))),
AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("bar".to_owned()))), AttrValElement::Expr(AttrValExpr::VarRef(VarName("bar".to_owned()))),
AttrValueElement::primitive("b{}azb{a}z".to_owned()), AttrValElement::primitive("b{}azb{a}z".to_owned()),
AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("bat".to_owned()))), AttrValElement::Expr(AttrValExpr::VarRef(VarName("bat".to_owned()))),
AttrValueElement::primitive("{}quok".to_owned()), AttrValElement::primitive("{}quok".to_owned()),
AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("test".to_owned()))), AttrValElement::Expr(AttrValExpr::VarRef(VarName("test".to_owned()))),
]), ]),
) )
} }
#[test] #[test]
fn test_parse_string_with_var_refs_attr_value() { fn test_parse_string_with_var_refs_attr_value() {
assert_eq!( assert_eq!(
AttrValue( AttrVal(
vec![ vec![
AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("var".to_owned()))), AttrValElement::Expr(AttrValExpr::VarRef(VarName("var".to_owned()))),
AttrValueElement::primitive("something".to_owned()) AttrValElement::primitive("something".to_owned())
] ]
.into() .into()
), ),
AttrValue::parse_string("{{var}}something") AttrVal::parse_string("{{var}}something")
); );
} }
} }

View file

@ -52,24 +52,24 @@ impl std::fmt::Display for UnaryOp {
} }
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
pub enum AttrValueExpr { pub enum AttrValExpr {
Literal(AttrValue), Literal(AttrVal),
VarRef(VarName), VarRef(VarName),
BinOp(Box<AttrValueExpr>, BinOp, Box<AttrValueExpr>), BinOp(Box<AttrValExpr>, BinOp, Box<AttrValExpr>),
UnaryOp(UnaryOp, Box<AttrValueExpr>), UnaryOp(UnaryOp, Box<AttrValExpr>),
IfElse(Box<AttrValueExpr>, Box<AttrValueExpr>, Box<AttrValueExpr>), IfElse(Box<AttrValExpr>, Box<AttrValExpr>, Box<AttrValExpr>),
JsonAccessIndex(Box<AttrValueExpr>, Box<AttrValueExpr>), JsonAccess(Box<AttrValExpr>, Box<AttrValExpr>),
} }
impl std::fmt::Display for AttrValueExpr { impl std::fmt::Display for AttrValExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
AttrValueExpr::VarRef(x) => write!(f, "{}", x), AttrValExpr::VarRef(x) => write!(f, "{}", x),
AttrValueExpr::Literal(x) => write!(f, "\"{}\"", x), AttrValExpr::Literal(x) => write!(f, "\"{}\"", x),
AttrValueExpr::BinOp(l, op, r) => write!(f, "({} {} {})", l, op, r), AttrValExpr::BinOp(l, op, r) => write!(f, "({} {} {})", l, op, r),
AttrValueExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x), AttrValExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x),
AttrValueExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c), AttrValExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c),
AttrValueExpr::JsonAccessIndex(value, index) => write!(f, "{}[{}]", value, index), AttrValExpr::JsonAccess(value, index) => write!(f, "{}[{}]", value, index),
} }
} }
} }
@ -79,9 +79,9 @@ impl std::fmt::Display for AttrValueExpr {
// write!(f, "{:?}", self) // write!(f, "{:?}", self)
//} //}
impl AttrValueExpr { impl AttrValExpr {
pub fn map_terminals_into(self, f: impl Fn(Self) -> Self) -> Self { pub fn map_terminals_into(self, f: impl Fn(Self) -> Self) -> Self {
use AttrValueExpr::*; use AttrValExpr::*;
match self { match self {
BinOp(box a, op, box b) => BinOp(box f(a), op, box f(b)), BinOp(box a, op, box b) => BinOp(box f(a), op, box f(b)),
IfElse(box a, box b, box c) => IfElse(box f(a), box f(b), box f(c)), IfElse(box a, box b, box c) => IfElse(box f(a), box f(b), box f(c)),
@ -90,12 +90,12 @@ impl AttrValueExpr {
} }
/// resolve variable references in the expression. Fails if a variable cannot be resolved. /// resolve variable references in the expression. Fails if a variable cannot be resolved.
pub fn resolve_refs(self, variables: &HashMap<VarName, PrimitiveValue>) -> Result<Self> { pub fn resolve_refs(self, variables: &HashMap<VarName, PrimVal>) -> Result<Self> {
use AttrValueExpr::*; use AttrValExpr::*;
match self { match self {
// Literal(x) => Ok(Literal(AttrValue::from_primitive(x.resolve_fully(&variables)?))), // Literal(x) => Ok(Literal(AttrValue::from_primitive(x.resolve_fully(&variables)?))),
Literal(x) => Ok(Literal(x)), Literal(x) => Ok(Literal(x)),
VarRef(ref name) => Ok(Literal(AttrValue::from_primitive( VarRef(ref name) => Ok(Literal(AttrVal::from_primitive(
variables.get(name).with_context(|| format!("Unknown variable {} referenced in {:?}", &name, &self))?.clone(), variables.get(name).with_context(|| format!("Unknown variable {} referenced in {:?}", &name, &self))?.clone(),
))), ))),
BinOp(box a, op, box b) => Ok(BinOp(box a.resolve_refs(variables)?, op, box b.resolve_refs(variables)?)), BinOp(box a, op, box b) => Ok(BinOp(box a.resolve_refs(variables)?, op, box b.resolve_refs(variables)?)),
@ -103,12 +103,12 @@ impl AttrValueExpr {
IfElse(box a, box b, box c) => { IfElse(box a, box b, box c) => {
Ok(IfElse(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?, box c.resolve_refs(variables)?)) Ok(IfElse(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?, box c.resolve_refs(variables)?))
} }
JsonAccessIndex(box a, box b) => Ok(JsonAccessIndex(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?)), JsonAccess(box a, box b) => Ok(JsonAccess(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?)),
} }
} }
pub fn var_refs(&self) -> Vec<&VarName> { pub fn var_refs(&self) -> Vec<&VarName> {
use AttrValueExpr::*; use AttrValExpr::*;
match self { match self {
Literal(s) => s.var_refs().collect(), Literal(s) => s.var_refs().collect(),
VarRef(name) => vec![name], VarRef(name) => vec![name],
@ -124,7 +124,7 @@ impl AttrValueExpr {
refs.append(&mut c.var_refs()); refs.append(&mut c.var_refs());
refs refs
} }
JsonAccessIndex(box a, box b) => { JsonAccess(box a, box b) => {
let mut refs = a.var_refs(); let mut refs = a.var_refs();
refs.append(&mut b.var_refs()); refs.append(&mut b.var_refs());
refs refs
@ -132,60 +132,60 @@ impl AttrValueExpr {
} }
} }
pub fn eval(self, values: &HashMap<VarName, PrimitiveValue>) -> Result<PrimitiveValue> { pub fn eval(self, values: &HashMap<VarName, PrimVal>) -> Result<PrimVal> {
match self { match self {
AttrValueExpr::Literal(x) => x.resolve_fully(&values), AttrValExpr::Literal(x) => x.resolve_fully(&values),
AttrValueExpr::VarRef(ref name) => values AttrValExpr::VarRef(ref name) => values
.get(name) .get(name)
.cloned() .cloned()
.context(format!("Got unresolved variable {} while trying to evaluate expression {:?}", &name, &self)), .context(format!("Got unresolved variable {} while trying to evaluate expression {:?}", &name, &self)),
AttrValueExpr::BinOp(a, op, b) => { AttrValExpr::BinOp(a, op, b) => {
let a = a.eval(values)?; let a = a.eval(values)?;
let b = b.eval(values)?; let b = b.eval(values)?;
Ok(match op { Ok(match op {
BinOp::Equals => PrimitiveValue::from(a == b), BinOp::Equals => PrimVal::from(a == b),
BinOp::NotEquals => PrimitiveValue::from(a != b), BinOp::NotEquals => PrimVal::from(a != b),
BinOp::And => PrimitiveValue::from(a.as_bool()? && b.as_bool()?), BinOp::And => PrimVal::from(a.as_bool()? && b.as_bool()?),
BinOp::Or => PrimitiveValue::from(a.as_bool()? || b.as_bool()?), BinOp::Or => PrimVal::from(a.as_bool()? || b.as_bool()?),
BinOp::Plus => PrimitiveValue::from(a.as_f64()? + b.as_f64()?), BinOp::Plus => PrimVal::from(a.as_f64()? + b.as_f64()?),
BinOp::Minus => PrimitiveValue::from(a.as_f64()? - b.as_f64()?), BinOp::Minus => PrimVal::from(a.as_f64()? - b.as_f64()?),
BinOp::Times => PrimitiveValue::from(a.as_f64()? * b.as_f64()?), BinOp::Times => PrimVal::from(a.as_f64()? * b.as_f64()?),
BinOp::Div => PrimitiveValue::from(a.as_f64()? / b.as_f64()?), BinOp::Div => PrimVal::from(a.as_f64()? / b.as_f64()?),
BinOp::Mod => PrimitiveValue::from(a.as_f64()? % b.as_f64()?), BinOp::Mod => PrimVal::from(a.as_f64()? % b.as_f64()?),
BinOp::GT => PrimitiveValue::from(a.as_f64()? > b.as_f64()?), BinOp::GT => PrimVal::from(a.as_f64()? > b.as_f64()?),
BinOp::LT => PrimitiveValue::from(a.as_f64()? < b.as_f64()?), BinOp::LT => PrimVal::from(a.as_f64()? < b.as_f64()?),
BinOp::Elvis => PrimitiveValue::from(if a.0.is_empty() { b } else { a }), BinOp::Elvis => PrimVal::from(if a.0.is_empty() { b } else { a }),
}) })
} }
AttrValueExpr::UnaryOp(op, a) => { AttrValExpr::UnaryOp(op, a) => {
let a = a.eval(values)?; let a = a.eval(values)?;
Ok(match op { Ok(match op {
UnaryOp::Not => PrimitiveValue::from(!a.as_bool()?), UnaryOp::Not => PrimVal::from(!a.as_bool()?),
}) })
} }
AttrValueExpr::IfElse(cond, yes, no) => { AttrValExpr::IfElse(cond, yes, no) => {
if cond.eval(values)?.as_bool()? { if cond.eval(values)?.as_bool()? {
yes.eval(values) yes.eval(values)
} else { } else {
no.eval(values) no.eval(values)
} }
} }
AttrValueExpr::JsonAccessIndex(val, index) => { AttrValExpr::JsonAccess(val, index) => {
let val = val.eval(values)?; let val = val.eval(values)?;
let index = index.eval(values)?; let index = index.eval(values)?;
match val.as_json_value()? { match val.as_json_value()? {
serde_json::Value::Array(val) => { serde_json::Value::Array(val) => {
let index = index.as_i32()?; let index = index.as_i32()?;
let indexed_value = val.get(index as usize).unwrap_or(&serde_json::Value::Null); let indexed_value = val.get(index as usize).unwrap_or(&serde_json::Value::Null);
Ok(PrimitiveValue::from(indexed_value)) Ok(PrimVal::from(indexed_value))
} }
serde_json::Value::Object(val) => { serde_json::Value::Object(val) => {
let indexed_value = val let indexed_value = val
.get(&index.as_string()?) .get(&index.as_string()?)
.or_else(|| val.get(&format!("{}", index.as_i32().ok()?))) .or_else(|| val.get(&index.as_i32().ok()?.to_string()))
.unwrap_or(&serde_json::Value::Null); .unwrap_or(&serde_json::Value::Null);
Ok(PrimitiveValue::from(indexed_value)) Ok(PrimVal::from(indexed_value))
} }
_ => bail!("Unable to index into value {}", val), _ => bail!("Unable to index into value {}", val),
} }

View file

@ -53,34 +53,34 @@ fn parse_unary_op(i: &str) -> IResult<&str, UnaryOp, VerboseError<&str>> {
// actual tree // // actual tree //
///////////////// /////////////////
fn parse_factor(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> { fn parse_factor(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, unary_op) = opt(parse_unary_op)(i)?; let (i, unary_op) = opt(parse_unary_op)(i)?;
let (i, factor) = alt(( let (i, factor) = alt((
context("expression", ws(delimited(tag("("), parse_expr, tag(")")))), context("expression", ws(delimited(tag("("), parse_expr, tag(")")))),
context("if-expression", ws(parse_ifelse)), context("if-expression", ws(parse_ifelse)),
context("literal", map(ws(parse_literal), |x| AttrValueExpr::Literal(AttrValue::parse_string(x)))), context("literal", map(ws(parse_literal), |x| AttrValExpr::Literal(AttrVal::parse_string(x)))),
context("identifier", map(ws(parse_identifier), |x| AttrValueExpr::VarRef(VarName(x.to_string())))), context("identifier", map(ws(parse_identifier), |x| AttrValExpr::VarRef(VarName(x.to_string())))),
))(i)?; ))(i)?;
Ok(( Ok((
i, i,
match unary_op { match unary_op {
Some(op) => AttrValueExpr::UnaryOp(op, box factor), Some(op) => AttrValExpr::UnaryOp(op, box factor),
None => factor, None => factor,
}, },
)) ))
} }
fn parse_object_index(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> { fn parse_object_index(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_factor(i)?; let (i, initial) = parse_factor(i)?;
let (i, remainder) = many0(alt(( let (i, remainder) = many0(alt((
delimited(tag("["), ws(parse_expr), tag("]")), delimited(tag("["), ws(parse_expr), tag("]")),
map(preceded(tag("."), parse_identifier), |x| AttrValueExpr::Literal(AttrValue::from_primitive(x))), map(preceded(tag("."), parse_identifier), |x| AttrValExpr::Literal(AttrVal::from_primitive(x))),
)))(i)?; )))(i)?;
let indexes = remainder.into_iter().fold(initial, |acc, index| AttrValueExpr::JsonAccessIndex(box acc, box index)); let indexes = remainder.into_iter().fold(initial, |acc, index| AttrValExpr::JsonAccess(box acc, box index));
Ok((i, indexes)) Ok((i, indexes))
} }
fn parse_term3(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> { fn parse_term3(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_object_index(i)?; let (i, initial) = parse_object_index(i)?;
let (i, remainder) = many0(alt(( let (i, remainder) = many0(alt((
map(preceded(tag("*"), parse_object_index), |x| (BinOp::Times, x)), map(preceded(tag("*"), parse_object_index), |x| (BinOp::Times, x)),
@ -88,23 +88,23 @@ fn parse_term3(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
map(preceded(tag("%"), parse_object_index), |x| (BinOp::Mod, x)), map(preceded(tag("%"), parse_object_index), |x| (BinOp::Mod, x)),
)))(i)?; )))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr)); let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs)) Ok((i, exprs))
} }
fn parse_term2(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> { fn parse_term2(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_term3(i)?; let (i, initial) = parse_term3(i)?;
let (i, remainder) = many0(alt(( let (i, remainder) = many0(alt((
map(preceded(tag("+"), parse_term3), |x| (BinOp::Plus, x)), map(preceded(tag("+"), parse_term3), |x| (BinOp::Plus, x)),
map(preceded(tag("-"), parse_term3), |x| (BinOp::Minus, x)), map(preceded(tag("-"), parse_term3), |x| (BinOp::Minus, x)),
)))(i)?; )))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr)); let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs)) Ok((i, exprs))
} }
fn parse_term1(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> { fn parse_term1(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_term2(i)?; let (i, initial) = parse_term2(i)?;
let (i, remainder) = many0(alt(( let (i, remainder) = many0(alt((
map(preceded(tag("=="), parse_term2), |x| (BinOp::Equals, x)), map(preceded(tag("=="), parse_term2), |x| (BinOp::Equals, x)),
@ -113,11 +113,11 @@ fn parse_term1(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
map(preceded(tag("<"), parse_term2), |x| (BinOp::LT, x)), map(preceded(tag("<"), parse_term2), |x| (BinOp::LT, x)),
)))(i)?; )))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr)); let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs)) Ok((i, exprs))
} }
pub fn parse_expr(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> { pub fn parse_expr(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_term1(i)?; let (i, initial) = parse_term1(i)?;
let (i, remainder) = many0(alt(( let (i, remainder) = many0(alt((
map(preceded(tag("&&"), parse_term1), |x| (BinOp::And, x)), map(preceded(tag("&&"), parse_term1), |x| (BinOp::And, x)),
@ -125,22 +125,22 @@ pub fn parse_expr(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
map(preceded(tag("?:"), parse_term1), |x| (BinOp::Elvis, x)), map(preceded(tag("?:"), parse_term1), |x| (BinOp::Elvis, x)),
)))(i)?; )))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr)); let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs)) Ok((i, exprs))
} }
fn parse_ifelse(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> { fn parse_ifelse(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, _) = tag("if")(i)?; let (i, _) = tag("if")(i)?;
let (i, a) = context("condition", ws(parse_expr))(i)?; let (i, a) = context("condition", ws(parse_expr))(i)?;
let (i, _) = tag("then")(i)?; let (i, _) = tag("then")(i)?;
let (i, b) = context("true-case", ws(parse_expr))(i)?; let (i, b) = context("true-case", ws(parse_expr))(i)?;
let (i, _) = tag("else")(i)?; let (i, _) = tag("else")(i)?;
let (i, c) = context("false-case", ws(parse_expr))(i)?; let (i, c) = context("false-case", ws(parse_expr))(i)?;
Ok((i, AttrValueExpr::IfElse(box a, box b, box c))) Ok((i, AttrValExpr::IfElse(box a, box b, box c)))
} }
pub fn parse<'a>(i: &'a str) -> IResult<&'a str, AttrValueExpr, VerboseError<&'a str>> { pub fn parse<'a>(i: &'a str) -> IResult<&'a str, AttrValExpr, VerboseError<&'a str>> {
complete(parse_expr)(i) complete(parse_expr)(i)
} }
@ -150,64 +150,64 @@ mod test {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[test] #[test]
fn test_parser() { fn test_parser() {
assert_eq!(AttrValueExpr::Literal(AttrValue::from_primitive("12")), AttrValueExpr::parse("12").unwrap()); assert_eq!(AttrValExpr::Literal(AttrVal::from_primitive("12")), AttrValExpr::parse("12").unwrap());
assert_eq!( assert_eq!(
AttrValueExpr::UnaryOp(UnaryOp::Not, box AttrValueExpr::Literal(AttrValue::from_primitive("false"))), AttrValExpr::UnaryOp(UnaryOp::Not, box AttrValExpr::Literal(AttrVal::from_primitive("false"))),
AttrValueExpr::parse("!false").unwrap() AttrValExpr::parse("!false").unwrap()
); );
assert_eq!( assert_eq!(
AttrValueExpr::BinOp( AttrValExpr::BinOp(
box AttrValueExpr::Literal(AttrValue::from_primitive("12")), box AttrValExpr::Literal(AttrVal::from_primitive("12")),
BinOp::Plus, BinOp::Plus,
box AttrValueExpr::Literal(AttrValue::from_primitive("2")) box AttrValExpr::Literal(AttrVal::from_primitive("2"))
), ),
AttrValueExpr::parse("12 + 2").unwrap() AttrValExpr::parse("12 + 2").unwrap()
); );
assert_eq!( assert_eq!(
AttrValueExpr::UnaryOp( AttrValExpr::UnaryOp(
UnaryOp::Not, UnaryOp::Not,
box AttrValueExpr::BinOp( box AttrValExpr::BinOp(
box AttrValueExpr::Literal(AttrValue::from_primitive("1")), box AttrValExpr::Literal(AttrVal::from_primitive("1")),
BinOp::Equals, BinOp::Equals,
box AttrValueExpr::Literal(AttrValue::from_primitive("2")) box AttrValExpr::Literal(AttrVal::from_primitive("2"))
) )
), ),
AttrValueExpr::parse("!(1 == 2)").unwrap() AttrValExpr::parse("!(1 == 2)").unwrap()
); );
assert_eq!( assert_eq!(
AttrValueExpr::IfElse( AttrValExpr::IfElse(
box AttrValueExpr::VarRef(VarName("a".to_string())), box AttrValExpr::VarRef(VarName("a".to_string())),
box AttrValueExpr::VarRef(VarName("b".to_string())), box AttrValExpr::VarRef(VarName("b".to_string())),
box AttrValueExpr::VarRef(VarName("c".to_string())), box AttrValExpr::VarRef(VarName("c".to_string())),
), ),
AttrValueExpr::parse("if a then b else c").unwrap() AttrValExpr::parse("if a then b else c").unwrap()
); );
assert_eq!( assert_eq!(
AttrValueExpr::JsonAccessIndex( AttrValExpr::JsonAccess(
box AttrValueExpr::VarRef(VarName("array".to_string())), box AttrValExpr::VarRef(VarName("array".to_string())),
box AttrValueExpr::BinOp( box AttrValExpr::BinOp(
box AttrValueExpr::Literal(AttrValue::from_primitive("1")), box AttrValExpr::Literal(AttrVal::from_primitive("1")),
BinOp::Plus, BinOp::Plus,
box AttrValueExpr::Literal(AttrValue::from_primitive("2")) box AttrValExpr::Literal(AttrVal::from_primitive("2"))
) )
), ),
AttrValueExpr::parse(r#"(array)[1+2]"#).unwrap() AttrValExpr::parse(r#"(array)[1+2]"#).unwrap()
); );
assert_eq!( assert_eq!(
AttrValueExpr::JsonAccessIndex( AttrValExpr::JsonAccess(
box AttrValueExpr::JsonAccessIndex( box AttrValExpr::JsonAccess(
box AttrValueExpr::VarRef(VarName("object".to_string())), box AttrValExpr::VarRef(VarName("object".to_string())),
box AttrValueExpr::Literal(AttrValue::from_primitive("field".to_string())), box AttrValExpr::Literal(AttrVal::from_primitive("field".to_string())),
), ),
box AttrValueExpr::Literal(AttrValue::from_primitive("field2".to_string())), box AttrValExpr::Literal(AttrVal::from_primitive("field2".to_string())),
), ),
AttrValueExpr::parse(r#"object.field.field2"#).unwrap() AttrValExpr::parse(r#"object.field.field2"#).unwrap()
); );
} }
#[test] #[test]
fn test_complex() { fn test_complex() {
let parsed = let parsed =
AttrValueExpr::parse(r#"if hi > 12 + 2 * 2 && 12 == 15 then "foo" else if !true then 'hi' else "{{bruh}}""#).unwrap(); AttrValExpr::parse(r#"if hi > 12 + 2 * 2 && 12 == 15 then "foo" else if !true then 'hi' else "{{bruh}}""#).unwrap();
assert_eq!( assert_eq!(
r#"(if ((hi > ("12" + ("2" * "2"))) && ("12" == "15")) then "foo" else (if !"true" then "hi" else "{{bruh}}"))"#, r#"(if ((hi > ("12" + ("2" * "2"))) && ("12" == "15")) then "foo" else (if !"true" then "hi" else "{{bruh}}"))"#,

View file

@ -6,21 +6,21 @@ use std::{convert::TryFrom, fmt, iter::FromIterator};
use crate::impl_try_from; use crate::impl_try_from;
#[derive(Clone, Deserialize, Serialize, derive_more::From, Default)] #[derive(Clone, Deserialize, Serialize, derive_more::From, Default)]
pub struct PrimitiveValue(pub String); pub struct PrimVal(pub String);
impl fmt::Display for PrimitiveValue { impl fmt::Display for PrimVal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0) write!(f, "{}", self.0)
} }
} }
impl fmt::Debug for PrimitiveValue { impl fmt::Debug for PrimVal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", self.0) write!(f, "\"{}\"", self.0)
} }
} }
/// Manually implement equality, to allow for values in different formats (i.e. "1" and "1.0") to still be considered as equal. /// Manually implement equality, to allow for values in different formats (i.e. "1" and "1.0") to still be considered as equal.
impl std::cmp::PartialEq<Self> for PrimitiveValue { impl std::cmp::PartialEq<Self> for PrimVal {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if let (Ok(a), Ok(b)) = (self.as_f64(), other.as_f64()) { if let (Ok(a), Ok(b)) = (self.as_f64(), other.as_f64()) {
a == b a == b
@ -30,56 +30,56 @@ impl std::cmp::PartialEq<Self> for PrimitiveValue {
} }
} }
impl FromIterator<PrimitiveValue> for PrimitiveValue { impl FromIterator<PrimVal> for PrimVal {
fn from_iter<T: IntoIterator<Item = PrimitiveValue>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = PrimVal>>(iter: T) -> Self {
PrimitiveValue(iter.into_iter().join("")) PrimVal(iter.into_iter().join(""))
} }
} }
impl std::str::FromStr for PrimitiveValue { impl std::str::FromStr for PrimVal {
type Err = anyhow::Error; type Err = anyhow::Error;
/// parses the value, trying to turn it into a number and a boolean first, /// parses the value, trying to turn it into a number and a boolean first,
/// before deciding that it is a string. /// before deciding that it is a string.
fn from_str(s: &str) -> Result<Self> { fn from_str(s: &str) -> Result<Self> {
Ok(PrimitiveValue::from_string(s.to_string())) Ok(PrimVal::from_string(s.to_string()))
} }
} }
impl_try_from!(PrimitiveValue { impl_try_from!(PrimVal {
for String => |x| x.as_string(); for String => |x| x.as_string();
for f64 => |x| x.as_f64(); for f64 => |x| x.as_f64();
for bool => |x| x.as_bool(); for bool => |x| x.as_bool();
for Vec<String> => |x| x.as_vec(); for Vec<String> => |x| x.as_vec();
}); });
impl From<bool> for PrimitiveValue { impl From<bool> for PrimVal {
fn from(x: bool) -> Self { fn from(x: bool) -> Self {
PrimitiveValue(x.to_string()) PrimVal(x.to_string())
} }
} }
impl From<i32> for PrimitiveValue { impl From<i32> for PrimVal {
fn from(s: i32) -> Self { fn from(s: i32) -> Self {
PrimitiveValue(s.to_string()) PrimVal(s.to_string())
} }
} }
impl From<f64> for PrimitiveValue { impl From<f64> for PrimVal {
fn from(s: f64) -> Self { fn from(s: f64) -> Self {
PrimitiveValue(s.to_string()) PrimVal(s.to_string())
} }
} }
impl From<&str> for PrimitiveValue { impl From<&str> for PrimVal {
fn from(s: &str) -> Self { fn from(s: &str) -> Self {
PrimitiveValue(s.to_string()) PrimVal(s.to_string())
} }
} }
impl From<&serde_json::Value> for PrimitiveValue { impl From<&serde_json::Value> for PrimVal {
fn from(v: &serde_json::Value) -> Self { fn from(v: &serde_json::Value) -> Self {
PrimitiveValue( PrimVal(
v.as_str() v.as_str()
.map(|x| x.to_string()) .map(|x| x.to_string())
.or_else(|| serde_json::to_string(v).ok()) .or_else(|| serde_json::to_string(v).ok())
@ -88,9 +88,9 @@ impl From<&serde_json::Value> for PrimitiveValue {
} }
} }
impl PrimitiveValue { impl PrimVal {
pub fn from_string(s: String) -> Self { pub fn from_string(s: String) -> Self {
PrimitiveValue(s) PrimVal(s)
} }
pub fn into_inner(self) -> String { pub fn into_inner(self) -> String {

View file

@ -154,7 +154,7 @@ macro_rules! resolve_block {
}; };
(@get_value $args:ident, $name:expr, = $default:expr) => { (@get_value $args:ident, $name:expr, = $default:expr) => {
$args.widget.get_attr($name).cloned().unwrap_or(AttrValue::from_primitive($default)) $args.widget.get_attr($name).cloned().unwrap_or(AttrVal::from_primitive($default))
}; };
(@get_value $args:ident, $name:expr,) => { (@get_value $args:ident, $name:expr,) => {

View file

@ -1,5 +1,5 @@
use super::{run_command, BuilderArgs}; use super::{run_command, BuilderArgs};
use crate::{config, eww_state, resolve_block, value::AttrValue, widgets::widget_node}; use crate::{config, eww_state, resolve_block, value::AttrVal, widgets::widget_node};
use anyhow::*; use anyhow::*;
use gtk::{prelude::*, ImageExt}; use gtk::{prelude::*, ImageExt};
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};

View file

@ -5,7 +5,7 @@ use crate::{
WindowName, WindowName,
}, },
eww_state::EwwState, eww_state::EwwState,
value::{AttrName, AttrValue, VarName}, value::{AttrName, AttrVal, VarName},
}; };
use anyhow::*; use anyhow::*;
use dyn_clone; use dyn_clone;
@ -62,11 +62,11 @@ pub struct Generic {
pub name: String, pub name: String,
pub text_pos: Option<TextPos>, pub text_pos: Option<TextPos>,
pub children: Vec<Box<dyn WidgetNode>>, pub children: Vec<Box<dyn WidgetNode>>,
pub attrs: HashMap<AttrName, AttrValue>, pub attrs: HashMap<AttrName, AttrVal>,
} }
impl Generic { impl Generic {
pub fn get_attr(&self, key: &str) -> Result<&AttrValue> { pub fn get_attr(&self, key: &str) -> Result<&AttrVal> {
self.attrs.get(key).context(format!("attribute '{}' missing from use of '{}'", key, &self.name)) self.attrs.get(key).context(format!("attribute '{}' missing from use of '{}'", key, &self.name))
} }
@ -98,7 +98,7 @@ impl WidgetNode for Generic {
pub fn generate_generic_widget_node( pub fn generate_generic_widget_node(
defs: &HashMap<String, WidgetDefinition>, defs: &HashMap<String, WidgetDefinition>,
local_env: &HashMap<VarName, AttrValue>, local_env: &HashMap<VarName, AttrVal>,
w: WidgetUse, w: WidgetUse,
) -> Result<Box<dyn WidgetNode>> { ) -> Result<Box<dyn WidgetNode>> {
if let Some(def) = defs.get(&w.name) { if let Some(def) = defs.get(&w.name) {