kermit_derive/lib.rs
1//! Procedural macros for the Kermit workspace.
2//!
3//! Provides `#[derive(IntoTrieIter)]` to automatically implement
4//! [`IntoIterator`] for trie-iterator types, wrapping them in
5//! `TrieIteratorWrapper` so that `for tuple in iter { ... }` yields
6//! `Vec<usize>` tuples directly.
7//!
8//! # Example
9//!
10//! ```ignore
11//! use kermit_iters::{LinearIterator, TrieIterator, TrieIteratorWrapper};
12//! use kermit_derive::IntoTrieIter;
13//!
14//! #[derive(IntoTrieIter)]
15//! struct MyTrieIter<'a> {
16//! // ... fields ...
17//! }
18//!
19//! impl LinearIterator for MyTrieIter<'_> { /* ... */ }
20//! impl TrieIterator for MyTrieIter<'_> { /* ... */ }
21//!
22//! // The derive generates:
23//! // impl<'a> IntoIterator for MyTrieIter<'a> {
24//! // type Item = Vec<usize>;
25//! // type IntoIter = TrieIteratorWrapper<Self>;
26//! // fn into_iter(self) -> Self::IntoIter { TrieIteratorWrapper::new(self) }
27//! // }
28//! ```
29//!
30//! See `kermit-derive/tests/derive_into_trie_iter.rs` for a runnable example
31//! with a minimal mock trie, and `kermit-ds` for two production uses
32//! (`TreeTrieIter`, `ColumnTrieIter`).
33
34#![deny(missing_docs)]
35
36use {
37 proc_macro::TokenStream,
38 quote::quote,
39 syn::{parse_macro_input, DeriveInput, GenericParam},
40};
41
42/// Derives [`IntoIterator`] for a trie-iterator struct.
43///
44/// The annotated struct must:
45/// - implement `kermit_iters::TrieIterator` (and therefore
46/// `kermit_iters::LinearIterator`),
47/// - have exactly one generic parameter, and it must be a lifetime named `'a`.
48///
49/// The expanded impl wraps `self` in a `kermit_iters::TrieIteratorWrapper`,
50/// yielding each root-to-leaf path in the trie as a `Vec<usize>`.
51#[proc_macro_derive(IntoTrieIter)]
52pub fn derive_into_trie_iter(input: TokenStream) -> TokenStream {
53 let input = parse_macro_input!(input as DeriveInput);
54 let ident = &input.ident;
55
56 let params: Vec<_> = input.generics.params.iter().collect();
57 let valid =
58 matches!(params.as_slice(), [GenericParam::Lifetime(lt)] if lt.lifetime.ident == "a");
59 if !valid {
60 let msg = format!(
61 "#[derive(IntoTrieIter)] requires exactly one generic parameter, a lifetime named \
62 `'a`; found {} parameter(s) on `{}`",
63 params.len(),
64 ident
65 );
66 return quote! { compile_error!(#msg); }.into();
67 }
68
69 let output = quote! {
70
71 impl<'a> IntoIterator for #ident<'a> {
72 type Item = Vec<usize>;
73 type IntoIter = TrieIteratorWrapper<Self>;
74
75 fn into_iter(self) -> Self::IntoIter {
76 TrieIteratorWrapper::new(self)
77 }
78 }
79
80 };
81
82 output.into()
83}