use bumpalo::Bump;
use self_cell::self_cell;
use crate::Result;
use crate::arena::DataValue;
self_cell!(
struct ParsedCell {
owner: Bump,
#[covariant]
dependent: DataValue,
}
);
pub struct ParsedData(ParsedCell);
impl ParsedData {
pub fn from_json(json: &str) -> Result<Self> {
let cell =
ParsedCell::try_new(Bump::with_capacity(json.len().saturating_mul(2)), |arena| {
let stable: &str = arena.alloc_str(json);
DataValue::from_str(stable, arena).map_err(crate::Error::from)
})?;
Ok(Self(cell))
}
pub fn value(&self) -> &DataValue<'_> {
self.0.borrow_dependent()
}
pub fn allocated_bytes(&self) -> usize {
self.0.borrow_owner().allocated_bytes()
}
}
impl std::fmt::Debug for ParsedData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ParsedData").field(self.value()).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Engine;
#[test]
fn parses_and_exposes_value() {
let data = ParsedData::from_json(r#"{"a": [1, 2, 3], "b": "text"}"#).unwrap();
let v = data.value();
assert!(v.is_object());
assert_eq!(v.to_string(), r#"{"a":[1,2,3],"b":"text"}"#);
assert!(data.allocated_bytes() > 0);
}
#[test]
fn malformed_json_is_a_parse_error() {
let err = ParsedData::from_json("{ not json").unwrap_err();
assert_eq!(err.tag(), "ParseError");
}
#[test]
fn evaluates_through_engine_and_session() {
let engine = Engine::new();
let rule = engine.compile(r#"{"+": [{"var": "x"}, 1]}"#).unwrap();
let data = ParsedData::from_json(r#"{"x": 41}"#).unwrap();
let arena = bumpalo::Bump::new();
for _ in 0..3 {
let out = engine.evaluate(&rule, &data, &arena).unwrap();
assert_eq!(out.as_i64(), Some(42));
}
let mut session = engine.session();
let out = session.eval_borrowed(&rule, &data).unwrap();
assert_eq!(out.as_i64(), Some(42));
}
#[test]
fn outlives_short_lived_eval_arenas() {
let engine = Engine::new();
let rule = engine.compile(r#"{"var": "name"}"#).unwrap();
let data = ParsedData::from_json(r#"{"name": "Ada"}"#).unwrap();
for _ in 0..2 {
let arena = bumpalo::Bump::new();
let out = engine.evaluate(&rule, &data, &arena).unwrap();
assert_eq!(out.to_string(), r#""Ada""#);
}
assert_eq!(data.value().to_string(), r#"{"name":"Ada"}"#);
}
#[test]
fn handle_is_send() {
fn assert_send<T: Send>() {}
assert_send::<ParsedData>();
}
}