From 0f9847c518899045fc98be9038c19d139217b261 Mon Sep 17 00:00:00 2001 From: MartinJM Date: Tue, 19 Apr 2022 13:29:17 +0200 Subject: [PATCH] Add several functions for simple expressions (#407) Co-authored-by: elkowar <5300871+elkowar@users.noreply.github.com> Co-authored-by: MartinJM <> --- CHANGELOG.md | 1 + crates/simplexpr/src/dynval.rs | 18 ++++++++++++- crates/simplexpr/src/eval.rs | 45 ++++++++++++++++++++++++++++++++- docs/src/expression_language.md | 4 +++ 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 625e6b4..a47556c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ All notable changes to eww will be listed here, starting at changes since versio - Add `scroll` widget (By: viandoxdev) - Add `notification` window type - Add drag and drop functionality to eventbox +- Add `search`, `captures`, `stringlength`, `arraylength` and `objectlength` functions for expressions (By: MartinJM, ElKowar) ### Notable Internal changes - Rework state management completely, now making local state and dynamic widget hierarchy changes possible. diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index ca88203..ab1faae 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -14,7 +14,7 @@ pub struct ConversionError { } impl ConversionError { - fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self { + pub fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self { ConversionError { value, target_type, source: Some(Box::new(source)) } } } @@ -214,6 +214,22 @@ impl DynVal { serde_json::from_str::(&self.0) .map_err(|e| ConversionError::new(self.clone(), "json-value", Box::new(e))) } + + pub fn as_json_array(&self) -> Result> { + serde_json::from_str::(&self.0) + .map_err(|e| ConversionError::new(self.clone(), "json-value", Box::new(e)))? + .as_array() + .cloned() + .ok_or_else(|| ConversionError { value: self.clone(), target_type: "json-array", source: None }) + } + + pub fn as_json_object(&self) -> Result> { + serde_json::from_str::(&self.0) + .map_err(|e| ConversionError::new(self.clone(), "json-value", Box::new(e)))? + .as_object() + .cloned() + .ok_or_else(|| ConversionError { value: self.clone(), target_type: "json-object", source: None }) + } } #[cfg(test)] diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 910dbb7..4952872 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -5,7 +5,10 @@ use crate::{ dynval::{ConversionError, DynVal}, }; use eww_shared_util::{Span, Spanned, VarName}; -use std::{collections::HashMap, convert::TryFrom}; +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, +}; #[derive(Debug, thiserror::Error)] pub enum EvalError { @@ -276,6 +279,46 @@ fn call_expr_function(name: &str, args: Vec) -> Result Err(EvalError::WrongArgCount(name.to_string())), }, + "search" => match args.as_slice() { + [string, pattern] => { + use serde_json::Value; + let string = string.as_string()?; + let pattern = regex::Regex::new(&pattern.as_string()?)?; + Ok(Value::Array(pattern.find_iter(&string).map(|x| Value::String(x.as_str().to_string())).collect()) + .try_into()?) + } + _ => Err(EvalError::WrongArgCount(name.to_string())), + }, + "captures" => match args.as_slice() { + [string, pattern] => { + use serde_json::Value; + let string = string.as_string()?; + let pattern = regex::Regex::new(&pattern.as_string()?)?; + Ok(Value::Array( + pattern + .captures_iter(&string) + .map(|captures| { + Value::Array(captures.iter().flatten().map(|x| Value::String(x.as_str().to_string())).collect()) + }) + .collect(), + ) + .try_into()?) + } + _ => Err(EvalError::WrongArgCount(name.to_string())), + }, + "strlength" => match args.as_slice() { + [string] => Ok(DynVal::from(string.as_string()?.len() as i32)), + _ => Err(EvalError::WrongArgCount(name.to_string())), + }, + "arraylength" => match args.as_slice() { + [json] => Ok(DynVal::from(json.as_json_array()?.len() as i32)), + _ => Err(EvalError::WrongArgCount(name.to_string())), + }, + "objectlength" => match args.as_slice() { + [json] => Ok(DynVal::from(json.as_json_object()?.len() as i32)), + _ => Err(EvalError::WrongArgCount(name.to_string())), + }, + _ => Err(EvalError::UnknownFunction(name.to_string())), } } diff --git a/docs/src/expression_language.md b/docs/src/expression_language.md index a98daf3..b29114a 100644 --- a/docs/src/expression_language.md +++ b/docs/src/expression_language.md @@ -32,4 +32,8 @@ Supported currently are the following features: - some function calls: - `round(number, decimal_digits)`: Round a number to the given amount of decimals - `replace(string, regex, replacement)`: Replace matches of a given regex in a string + - `search(string, regex)`: Search for a given regex in a string (returns array) + - `captures(string, regex)`: Get the captures of a given regex in a string (returns array) + - `hex_decode(string)`: Hex decodes the string + - `length(value)`: Gets the length of the value, as string length, json array length, or json object length