diff --git a/benches/request_template_bench.rs b/benches/request_template_bench.rs index 09df05d7c1..26cfc3fff0 100644 --- a/benches/request_template_bench.rs +++ b/benches/request_template_bench.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use derive_setters::Setters; use hyper::HeaderMap; +use serde::{Serialize, Serializer}; use serde_json::json; use tailcall::endpoint::Endpoint; use tailcall::has_headers::HasHeaders; @@ -20,14 +21,21 @@ impl Default for Context { Self { value: serde_json::Value::Null, headers: HeaderMap::new() } } } + +impl Serialize for Context { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_none() + // self.value.serialize(serializer) + } +} + impl PathString for Context { fn path_string>(&self, parts: &[T]) -> Option> { self.value.path_string(parts) } - - fn evaluate(&self, _filter: &jaq_interpret::Filter) -> Option { - None - } } impl HasHeaders for Context { fn headers(&self) -> &HeaderMap { diff --git a/src/config/reader_context.rs b/src/config/reader_context.rs index 03c235f8e4..124318e170 100644 --- a/src/config/reader_context.rs +++ b/src/config/reader_context.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; use std::sync::Arc; -use jaq_interpret::Filter; +use serde::{Serialize, Serializer}; use crate::path::PathString; use crate::EnvIO; @@ -12,6 +12,15 @@ pub struct ConfigReaderContext<'a> { pub vars: &'a BTreeMap, } +impl<'a> Serialize for ConfigReaderContext<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_none() + } +} + impl<'a> PathString for ConfigReaderContext<'a> { fn path_string>(&self, path: &[T]) -> Option> { if path.is_empty() { @@ -25,10 +34,6 @@ impl<'a> PathString for ConfigReaderContext<'a> { _ => None, }) } - - fn evaluate(&self, _filter: &Filter) -> Option { - None - } } #[cfg(test)] diff --git a/src/grpc/request_template.rs b/src/grpc/request_template.rs index c7c8c98652..c23518da8f 100644 --- a/src/grpc/request_template.rs +++ b/src/grpc/request_template.rs @@ -124,6 +124,7 @@ mod tests { use hyper::header::{HeaderName, HeaderValue}; use hyper::{HeaderMap, Method}; use pretty_assertions::assert_eq; + use serde::{Serialize, Serializer}; use tailcall_fixtures::protobuf; use super::RequestTemplate; @@ -185,14 +186,20 @@ mod tests { } } + impl Serialize for Context { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_none() + // self.value.serialize(serializer) + } + } + impl crate::path::PathString for Context { fn path_string>(&self, parts: &[T]) -> Option> { self.value.path_string(parts) } - - fn evaluate(&self, _filter: &jaq_interpret::Filter) -> Option { - todo!() - } } impl crate::has_headers::HasHeaders for Context { diff --git a/src/http/request_template.rs b/src/http/request_template.rs index 59aa42ec52..5360ed4097 100644 --- a/src/http/request_template.rs +++ b/src/http/request_template.rs @@ -263,6 +263,7 @@ mod tests { use hyper::header::HeaderName; use hyper::HeaderMap; use pretty_assertions::assert_eq; + use serde::{Serialize, Serializer}; use serde_json::json; use super::RequestTemplate; @@ -282,13 +283,19 @@ mod tests { } } - impl crate::path::PathString for Context { - fn path_string>(&self, parts: &[T]) -> Option> { - self.value.path_string(parts) + impl Serialize for Context { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_none() + // self.value.serialize(serializer) } + } - fn evaluate(&self, _filter: &jaq_interpret::Filter) -> Option { - None + impl PathString for Context { + fn path_string>(&self, parts: &[T]) -> Option> { + self.value.path_string(parts) } } diff --git a/src/mustache.rs b/src/mustache.rs index ec4d71f148..f28b60306e 100644 --- a/src/mustache.rs +++ b/src/mustache.rs @@ -1,5 +1,6 @@ use std::fmt::Display; +use jaq_interpret::FilterT; use nom::branch::alt; use nom::bytes::complete::{tag, take_until}; use nom::character::complete::char; @@ -7,6 +8,7 @@ use nom::combinator::map; use nom::multi::many0; use nom::sequence::delimited; use nom::{Finish, IResult}; +use serde::Serialize; use crate::path::{PathGraphql, PathString}; @@ -61,17 +63,33 @@ impl Mustache { .collect(); if val.is_empty() { - self.evaluate(value) + self.evaluate_inner(value) + .map(|v| v.to_string()) + .unwrap_or_default() } else { val } } - fn evaluate(&self, value: &impl PathString) -> String { - value - .evaluate(&self.jacques) - .map(|v| v.to_string()) - .unwrap_or_default() + // TODO: Null converts to "null" as string but it should be empty string + // fn evaluate(&self, value: &T) -> async_graphql::Value { + // self.evaluate_inner(value).unwrap_or_default() + // } + + fn evaluate_inner(&self, value: &T) -> Option { + let iter = jaq_interpret::RcIter::new(vec![].into_iter()); + let value = serde_json::to_value(value).ok()?; + if value.is_null() { + return None; + } + let mut result = self.jacques.run(( + jaq_interpret::Ctx::new(vec![], &iter), + jaq_interpret::Val::from(value), + )); + let result = result.next()?; + let result = result.ok()?; + let result = async_graphql::Value::from(result.to_string()); + Some(result) } pub fn render_graphql(&self, value: &impl PathGraphql) -> String { @@ -416,7 +434,7 @@ mod tests { mod render { use std::borrow::Cow; - use jaq_interpret::Filter; + use serde::{Serialize, Serializer}; use serde_json::json; use crate::mustache::{Mustache, Segment}; @@ -435,6 +453,14 @@ mod tests { fn test_render_mixed() { struct DummyPath; + impl Serialize for DummyPath { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str("") + } + } impl PathString for DummyPath { fn path_string>(&self, parts: &[T]) -> Option> { let parts: Vec<&str> = parts.iter().map(AsRef::as_ref).collect(); @@ -447,10 +473,6 @@ mod tests { None } } - - fn evaluate(&self, _filter: &Filter) -> Option { - None // TODO - } } let mustache = Mustache::from(vec![ @@ -471,14 +493,18 @@ mod tests { fn test_render_with_missing_path() { struct DummyPath; + impl Serialize for DummyPath { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str("") + } + } impl PathString for DummyPath { fn path_string>(&self, _: &[T]) -> Option> { None } - - fn evaluate(&self, _filter: &Filter) -> Option { - None - } } let mustache = Mustache::from(vec![ @@ -511,6 +537,14 @@ mod tests { fn test_render_preserves_spaces() { struct DummyPath; + impl Serialize for DummyPath { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str("") + } + } impl PathString for DummyPath { fn path_string>(&self, parts: &[T]) -> Option> { let parts: Vec<&str> = parts.iter().map(AsRef::as_ref).collect(); @@ -521,10 +555,6 @@ mod tests { None } } - - fn evaluate(&self, _filter: &Filter) -> Option { - todo!() - } } let mustache = Mustache::from(vec![ diff --git a/src/path.rs b/src/path.rs index 29a1cf63d6..dd58895948 100644 --- a/src/path.rs +++ b/src/path.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use jaq_interpret::{Filter, FilterT}; +use serde::{Serialize, Serializer}; use serde_json::json; use crate::json::JsonLike; @@ -14,9 +14,8 @@ use crate::lambda::{EvaluationContext, ResolverContextLike}; /// The PathString trait provides a method for accessing values from a JSON-like /// structure. The returned value is encoded as a plain string. /// This is typically used in evaluating mustache templates. -pub trait PathString { +pub trait PathString: Serialize { fn path_string>(&self, path: &[T]) -> Option>; - fn evaluate(&self, filter: &Filter) -> Option; } /// @@ -33,18 +32,6 @@ impl PathString for serde_json::Value { _ => Cow::Owned(a.to_string()), }) } - - fn evaluate(&self, filter: &Filter) -> Option { - let iter = jaq_interpret::RcIter::new(vec![].into_iter()); - let mut result = filter.run(( - jaq_interpret::Ctx::new(vec![], &iter), - jaq_interpret::Val::from(self.clone()), - )); - let result = result.next()?; - let result = result.ok()?; - let result = async_graphql::Value::from(result.to_string()); - Some(result) - } } fn convert_value(value: Cow<'_, async_graphql::Value>) -> Option> { @@ -63,6 +50,18 @@ fn convert_value(value: Cow<'_, async_graphql::Value>) -> Option> { } } +impl<'a, Ctx: ResolverContextLike<'a>> Serialize for EvaluationContext<'a, Ctx> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.value() + .cloned() + .unwrap_or_default() + .serialize(serializer) + } +} + impl<'a, Ctx: ResolverContextLike<'a>> PathString for EvaluationContext<'a, Ctx> { fn path_string>(&self, path: &[T]) -> Option> { let ctx = self; @@ -90,18 +89,6 @@ impl<'a, Ctx: ResolverContextLike<'a>> PathString for EvaluationContext<'a, Ctx> _ => None, }) } - - fn evaluate(&self, filter: &Filter) -> Option { - let iter = jaq_interpret::RcIter::new(vec![].into_iter()); - let mut result = filter.run(( - jaq_interpret::Ctx::new(vec![], &iter), - jaq_interpret::Val::from(serde_json::to_value(self.value()?).ok()?), - )); - let result = result.next()?; - let result = result.ok()?; - let result = async_graphql::Value::from(result.to_string()); - Some(result) - } } impl<'a, Ctx: ResolverContextLike<'a>> PathGraphql for EvaluationContext<'a, Ctx> {