use std::collections::HashMap;
use dataflow_rs::engine::error::DataflowError;
use dataflow_rs::engine::task_context::TaskContext;
use dataflow_rs::{Template, TemplateCompiler};
use serde::{Deserialize, Deserializer};
use serde_json::Value;
#[derive(Debug, Default)]
pub struct TemplatedInput {
raw: Value,
templates: HashMap<String, Template>,
}
impl<'de> Deserialize<'de> for TemplatedInput {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
Ok(Self {
raw: Value::deserialize(d)?,
templates: HashMap::new(),
})
}
}
impl From<Value> for TemplatedInput {
fn from(raw: Value) -> Self {
Self {
raw,
templates: HashMap::new(),
}
}
}
impl TemplatedInput {
pub fn compile(
&mut self,
handler: &'static str,
c: &TemplateCompiler,
) -> dataflow_rs::Result<()> {
let Some(object) = self.raw.as_object() else {
return Ok(());
};
let mut compiled = HashMap::new();
for (field, value) in object {
let at = super::schema::template_paths(handler, field);
if at.contains(&"") {
let mut template = Template::from(value.clone());
template.compile(c, &format!("{handler}.{field}"))?;
compiled.insert(field.clone(), template);
} else if at.contains(&"*")
&& let Some(members) = value.as_object()
{
for (member, member_value) in members {
let mut template = Template::from(member_value.clone());
template.compile(c, &format!("{handler}.{field}.{member}"))?;
compiled.insert(format!("{field}.{member}"), template);
}
}
}
self.templates = compiled;
Ok(())
}
pub fn raw(&self) -> &Value {
&self.raw
}
pub fn get(&self, field: &str) -> Option<&Value> {
self.raw.get(field)
}
pub fn value_of(
&self,
field: &str,
handler: &str,
ctx: &TaskContext<'_>,
) -> Option<Result<Value, DataflowError>> {
let raw = self.raw.get(field)?;
Some(match self.templates.get(field) {
Some(template) => template.resolve(ctx).map(|v| Value::from(&v)),
None => Ok(super::connector_helpers::resolve_declared_field(
handler, field, raw, ctx,
)),
})
}
}
impl TemplatedInput {
pub fn member_value(
&self,
field: &str,
member: &str,
_handler: &str,
ctx: &TaskContext<'_>,
) -> Option<Result<Value, DataflowError>> {
let raw = self.raw.get(field)?.get(member)?;
Some(match self.templates.get(&format!("{field}.{member}")) {
Some(template) => template.resolve(ctx).map(|v| Value::from(&v)),
None => Ok(raw.clone()),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn an_uncompiled_input_reads_its_fields_as_literals() {
let input = TemplatedInput::from(json!({"key": "orders", "n": 3}));
let datalogic = std::sync::Arc::new(dataflow_rs::datalogic_rs::Engine::new());
let mut message = dataflow_rs::Message::from_value(&json!({}));
let ctx = TaskContext::new(&mut message, &datalogic);
assert_eq!(
input
.value_of("key", "cache_read", &ctx)
.expect("set")
.expect("reads"),
json!("orders")
);
assert!(input.value_of("absent", "cache_read", &ctx).is_none());
}
#[test]
fn the_authored_input_survives_verbatim() {
let input = TemplatedInput::from(json!({"connector": "c", "op": "insert_one"}));
assert_eq!(input.get("connector"), Some(&json!("c")));
assert_eq!(input.raw()["op"], json!("insert_one"));
}
}