use crate::bindings::payload::PayloadBinding;
use crate::constants::PAYLOAD;
use crate::context::input::InputContext;
use crate::context::xml::xml_to_pel;
use crate::input::Format;
use crate::value::Value;
use pel::expression::Symbol;
use pel::runtime::value::Value as PelValue;
use pel::runtime::{Binding as PelBinding, Context, ValueHandler};
use pel::Reference;
use std::cell::RefCell;
pub(crate) struct PayloadContext<'a> {
input_context: &'a InputContext,
binding: &'a dyn PayloadBinding,
payload: RefCell<Option<PelValue>>,
content_type: Option<Format>,
}
impl<'a> PayloadContext<'a> {
pub fn new(
input_context: &'a InputContext,
binding: &'a dyn PayloadBinding,
content_type: Option<Format>,
) -> Self {
Self {
input_context,
binding,
payload: RefCell::new(None),
content_type,
}
}
fn do_resolve(&self) -> PelValue {
let present = self.payload.borrow().is_some();
if !present {
let bytes = self.binding.as_bytes();
let value = self
.input_context
.format()
.iter()
.filter(|f| match &self.content_type {
None => true,
Some(content_type) => (*f).eq(content_type),
})
.map(|f| Self::match_format(f, bytes.as_ref()))
.find(|v| !v.is_null());
self.payload.replace(value);
}
self.payload
.borrow()
.as_ref()
.cloned()
.unwrap_or_else(PelValue::null)
}
fn match_format(format: &Format, bytes: &[u8]) -> PelValue {
match format {
#[cfg(feature = "experimental_coerced_type")]
Format::Json => serde_json::from_slice(bytes)
.unwrap_or(Value::Null)
.coerced_pel(bytes),
#[cfg(not(feature = "experimental_coerced_type"))]
Format::Json => serde_json::from_slice(bytes)
.unwrap_or(Value::Null)
.into_pel(),
Format::PlainText => PelValue::string(String::from_utf8_lossy(bytes).to_string()),
Format::Xml => xml_to_pel(String::from_utf8_lossy(bytes).to_string()),
}
}
}
impl Context for PayloadContext<'_> {
fn resolve(&self, symbol: &Symbol) -> PelBinding {
match !self.input_context.format().is_empty() && symbol.as_str() == PAYLOAD {
true => PelBinding::Available(self.do_resolve()),
false => PelBinding::Unknown,
}
}
fn value_handler(&self, _reference: Reference) -> Option<&dyn ValueHandler> {
None
}
}