Repository: awslabs/llrt
File: modules/llrt_fetch/src/headers.rs (Rust)

Code before:
        )
    }
}

impl<'js> CustomInspect<'js> for Headers {
    fn custom_inspect(&self, ctx: Ctx<'js>) -> Result<Object<'js>> {
        let obj = Object::new(ctx)?;
        for (k, v) in self.headers.iter() {
            obj.set(k.as_ref(), v.as_ref())?;
        }

        Ok(obj)
    }
}

fn coerce_to_string<'js>(ctx: &Ctx<'js>, value: Value<'js>) -> Result<String> {
    if value.is_null() {
        Ok("null".to_string())
    } else if let Some(s) = value.as_string() {
        Ok(s.to_string()?)
    } else {
        // fallback: try JSON.stringify or [object Object]
        let global = ctx.globals();
        let string_ctor: Function = global.get("String")?;
        let result: String = string_ctor.call((value,))?;
        Ok(result)
    }
}

fn is_http_header_name(name: &str) -> bool {
    if name.is_empty() {
        return false;
    }

    // 3.2.6.  Field Value Components
    // https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
    name.bytes().all(|b| {
        matches!(b,
            b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' |
            b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~' |
            b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z'
        )
    })
}

fn is_http_header_value(value: &str) -> bool {
    let bytes = value.as_bytes();

    // Reject leading/trailing SP or HTAB
    if bytes.first().is_some_and(|&b| b == 0x20 || b == 0x09) {
        return false;

Review comment (suggestion):
There is a Coerce type we can use instead (see environment.rs for instance). If not use the primordials to avoid accessors

Code after:

impl<'js> IteratorDef<'js> for Headers {
    fn js_entries(&self, ctx: Ctx<'js>) -> Result<Array<'js>> {
        map_to_entries(
            &ctx,
            self.headers.iter().map(|(k, v)| (k.as_ref(), v.as_ref())),
        )
    }
}

impl<'js> CustomInspect<'js> for Headers {
    fn custom_inspect(&self, ctx: Ctx<'js>) -> Result<Object<'js>> {
        let obj = Object::new(ctx)?;
        for (k, v) in self.headers.iter() {
            obj.set(k.as_ref(), v.as_ref())?;
        }

        Ok(obj)
    }
}

fn coerce_to_string<'js>(ctx: &Ctx<'js>, value: Value<'js>) -> Result<String> {
    if value.is_null() {
        Ok("null".into())
    } else if value.is_undefined() {
        Ok("undefined".into())
    } else if let Some(v) = value.as_bool() {
        Ok(v.to_string())
    } else if let Some(v) = value.as_number() {
        Ok(v.to_string())
    } else if let Some(s) = value.as_string() {
        s.to_string()
    } else {
        // fallback: try JSON.stringify or [object Object]
        let base_primordials = BasePrimordials::get(ctx)?;
        base_primordials.constructor_string.construct((value,))
    }
}

fn is_http_header_name(name: &str) -> bool {
    if name.is_empty() {
        return false;
    }

    // 3.2.6.  Field Value Components
    // https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
    name.bytes().all(|b| {
        matches!(b,
            b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' |
            b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~' |
            b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z'