use crate::bindings::attributes::AttributesBindingAdapter;
use crate::constants::{
ATTRIBUTES, ATTRIBUTES_REFERENCE, HEADERS, HEADERS_REFERENCE, LOCAL_ADDRESS, METHOD,
QUERY_PARAMS, QUERY_PARAMS_REFERENCE, QUERY_STRING, REMOTE_ADDRESS, REQUEST_PATH, REQUEST_URI,
SCHEME, STATUS_CODE, VERSION,
};
use crate::context::input::InputContext;
use crate::context::map::MapContext;
use pel::expression::Symbol;
use pel::runtime::value::Value as PelValue;
use pel::runtime::{Binding as PelBinding, Context, ValueHandler};
use pel::Reference;
pub(crate) struct AttributesContext<'a> {
input_context: &'a InputContext,
binding: &'a AttributesBindingAdapter<'a>,
headers: HeaderValueHandler<'a>,
query: MapContext<String>,
}
impl<'a> AttributesContext<'a> {
pub fn new(input_context: &'a InputContext, binding: &'a AttributesBindingAdapter<'a>) -> Self {
let headers = HeaderValueHandler::new(binding);
let query = MapContext::new(binding.extract_query_params());
Self {
input_context,
binding,
headers,
query,
}
}
}
impl Context for AttributesContext<'_> {
fn resolve(&self, symbol: &Symbol) -> PelBinding {
match self.input_context.attributes() && symbol.as_str() == ATTRIBUTES {
true => PelBinding::Available(PelValue::reference(ATTRIBUTES_REFERENCE)),
false => PelBinding::Unknown,
}
}
fn value_handler(&self, reference: Reference) -> Option<&dyn ValueHandler> {
match reference {
ATTRIBUTES_REFERENCE => Some(self),
HEADERS_REFERENCE => Some(&self.headers),
QUERY_PARAMS_REFERENCE => Some(&self.query),
_ => None,
}
}
}
impl ValueHandler for AttributesContext<'_> {
fn detach(&self) -> Option<PelValue> {
let values = [
(HEADERS, self.headers.detach()),
(QUERY_PARAMS, self.query.detach()),
(METHOD, self.binding.method()),
(REQUEST_PATH, self.binding.path()),
(REQUEST_URI, self.binding.uri()),
(REMOTE_ADDRESS, self.binding.remote_address()),
(LOCAL_ADDRESS, self.binding.local_address()),
(QUERY_STRING, self.binding.query_string()),
(SCHEME, self.binding.scheme()),
(VERSION, self.binding.version()),
(STATUS_CODE, self.binding.status_code()),
]
.map(|(k, v)| (k.to_string(), v.unwrap_or_else(PelValue::null)));
Some(PelValue::object(values.into()))
}
fn select_by_key(&self, key: &str) -> Option<PelValue> {
let selection = match key {
HEADERS => Some(PelValue::reference(HEADERS_REFERENCE)),
QUERY_PARAMS => Some(PelValue::reference(QUERY_PARAMS_REFERENCE)),
METHOD => self.binding.method(),
REQUEST_PATH => self.binding.path(),
REQUEST_URI => self.binding.uri(),
REMOTE_ADDRESS => self.binding.remote_address(),
LOCAL_ADDRESS => self.binding.local_address(),
QUERY_STRING => self.binding.query_string(),
SCHEME => self.binding.scheme(),
VERSION => self.binding.version(),
STATUS_CODE => self.binding.status_code(),
_ => None,
};
Some(selection.unwrap_or_else(PelValue::null))
}
}
struct HeaderValueHandler<'a> {
binding: &'a AttributesBindingAdapter<'a>,
}
impl<'a> HeaderValueHandler<'a> {
pub fn new(binding: &'a AttributesBindingAdapter<'a>) -> Self {
Self { binding }
}
}
impl ValueHandler for HeaderValueHandler<'_> {
fn detach(&self) -> Option<PelValue> {
Some(PelValue::object(self.binding.extract_headers()))
}
fn select_by_key(&self, key: &str) -> Option<PelValue> {
self.binding.extract_header(key)
}
}