#[macro_export]
macro_rules! known_idents {
(
$( $name:ident ),* $(,)?
$(
{ $( $const_name:ident = $string:literal ),+ $(,)? }
)?
) => {
$crate::known_idents! {
@impl
file_id = $crate::KNOWN_IDENTS_FILE_ID,
ctor = ($crate::Ident::new_const),
$( $name ),*
$( { $( $const_name = $string ),+ } )?
}
};
(
@impl
file_id = $file_id:expr,
ctor = ($ctor:path),
$( $name:ident ),* $(,)?
$(
{ $( $const_name:ident = $string:literal ),+ $(,)? }
)?
) => {
::paste::paste! {
$(
#[allow(dead_code)]
pub const [<$name:upper>]: $crate::SpannedIdent =
__known_spanned($ctor(stringify!($name)));
)*
$(
$(
#[allow(dead_code)]
pub const $const_name: $crate::SpannedIdent =
__known_spanned($ctor($string));
)+
)?
const fn __known_spanned(
ident: $crate::Ident,
) -> $crate::SpannedIdent {
match try_resolve_spanned(ident) {
::core::option::Option::Some(spanned) => spanned,
::core::option::Option::None => {
$crate::SpannedIdent::new(ident, $crate::Span::unknown())
}
}
}
pub const KNOWN_IDENT_KEYS: &[$crate::Ident] = &[
$( $ctor(stringify!($name)), )*
$( $( $ctor($string), )+ )?
];
pub const KNOWN_IDENT_STRS: &[&str] = &[
$( stringify!($name), )*
$( $( $string, )+ )?
];
pub const BLOB: &str = concat!(
$( stringify!($name), )*
$( $( $string, )+ )?
);
pub const fn try_resolve_spanned(
ident: $crate::Ident,
) -> Option<$crate::SpannedIdent> {
let mut i = 0;
let mut offset = 0u32;
while i < KNOWN_IDENT_KEYS.len() {
let len = KNOWN_IDENT_STRS[i].len() as u32;
if KNOWN_IDENT_KEYS[i].const_eq(ident) {
let span = unsafe {
$crate::Span::new_inline_unchecked(
$file_id,
0,
offset,
len,
)
};
return Some($crate::SpannedIdent::new(
KNOWN_IDENT_KEYS[i],
span,
));
}
offset += len;
i += 1;
}
None
}
#[allow(dead_code)]
pub const fn try_resolve(ident: $crate::Ident) -> Option<&'static str> {
let mut i = 0;
while i < KNOWN_IDENT_KEYS.len() {
if KNOWN_IDENT_KEYS[i].const_eq(ident) {
return Some(KNOWN_IDENT_STRS[i]);
}
i += 1;
}
None
}
}
};
}
macro_rules! known_idents_internal {
( $( $tt:tt )* ) => {
$crate::known_idents! {
@impl
file_id = $crate::LABURNUM_INTERNAL_KNOWN_IDENTS_FILE_ID,
ctor = ($crate::Ident::new_const_internal),
$( $tt )*
}
};
}
pub(crate) use known_idents_internal;
#[cfg(test)]
mod tests {
mod ki {
crate::known_idents! {
theme,
palette,
{
SELF_KW = "self",
COLOR_HEX = "ColorHex",
}
}
}
use crate::{Ident, KNOWN_IDENTS_FILE_ID, SpanCache};
#[test]
fn blob_concatenates_in_layout_order() {
assert_eq!(ki::BLOB, "themepaletteselfColorHex");
}
#[test]
fn spanned_resolution_uses_const_offsets() {
let cache = unsafe { SpanCache::new(KNOWN_IDENTS_FILE_ID, 0) };
let ident = ki::PALETTE.ident();
let span = ki::PALETTE.span();
assert_eq!(ident, Ident::new("palette"));
assert_eq!(span.file_id(), KNOWN_IDENTS_FILE_ID);
let data = span.data(&cache).unwrap();
assert_eq!(data.start, 5);
assert_eq!(data.len, 7);
assert_eq!(span.text(&cache, ki::BLOB), Some("palette"));
let self_span = ki::SELF_KW.span();
assert_eq!(self_span.text(&cache, ki::BLOB), Some("self"));
}
#[test]
fn text_resolution_and_misses() {
assert_eq!(ki::try_resolve(ki::COLOR_HEX.ident()), Some("ColorHex"));
assert_eq!(ki::try_resolve(Ident::new("nope")), None);
assert!(ki::try_resolve_spanned(Ident::new("nope")).is_none());
}
#[test]
fn known_idents_compose_as_a_const() {
const PALETTE_SPANNED: crate::SpannedIdent = ki::PALETTE;
assert_eq!(PALETTE_SPANNED.ident(), Ident::new("palette"));
}
}