#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct HostObject {
pub class_name: &'static str,
pub marker: &'static str,
pub is_boxed_primitive: bool,
}
const fn host(class_name: &'static str, marker: &'static str) -> HostObject {
HostObject {
class_name,
marker,
is_boxed_primitive: false,
}
}
const fn boxed(class_name: &'static str, marker: &'static str) -> HostObject {
HostObject {
class_name,
marker,
is_boxed_primitive: true,
}
}
pub const HOST_OBJECTS: &[HostObject] = &[
host("ArrayBuffer", "__smelt_arraybuffer"),
host("SharedArrayBuffer", "__smelt_sharedarraybuffer"),
host("Buffer", "__smelt_buffer"),
host("DataView", "__smelt_dataview"),
host("WeakMap", "__smelt_weakmap"),
host("WeakSet", "__smelt_weakset"),
host("File", "__smelt_file"),
host("Blob", "__smelt_blob"),
host("DOMException", "__smelt_domexception"),
boxed("Number", "__smelt_number"),
boxed("Boolean", "__smelt_boolean"),
boxed("String", "__smelt_string"),
boxed("Symbol", "__smelt_symbol"),
];
#[must_use]
pub fn host_object_by_class(class_name: &str) -> Option<&'static HostObject> {
HOST_OBJECTS
.iter()
.find(|entry| entry.class_name == class_name)
}
#[must_use]
pub fn host_object_marker(class_name: &str) -> Option<&'static str> {
host_object_by_class(class_name).map(|entry| entry.marker)
}
pub fn host_object_markers() -> impl Iterator<Item = &'static str> {
HOST_OBJECTS.iter().map(|entry| entry.marker)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn markers_are_unique_and_prefixed() {
let mut seen = std::collections::HashSet::new();
for entry in HOST_OBJECTS {
assert!(
entry.marker.starts_with("__smelt_"),
"marker `{}` for `{}` must be `__smelt_`-prefixed",
entry.marker,
entry.class_name,
);
assert!(
seen.insert(entry.marker),
"duplicate host-object marker `{}`",
entry.marker,
);
}
}
#[test]
fn lookups_round_trip() {
for entry in HOST_OBJECTS {
assert_eq!(host_object_by_class(entry.class_name), Some(entry));
assert_eq!(host_object_marker(entry.class_name), Some(entry.marker));
}
assert_eq!(host_object_by_class("NotAHostObject"), None);
assert_eq!(host_object_marker("NotAHostObject"), None);
}
#[test]
fn boxed_primitive_wrappers_are_classified() {
let boxed = HOST_OBJECTS
.iter()
.filter(|entry| entry.is_boxed_primitive)
.map(|entry| entry.class_name)
.collect::<std::collections::HashSet<_>>();
assert_eq!(
boxed,
["Number", "Boolean", "String", "Symbol"]
.into_iter()
.collect(),
);
}
#[test]
fn markers_iterator_covers_boxed_primitives() {
let markers = host_object_markers().collect::<std::collections::HashSet<_>>();
for expected in ["__smelt_boolean", "__smelt_string", "__smelt_number"] {
assert!(
markers.contains(expected),
"runtime host-marker set must include `{expected}`",
);
}
}
}