use boa_engine::{
Context, JsData, JsResult, JsString, JsValue, boa_class,
builtins::iterable::create_iter_result_object, error::JsNativeError, interop::JsClass,
object::JsObject, object::builtins::JsArray,
};
use boa_gc::{Finalize, Trace};
use super::headers::JsHeaders;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum IterationKind {
Key,
Value,
KeyAndValue,
}
#[derive(Debug, Finalize, Trace, JsData)]
pub(crate) struct HeadersIterator {
iterated_headers: JsObject<JsHeaders>,
next_index: usize,
#[unsafe_ignore_trace]
iteration_kind: IterationKind,
}
#[boa_class(rename = "Headers Iterator")]
impl HeadersIterator {
#[boa(constructor)]
fn constructor() -> JsResult<Self> {
Err(JsNativeError::typ()
.with_message("Illegal constructor")
.into())
}
#[boa(method)]
fn next(&mut self, context: &mut Context) -> JsValue {
let item_kind = self.iteration_kind;
let element = self
.iterated_headers
.borrow()
.data()
.headers
.borrow()
.iter()
.nth(self.next_index)
.map(|(k, v)| {
(
JsValue::from(JsString::from(k.as_str())),
JsValue::from(JsString::from(v.to_str().unwrap_or(""))),
)
});
if let Some((key, value)) = element {
self.next_index += 1;
return match item_kind {
IterationKind::Key => create_iter_result_object(key, false, context),
IterationKind::Value => create_iter_result_object(value, false, context),
IterationKind::KeyAndValue => {
let result = JsArray::from_iter([key, value], context);
create_iter_result_object(result.into(), false, context)
}
};
}
create_iter_result_object(JsValue::undefined(), true, context)
}
#[boa(method)]
#[boa(symbol = "iterator")]
fn symbol_iterator(this: JsClass<Self>) -> JsValue {
this.inner().into()
}
}
impl HeadersIterator {
pub(crate) fn create_headers_iterator(
headers: JsObject<JsHeaders>,
kind: IterationKind,
context: &mut Context,
) -> JsResult<JsValue> {
let iter = Self {
iterated_headers: headers,
next_index: 0,
iteration_kind: kind,
};
let proto = context
.realm()
.get_class::<Self>()
.ok_or_else(|| boa_engine::js_error!(Error: "Headers Iterator not registered"))?
.prototype();
let headers_iterator = JsObject::from_proto_and_data(proto, iter);
Ok(headers_iterator.into())
}
}