Implement, test and document serde Deserialize for expressions

Relates to #18
This commit is contained in:
Sebastian Schmidt 2019-03-23 15:20:43 +02:00
parent 3d440524a5
commit a7ca4c717d
7 changed files with 93 additions and 25 deletions

View File

@ -5,6 +5,8 @@
### Added
* Add `serde` feature
* Implement `serde::de::Deserialize` for `Node`
* Document `serde` usage
### Removed

View File

@ -18,4 +18,7 @@ path = "src/lib.rs"
[dependencies]
serde = { version = "1", optional = true}
[features]
[features]
[dev-dependencies]
ron = "0.4"

View File

@ -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),
}
}
}

View File

@ -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
View 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())
}
}

View File

@ -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

View File

@ -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());
}
}