Implement, test and document serde Deserialize
for expressions
Relates to #18
This commit is contained in:
parent
3d440524a5
commit
a7ca4c717d
@ -5,6 +5,8 @@
|
||||
### Added
|
||||
|
||||
* Add `serde` feature
|
||||
* Implement `serde::de::Deserialize` for `Node`
|
||||
* Document `serde` usage
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -18,4 +18,7 @@ path = "src/lib.rs"
|
||||
[dependencies]
|
||||
serde = { version = "1", optional = true}
|
||||
|
||||
[features]
|
||||
[features]
|
||||
|
||||
[dev-dependencies]
|
||||
ron = "0.4"
|
@ -10,64 +10,48 @@ impl fmt::Display for Error {
|
||||
"An operator expected {} arguments, but got {}.",
|
||||
expected, actual
|
||||
),
|
||||
|
||||
WrongFunctionArgumentAmount { expected, actual } => write!(
|
||||
f,
|
||||
"A function expected {} arguments, but got {}.",
|
||||
expected, actual
|
||||
),
|
||||
|
||||
ExpectedString { actual } => {
|
||||
write!(f, "Expected a Value::String, but got {:?}.", actual)
|
||||
},
|
||||
|
||||
ExpectedInt { actual } => write!(f, "Expected a Value::Int, but got {:?}.", actual),
|
||||
|
||||
ExpectedFloat { actual } => write!(f, "Expected a Value::Float, but got {:?}.", actual),
|
||||
|
||||
ExpectedNumber { actual } => {
|
||||
write!(f, "Expected a Value::Number, but got {:?}.", actual)
|
||||
},
|
||||
|
||||
ExpectedBoolean { actual } => {
|
||||
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
|
||||
},
|
||||
|
||||
ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
|
||||
|
||||
EmptyExpression => write!(
|
||||
f,
|
||||
"Got an empty expression that cannot be parsed into a node tree, because it \
|
||||
returns nothing."
|
||||
),
|
||||
|
||||
AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."),
|
||||
|
||||
PrecedenceViolation => write!(
|
||||
f,
|
||||
"Tried to append a node to another node with higher precedence."
|
||||
),
|
||||
|
||||
VariableIdentifierNotFound(identifier) => write!(
|
||||
f,
|
||||
"Variable identifier is not bound to anything by configuration: {:?}.",
|
||||
identifier
|
||||
),
|
||||
|
||||
FunctionIdentifierNotFound(identifier) => write!(
|
||||
f,
|
||||
"Function identifier is not bound to anything by configuration: {:?}.",
|
||||
identifier
|
||||
),
|
||||
|
||||
TypeError { expected, actual } => {
|
||||
write!(f, "Expected one of {:?}, but got {:?}.", expected, actual)
|
||||
},
|
||||
|
||||
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
|
||||
|
||||
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
|
||||
|
||||
UnmatchedPartialToken { first, second } => {
|
||||
if let Some(second) = second {
|
||||
write!(
|
||||
@ -84,28 +68,23 @@ impl fmt::Display for Error {
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
AdditionError { augend, addend } => write!(f, "Error adding {} + {}", augend, addend),
|
||||
|
||||
SubtractionError {
|
||||
minuend,
|
||||
subtrahend,
|
||||
} => write!(f, "Error subtracting {} - {}", minuend, subtrahend),
|
||||
|
||||
NegationError { argument } => write!(f, "Error negating -{}", argument),
|
||||
|
||||
MultiplicationError {
|
||||
multiplicand,
|
||||
multiplier,
|
||||
} => write!(f, "Error multiplying {} * {}", multiplicand, multiplier),
|
||||
|
||||
DivisionError { dividend, divisor } => {
|
||||
write!(f, "Error dividing {} / {}", dividend, divisor)
|
||||
},
|
||||
|
||||
ModulationError { dividend, divisor } => {
|
||||
write!(f, "Error modulating {} % {}", dividend, divisor)
|
||||
},
|
||||
Custom(message) => write!(f, "Error: {}", message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +153,9 @@ pub enum Error {
|
||||
/// The second argument of the modulation.
|
||||
divisor: Value,
|
||||
},
|
||||
|
||||
/// A custom error explained by its message.
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
|
42
src/feature_serde/mod.rs
Normal file
42
src/feature_serde/mod.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use interface::build_operator_tree;
|
||||
use serde::{de, Deserialize, Deserializer};
|
||||
use std::fmt;
|
||||
use ::{Error, Node};
|
||||
|
||||
impl<'de> Deserialize<'de> for Node {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(NodeVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct NodeVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for NodeVisitor {
|
||||
type Value = Node;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"a string in the expression format of the `evalexpr` crate"
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
match build_operator_tree(v) {
|
||||
Ok(node) => Ok(node),
|
||||
Err(error) => Err(E::custom(error)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Error::Custom(msg.to_string())
|
||||
}
|
||||
}
|
31
src/lib.rs
31
src/lib.rs
@ -231,6 +231,31 @@
|
||||
//! | `true` | no | Expression is interpreted as `Value::Bool` |
|
||||
//! | `.34` | no | Expression is interpreted as `Value::Float` |
|
||||
//!
|
||||
//! ### [serde](https://serde.rs)
|
||||
//!
|
||||
//! This crate implements `serde::de::Deserialize` for its type `Node` that represents a parsed expression tree.
|
||||
//! The implementation expects a `string` as input.
|
||||
//! Example parsing with [ron format](docs.rs/ron):
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate ron;
|
||||
//! use evalexpr::*;
|
||||
//!
|
||||
//! let mut configuration = HashMapConfiguration::new();
|
||||
//! configuration.insert_variable("five", 5);
|
||||
//!
|
||||
//! // In ron format, strings are surrounded by "
|
||||
//! let serialized_free = "\"five * five\"";
|
||||
//! match ron::de::from_str::<Node>(serialized_free) {
|
||||
//! Ok(free) => assert_eq!(free.eval_with_configuration(&configuration), Ok(Value::from(25))),
|
||||
//! Err(error) => {
|
||||
//! // Handle error
|
||||
//! },
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! With `serde`, expressions can be integrated into arbitrarily complex data.
|
||||
//!
|
||||
//! ## License
|
||||
//!
|
||||
//! This crate is primarily distributed under the terms of the MIT license.
|
||||
@ -239,19 +264,21 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate ron;
|
||||
#[cfg(feature = "serde")]
|
||||
extern crate serde;
|
||||
|
||||
mod configuration;
|
||||
pub mod error;
|
||||
#[cfg(feature = "serde")]
|
||||
mod feature_serde;
|
||||
mod function;
|
||||
mod interface;
|
||||
mod operator;
|
||||
mod token;
|
||||
mod tree;
|
||||
mod value;
|
||||
#[cfg(feature = "serde")]
|
||||
mod feature_serde;
|
||||
|
||||
// Exports
|
||||
|
||||
|
@ -407,3 +407,15 @@ fn test_shortcut_functions() {
|
||||
Ok(vec![Value::Int(3), Value::Int(3)])
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn test_serde() {
|
||||
let strings = ["3", "4+4", "21^(2*2)--3>5||!true"];
|
||||
|
||||
for string in &strings {
|
||||
let manual_tree = build_operator_tree(string).unwrap();
|
||||
let serde_tree: Node = ron::de::from_str(&format!("\"{}\"", string)).unwrap();
|
||||
assert_eq!(manual_tree.eval(), serde_tree.eval());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user