use crate::constants::{
DESTINATION_ADDRESS, METHOD_HEADER, PATH_HEADER, REQUEST_PROTOCOL, REQUEST_SCHEME,
SCHEME_HEADER, SOURCE_ADDRESS, STATUS_CODE_HEADER,
};
use classy::event::HeadersAccessor;
use classy::hl::{HeadersHandler, HttpClientResponse, PropertyAccessor};
use pel::runtime::value::Value as PelValue;
use std::collections::HashMap;
use url::Url;
pub trait AttributesBinding {
fn extract_headers(&self) -> HashMap<String, String>;
fn extract_header(&self, key: &str) -> Option<String>;
fn extract_query_params(&self) -> HashMap<String, String> {
self.extract_header(PATH_HEADER)
.and_then(fake_url)
.map(|url| {
url.query_pairs()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()
})
.unwrap_or_default()
}
fn method(&self) -> Option<String> {
self.extract_header(METHOD_HEADER)
}
fn path(&self) -> Option<String> {
let mut url = self.extract_header(PATH_HEADER).and_then(fake_url)?;
url.set_query(None);
Some(url.path().to_string())
}
fn uri(&self) -> Option<String> {
self.extract_header(PATH_HEADER)
}
fn remote_address(&self) -> Option<String> {
None
}
fn local_address(&self) -> Option<String> {
None
}
fn query_string(&self) -> Option<String> {
let path = self.extract_header(PATH_HEADER)?;
fake_url(path)?.query().map(str::to_string)
}
fn scheme(&self) -> Option<String> {
self.extract_header(SCHEME_HEADER)
}
fn version(&self) -> Option<String> {
None
}
fn status_code(&self) -> Option<u32> {
self.extract_header(STATUS_CODE_HEADER)
.and_then(|value| value.parse::<u32>().ok())
}
}
impl AttributesBinding for HttpClientResponse {
fn extract_headers(&self) -> HashMap<String, String> {
self.headers().clone()
}
fn extract_header(&self, key: &str) -> Option<String> {
self.header(key).cloned()
}
}
pub struct HandlerAttributesBinding<'a> {
handler: &'a dyn HeadersHandler,
properties: Option<&'a dyn PropertyAccessor>,
}
impl<'a> HandlerAttributesBinding<'a> {
pub fn new(handler: &'a dyn HeadersHandler, properties: &'a dyn PropertyAccessor) -> Self {
Self {
handler,
properties: Some(properties),
}
}
pub fn partial(handler: &'a dyn HeadersHandler) -> Self {
Self {
handler,
properties: None,
}
}
}
impl AttributesBinding for HandlerAttributesBinding<'_> {
fn extract_headers(&self) -> HashMap<String, String> {
self.handler.headers().into_iter().collect()
}
fn extract_header(&self, key: &str) -> Option<String> {
self.handler.header(key)
}
fn remote_address(&self) -> Option<String> {
self.properties.and_then(remote_address)
}
fn local_address(&self) -> Option<String> {
self.properties.and_then(local_address)
}
fn scheme(&self) -> Option<String> {
self.properties.and_then(scheme)
}
fn version(&self) -> Option<String> {
self.properties.and_then(version)
}
}
#[doc(hidden)]
pub struct AccessorAttributesBinding<'a> {
accessor: &'a dyn HeadersAccessor,
properties: Option<&'a dyn PropertyAccessor>,
}
impl<'a> AccessorAttributesBinding<'a> {
pub fn new(handler: &'a dyn HeadersAccessor, properties: &'a dyn PropertyAccessor) -> Self {
Self {
accessor: handler,
properties: Some(properties),
}
}
pub fn partial(accessor: &'a dyn HeadersAccessor) -> Self {
Self {
accessor,
properties: None,
}
}
}
impl AttributesBinding for AccessorAttributesBinding<'_> {
fn extract_headers(&self) -> HashMap<String, String> {
self.accessor.headers().into_iter().collect()
}
fn extract_header(&self, key: &str) -> Option<String> {
self.accessor.header(key)
}
fn remote_address(&self) -> Option<String> {
self.properties.and_then(remote_address)
}
fn local_address(&self) -> Option<String> {
self.properties.and_then(local_address)
}
fn scheme(&self) -> Option<String> {
self.properties.and_then(scheme)
}
fn version(&self) -> Option<String> {
self.properties.and_then(version)
}
}
pub(crate) struct AttributesBindingAdapter<'a> {
delegate: &'a dyn AttributesBinding,
}
impl<'a> AttributesBindingAdapter<'a> {
pub fn new(delegate: &'a dyn AttributesBinding) -> Self {
Self { delegate }
}
pub fn extract_headers(&self) -> HashMap<String, PelValue> {
convert_maps(self.delegate.extract_headers())
}
pub fn extract_header(&self, key: &str) -> Option<PelValue> {
convert_string(self.delegate.extract_header(key))
}
pub fn extract_query_params(&self) -> HashMap<String, PelValue> {
convert_maps(self.delegate.extract_query_params())
}
pub fn method(&self) -> Option<PelValue> {
convert_string(self.delegate.method())
}
pub fn path(&self) -> Option<PelValue> {
convert_string(self.delegate.path())
}
pub fn uri(&self) -> Option<PelValue> {
convert_string(self.delegate.uri())
}
pub fn remote_address(&self) -> Option<PelValue> {
convert_string(self.delegate.remote_address())
}
pub fn local_address(&self) -> Option<PelValue> {
convert_string(self.delegate.local_address())
}
pub fn query_string(&self) -> Option<PelValue> {
convert_string(self.delegate.query_string())
}
pub fn scheme(&self) -> Option<PelValue> {
convert_string(self.delegate.scheme())
}
pub fn version(&self) -> Option<PelValue> {
convert_string(self.delegate.version())
}
pub fn status_code(&self) -> Option<PelValue> {
self.delegate
.status_code()
.map(|s| PelValue::number(s as f64))
}
}
fn fake_url(uri: String) -> Option<Url> {
Url::parse("http://fake_base").ok()?.join(uri.as_str()).ok()
}
fn convert_maps(map: HashMap<String, String>) -> HashMap<String, PelValue> {
map.into_iter()
.map(|(key, val)| (key, PelValue::string(val)))
.collect()
}
fn convert_string(val: Option<String>) -> Option<PelValue> {
val.map(PelValue::string)
}
fn remote_address(properties: &dyn PropertyAccessor) -> Option<String> {
properties
.read_property(SOURCE_ADDRESS)
.map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
}
fn local_address(properties: &dyn PropertyAccessor) -> Option<String> {
properties
.read_property(DESTINATION_ADDRESS)
.map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
}
fn scheme(properties: &dyn PropertyAccessor) -> Option<String> {
properties
.read_property(REQUEST_SCHEME)
.map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
}
fn version(properties: &dyn PropertyAccessor) -> Option<String> {
properties
.read_property(REQUEST_PROTOCOL)
.map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
}