Skip to main content

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}