pdk_script/bindings/
attributes.rs1use crate::constants::{
6 DESTINATION_ADDRESS, METHOD_HEADER, PATH_HEADER, REQUEST_PROTOCOL, REQUEST_SCHEME,
7 SCHEME_HEADER, SOURCE_ADDRESS, STATUS_CODE_HEADER,
8};
9use classy::event::HeadersAccessor;
10use classy::hl::{HeadersHandler, HttpClientResponse, PropertyAccessor};
11use pel::runtime::value::Value as PelValue;
12use std::collections::HashMap;
13use url::Url;
14
15pub trait AttributesBinding {
17 fn extract_headers(&self) -> HashMap<String, String>;
19
20 fn extract_header(&self, key: &str) -> Option<String>;
22
23 fn extract_query_params(&self) -> HashMap<String, String> {
25 self.extract_header(PATH_HEADER)
26 .and_then(fake_url)
27 .map(|url| {
28 url.query_pairs()
29 .map(|(k, v)| (k.to_string(), v.to_string()))
30 .collect()
31 })
32 .unwrap_or_default()
33 }
34
35 fn method(&self) -> Option<String> {
37 self.extract_header(METHOD_HEADER)
38 }
39
40 fn path(&self) -> Option<String> {
42 let mut url = self.extract_header(PATH_HEADER).and_then(fake_url)?;
43 url.set_query(None);
44 Some(url.path().to_string())
45 }
46
47 fn uri(&self) -> Option<String> {
49 self.extract_header(PATH_HEADER)
50 }
51
52 fn remote_address(&self) -> Option<String> {
54 None
55 }
56
57 fn local_address(&self) -> Option<String> {
59 None
60 }
61
62 fn query_string(&self) -> Option<String> {
64 let path = self.extract_header(PATH_HEADER)?;
65 fake_url(path)?.query().map(str::to_string)
66 }
67
68 fn scheme(&self) -> Option<String> {
70 self.extract_header(SCHEME_HEADER)
71 }
72
73 fn version(&self) -> Option<String> {
75 None
76 }
77
78 fn status_code(&self) -> Option<u32> {
80 self.extract_header(STATUS_CODE_HEADER)
81 .and_then(|value| value.parse::<u32>().ok())
82 }
83}
84
85impl AttributesBinding for HttpClientResponse {
86 fn extract_headers(&self) -> HashMap<String, String> {
87 self.headers().clone()
88 }
89
90 fn extract_header(&self, key: &str) -> Option<String> {
91 self.header(key).cloned()
92 }
93}
94
95pub struct HandlerAttributesBinding<'a> {
97 handler: &'a dyn HeadersHandler,
98 properties: Option<&'a dyn PropertyAccessor>,
99}
100
101impl<'a> HandlerAttributesBinding<'a> {
102 pub fn new(handler: &'a dyn HeadersHandler, properties: &'a dyn PropertyAccessor) -> Self {
104 Self {
105 handler,
106 properties: Some(properties),
107 }
108 }
109
110 pub fn partial(handler: &'a dyn HeadersHandler) -> Self {
112 Self {
113 handler,
114 properties: None,
115 }
116 }
117}
118
119impl AttributesBinding for HandlerAttributesBinding<'_> {
120 fn extract_headers(&self) -> HashMap<String, String> {
121 self.handler.headers().into_iter().collect()
122 }
123
124 fn extract_header(&self, key: &str) -> Option<String> {
125 self.handler.header(key)
126 }
127
128 fn remote_address(&self) -> Option<String> {
129 self.properties.and_then(remote_address)
130 }
131
132 fn local_address(&self) -> Option<String> {
133 self.properties.and_then(local_address)
134 }
135
136 fn scheme(&self) -> Option<String> {
137 self.properties.and_then(scheme)
138 }
139
140 fn version(&self) -> Option<String> {
141 self.properties.and_then(version)
142 }
143}
144
145#[doc(hidden)]
147pub struct AccessorAttributesBinding<'a> {
148 accessor: &'a dyn HeadersAccessor,
149 properties: Option<&'a dyn PropertyAccessor>,
150}
151
152impl<'a> AccessorAttributesBinding<'a> {
153 pub fn new(handler: &'a dyn HeadersAccessor, properties: &'a dyn PropertyAccessor) -> Self {
155 Self {
156 accessor: handler,
157 properties: Some(properties),
158 }
159 }
160
161 pub fn partial(accessor: &'a dyn HeadersAccessor) -> Self {
163 Self {
164 accessor,
165 properties: None,
166 }
167 }
168}
169
170impl AttributesBinding for AccessorAttributesBinding<'_> {
171 fn extract_headers(&self) -> HashMap<String, String> {
172 self.accessor.headers().into_iter().collect()
173 }
174
175 fn extract_header(&self, key: &str) -> Option<String> {
176 self.accessor.header(key)
177 }
178
179 fn remote_address(&self) -> Option<String> {
180 self.properties.and_then(remote_address)
181 }
182
183 fn local_address(&self) -> Option<String> {
184 self.properties.and_then(local_address)
185 }
186
187 fn scheme(&self) -> Option<String> {
188 self.properties.and_then(scheme)
189 }
190
191 fn version(&self) -> Option<String> {
192 self.properties.and_then(version)
193 }
194}
195
196pub(crate) struct AttributesBindingAdapter<'a> {
197 delegate: &'a dyn AttributesBinding,
198}
199
200impl<'a> AttributesBindingAdapter<'a> {
201 pub fn new(delegate: &'a dyn AttributesBinding) -> Self {
202 Self { delegate }
203 }
204
205 pub fn extract_headers(&self) -> HashMap<String, PelValue> {
206 convert_maps(self.delegate.extract_headers())
207 }
208
209 pub fn extract_header(&self, key: &str) -> Option<PelValue> {
210 convert_string(self.delegate.extract_header(key))
211 }
212
213 pub fn extract_query_params(&self) -> HashMap<String, PelValue> {
214 convert_maps(self.delegate.extract_query_params())
215 }
216
217 pub fn method(&self) -> Option<PelValue> {
218 convert_string(self.delegate.method())
219 }
220
221 pub fn path(&self) -> Option<PelValue> {
222 convert_string(self.delegate.path())
223 }
224
225 pub fn uri(&self) -> Option<PelValue> {
226 convert_string(self.delegate.uri())
227 }
228
229 pub fn remote_address(&self) -> Option<PelValue> {
230 convert_string(self.delegate.remote_address())
231 }
232
233 pub fn local_address(&self) -> Option<PelValue> {
234 convert_string(self.delegate.local_address())
235 }
236
237 pub fn query_string(&self) -> Option<PelValue> {
238 convert_string(self.delegate.query_string())
239 }
240
241 pub fn scheme(&self) -> Option<PelValue> {
242 convert_string(self.delegate.scheme())
243 }
244
245 pub fn version(&self) -> Option<PelValue> {
246 convert_string(self.delegate.version())
247 }
248
249 pub fn status_code(&self) -> Option<PelValue> {
250 self.delegate
251 .status_code()
252 .map(|s| PelValue::number(s as f64))
253 }
254}
255
256fn fake_url(uri: String) -> Option<Url> {
257 Url::parse("http://fake_base").ok()?.join(uri.as_str()).ok()
258}
259fn convert_maps(map: HashMap<String, String>) -> HashMap<String, PelValue> {
260 map.into_iter()
261 .map(|(key, val)| (key, PelValue::string(val)))
262 .collect()
263}
264
265fn convert_string(val: Option<String>) -> Option<PelValue> {
266 val.map(PelValue::string)
267}
268
269fn remote_address(properties: &dyn PropertyAccessor) -> Option<String> {
270 properties
271 .read_property(SOURCE_ADDRESS)
272 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
273}
274
275fn local_address(properties: &dyn PropertyAccessor) -> Option<String> {
276 properties
277 .read_property(DESTINATION_ADDRESS)
278 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
279}
280
281fn scheme(properties: &dyn PropertyAccessor) -> Option<String> {
282 properties
283 .read_property(REQUEST_SCHEME)
284 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
285}
286
287fn version(properties: &dyn PropertyAccessor) -> Option<String> {
288 properties
289 .read_property(REQUEST_PROTOCOL)
290 .map(|bytes| String::from_utf8_lossy(bytes.as_slice()).to_string())
291}