Skip to content

Commit

Permalink
Add range_select function to simple expr
Browse files Browse the repository at this point in the history
  • Loading branch information
ModProg committed Nov 26, 2022
1 parent 7a0e1b7 commit 3953b9c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 2 deletions.
25 changes: 24 additions & 1 deletion crates/simplexpr/src/dynval.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
use eww_shared_util::{Span, Spanned};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt, iter::FromIterator, str::FromStr};
use std::{
convert::TryFrom,
fmt,
iter::FromIterator,
ops::{Bound, RangeBounds},
str::FromStr,
};

pub type Result<T> = std::result::Result<T, ConversionError>;

Expand Down Expand Up @@ -232,6 +238,23 @@ impl DynVal {
.cloned()
.ok_or_else(|| ConversionError { value: self.clone(), target_type: "json-object", source: None })
}

pub fn as_range(&self) -> Result<(Bound<f64>, Bound<f64>)> {
let range = self.as_string()?;
let Some((from, to)) = range.split_once("..") else {
return Err(ConversionError { value: self.clone(), target_type: "range", source: None })
};
let from = match from {
"" => Bound::Unbounded,
v => DynVal::from_str(v)?.as_f64().map(Bound::Included)?,
};
let to = match to {
"" => Bound::Unbounded,
v if v.starts_with('=') => DynVal::from_str(&v[1..])?.as_f64().map(Bound::Included)?,
v => DynVal::from_str(v)?.as_f64().map(Bound::Excluded)?,
};
Ok((from, to))
}
}

#[cfg(test)]
Expand Down
30 changes: 30 additions & 0 deletions crates/simplexpr/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use eww_shared_util::{Span, Spanned, VarName};
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
ops::RangeBounds,
};

#[derive(Debug, thiserror::Error)]
Expand All @@ -33,6 +34,10 @@ pub enum EvalError {
#[error("Unable to index into value {0}")]
CannotIndex(String),

// TODO useful error variant?
#[error("Function {0} failed: {1}")]
FunctionError(&'static str, String),

#[error("Json operation failed: {0}")]
SerdeError(#[from] serde_json::error::Error),

Expand Down Expand Up @@ -341,6 +346,31 @@ fn call_expr_function(name: &str, args: Vec<DynVal>) -> Result<DynVal, EvalError
[json] => Ok(DynVal::from(json.as_json_object()?.len() as i32)),
_ => Err(EvalError::WrongArgCount(name.to_string())),
},
"range_select" => match args.as_slice() {
[value, map] => {
let value = value.as_f64()?;
let map = map.as_json_array()?;
for mapping in map {
let mapping = DynVal::from(&mapping);
let array = mapping.as_json_array()?;
let [range, result] = array.as_slice() else {
// TODO what error to use here
// TODO we could also add `DynVal::as_tuple<const LENGTH: usize>()`
return Err(ConversionError{
value: mapping,
target_type: "[string, string]",
source: None
}.into())
};
let range = DynVal::from(range).as_range()?;
if range.contains(&value) {
return Ok(result.into());
}
}
Err(EvalError::FunctionError("range_select", format!("No entry matched value: {value}")))
}
_ => Err(EvalError::WrongArgCount(name.to_string())),
},

_ => Err(EvalError::UnknownFunction(name.to_string())),
}
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[toolchain]
channel = "nightly-2022-08-27"
channel = "nightly-2022-11-25"
components = [ "rust-src" ]
profile = "default"

0 comments on commit 3953b9c

Please sign in to comment.