use scattered_collect::{ScatteredMap, gather, scatter};
trait Command: Send + Sync {
fn execute(&self);
}
#[gather]
static COMMANDS: ScatteredMap<&'static str, &'static dyn Command>;
const fn ascii_lowercase<const N: usize>(s: &str) -> [u8; N] {
let src = s.as_bytes();
let mut out = [0u8; N];
let mut i = 0;
while i < N {
out[i] = src[i].to_ascii_lowercase();
i += 1;
}
out
}
macro_rules! register_commands {
($($handle:ident: $type:ident),* $(,)?) => {
$(
#[scatter(COMMANDS)]
static $handle: (&'static str, &'static dyn Command) = {
const LEN: usize = stringify!($type).len();
const KEY_BYTES: [u8; LEN] = ascii_lowercase::<LEN>(stringify!($type));
const KEY: &str = match ::core::str::from_utf8(&KEY_BYTES) {
Ok(key) => key,
Err(_) => panic!("command name was not valid UTF-8"),
};
(KEY, &$type)
};
)*
};
}
struct Ping;
impl Command for Ping {
fn execute(&self) {
println!("Ping executed");
}
}
struct Pong;
impl Command for Pong {
fn execute(&self) {
println!("Pong executed");
}
}
register_commands!(
PING: Ping,
PONG: Pong,
);
fn main() {
for name in ["ping", "pong"] {
let command = COMMANDS.get(name).expect("command not found");
print!("{name}: ");
command.execute();
}
PING.value.execute();
PONG.value.execute();
}