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
|
### Added
|
||||||
|
|
||||||
* Add `serde` feature
|
* Add `serde` feature
|
||||||
|
* Implement `serde::de::Deserialize` for `Node`
|
||||||
|
* Document `serde` usage
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -18,4 +18,7 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1", optional = true}
|
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 {}.",
|
"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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
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` |
|
//! | `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
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user