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 ### Added
* Add `serde` feature * Add `serde` feature
* Implement `serde::de::Deserialize` for `Node`
* Document `serde` usage
### Removed ### Removed

View File

@ -19,3 +19,6 @@ path = "src/lib.rs"
serde = { version = "1", optional = true} 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 {}.", "An operator expected {} arguments, but got {}.",
expected, actual expected, actual
), ),
WrongFunctionArgumentAmount { expected, actual } => write!( WrongFunctionArgumentAmount { expected, actual } => write!(
f, f,
"A function expected {} arguments, but got {}.", "A function expected {} arguments, but got {}.",
expected, actual expected, actual
), ),
ExpectedString { actual } => { ExpectedString { actual } => {
write!(f, "Expected a Value::String, but got {:?}.", actual) write!(f, "Expected a Value::String, but got {:?}.", actual)
}, },
ExpectedInt { actual } => write!(f, "Expected a Value::Int, 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), ExpectedFloat { actual } => write!(f, "Expected a Value::Float, but got {:?}.", actual),
ExpectedNumber { actual } => { ExpectedNumber { actual } => {
write!(f, "Expected a Value::Number, but got {:?}.", actual) write!(f, "Expected a Value::Number, but got {:?}.", actual)
}, },
ExpectedBoolean { actual } => { ExpectedBoolean { actual } => {
write!(f, "Expected a Value::Boolean, but got {:?}.", actual) write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
}, },
ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual), ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
EmptyExpression => write!( EmptyExpression => write!(
f, f,
"Got an empty expression that cannot be parsed into a node tree, because it \ "Got an empty expression that cannot be parsed into a node tree, because it \
returns nothing." returns nothing."
), ),
AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."), AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."),
PrecedenceViolation => write!( PrecedenceViolation => write!(
f, f,
"Tried to append a node to another node with higher precedence." "Tried to append a node to another node with higher precedence."
), ),
VariableIdentifierNotFound(identifier) => write!( VariableIdentifierNotFound(identifier) => write!(
f, f,
"Variable identifier is not bound to anything by configuration: {:?}.", "Variable identifier is not bound to anything by configuration: {:?}.",
identifier identifier
), ),
FunctionIdentifierNotFound(identifier) => write!( FunctionIdentifierNotFound(identifier) => write!(
f, f,
"Function identifier is not bound to anything by configuration: {:?}.", "Function identifier is not bound to anything by configuration: {:?}.",
identifier identifier
), ),
TypeError { expected, actual } => { TypeError { expected, actual } => {
write!(f, "Expected one of {:?}, but got {:?}.", expected, actual) write!(f, "Expected one of {:?}, but got {:?}.", expected, actual)
}, },
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."), UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."), UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
UnmatchedPartialToken { first, second } => { UnmatchedPartialToken { first, second } => {
if let Some(second) = second { if let Some(second) = second {
write!( write!(
@ -84,28 +68,23 @@ impl fmt::Display for Error {
) )
} }
}, },
AdditionError { augend, addend } => write!(f, "Error adding {} + {}", augend, addend), AdditionError { augend, addend } => write!(f, "Error adding {} + {}", augend, addend),
SubtractionError { SubtractionError {
minuend, minuend,
subtrahend, subtrahend,
} => write!(f, "Error subtracting {} - {}", minuend, subtrahend), } => write!(f, "Error subtracting {} - {}", minuend, subtrahend),
NegationError { argument } => write!(f, "Error negating -{}", argument), NegationError { argument } => write!(f, "Error negating -{}", argument),
MultiplicationError { MultiplicationError {
multiplicand, multiplicand,
multiplier, multiplier,
} => write!(f, "Error multiplying {} * {}", multiplicand, multiplier), } => write!(f, "Error multiplying {} * {}", multiplicand, multiplier),
DivisionError { dividend, divisor } => { DivisionError { dividend, divisor } => {
write!(f, "Error dividing {} / {}", dividend, divisor) write!(f, "Error dividing {} / {}", dividend, divisor)
}, },
ModulationError { dividend, divisor } => { ModulationError { dividend, divisor } => {
write!(f, "Error modulating {} % {}", 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. /// The second argument of the modulation.
divisor: Value, divisor: Value,
}, },
/// A custom error explained by its message.
Custom(String),
} }
impl Error { 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` | //! | `true` | no | Expression is interpreted as `Value::Bool` |
//! | `.34` | no | Expression is interpreted as `Value::Float` | //! | `.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 //! ## License
//! //!
//! This crate is primarily distributed under the terms of the MIT license. //! This crate is primarily distributed under the terms of the MIT license.
@ -239,19 +264,21 @@
#![warn(missing_docs)] #![warn(missing_docs)]
#[cfg(test)]
extern crate ron;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
extern crate serde; extern crate serde;
mod configuration; mod configuration;
pub mod error; pub mod error;
#[cfg(feature = "serde")]
mod feature_serde;
mod function; mod function;
mod interface; mod interface;
mod operator; mod operator;
mod token; mod token;
mod tree; mod tree;
mod value; mod value;
#[cfg(feature = "serde")]
mod feature_serde;
// Exports // Exports

View File

@ -407,3 +407,15 @@ fn test_shortcut_functions() {
Ok(vec![Value::Int(3), Value::Int(3)]) 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());
}
}