use std::panic::Location;
use topcoat_core::{
context::{Cx, identity},
identity::SiteKey,
};
#[must_use]
#[track_caller]
pub fn element_id(cx: &Cx) -> String {
let identity = identity(cx).child(SiteKey::from_location(Location::caller()));
format!("tc-{identity}")
}
#[cfg(test)]
mod tests {
use topcoat_core::{context::with_identity, identity::Identity};
use super::*;
fn id_at_one_site(cx: &Cx) -> String {
element_id(cx)
}
#[test]
fn repeated_calls_at_one_site_return_the_same_id() {
let cx = Cx::default();
assert_eq!(id_at_one_site(&cx), id_at_one_site(&cx.clone()));
}
#[test]
fn different_sites_return_different_ids() {
let cx = Cx::default();
assert_ne!(element_id(&cx), element_id(&cx));
}
#[test]
fn keyed_contexts_return_different_ids() {
let cx = Cx::default();
let ids: Vec<_> = (0..3).map(|key| id_at_one_site(&cx.keyed(key))).collect();
assert_ne!(ids[0], ids[1]);
assert_ne!(ids[1], ids[2]);
assert_ne!(ids[0], ids[2]);
}
#[test]
fn ids_are_valid_css_identifiers() {
let cx = Cx::default();
for key in 0..64 {
let id = id_at_one_site(&cx.keyed(key));
assert!(id.starts_with(|c: char| c.is_ascii_alphabetic()), "{id}");
assert!(
id.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_'),
"{id}",
);
}
}
#[test]
#[should_panic(expected = "unkeyed-test-loop")]
fn an_ambiguous_identity_panics() {
let site = SiteKey::new(file!(), line!(), column!(), 0);
let cx = with_identity(
Cx::default(),
Identity::ROOT.ambiguous_child(site, "unkeyed-test-loop"),
);
let _ = element_id(&cx);
}
}