libtmux_macros/lib.rs
1//! Derive macros for libtmux filter expressions.
2//!
3//! Start from the [`libtmux` documentation][libtmux]: this crate is the
4//! implementation of its `derive` feature, and a caller does not depend on it
5//! directly.
6//!
7//! [libtmux]: https://docs.rs/libtmux
8
9use proc_macro::TokenStream;
10use syn::{DeriveInput, parse_macro_input};
11
12mod expand;
13mod model;
14mod parse;
15
16use expand::expand_filterable;
17
18/// Derive a stable typed filter schema for a named struct.
19///
20/// The required `target` is the stable name embedded in portable expressions.
21/// Every supported field produces one member of the generated `<Type>Fields`
22/// companion. Bring `renamed_libtmux::query::Filterable` into scope to call
23/// `filter_fields`.
24///
25/// Enable the core crate's `derive` feature and use its root `Filterable`
26/// re-export. A deliberate direct dependency on `libtmux-macros` must exactly
27/// match the `libtmux` version because the expansion uses a hidden core ABI.
28///
29/// # Examples
30///
31/// ```
32/// use renamed_libtmux::Filterable;
33/// use renamed_libtmux::query::{Filterable as _, QueryIteratorExt as _};
34///
35/// #[derive(Filterable)]
36/// #[filterable(target = "task")]
37/// struct Task {
38/// name: String,
39/// done: bool,
40/// }
41///
42/// let values = vec![
43/// Task { name: "build".into(), done: false },
44/// Task { name: "test".into(), done: true },
45/// ];
46/// let fields = Task::filter_fields();
47/// let expression = fields.name.contains("ui").and(fields.done.eq(false));
48/// let selected = values.iter().matching(&expression).collect::<Vec<_>>();
49/// assert_eq!(selected.len(), 1);
50/// ```
51#[proc_macro_derive(Filterable, attributes(filterable))]
52pub fn derive_filterable(input: TokenStream) -> TokenStream {
53 let input = parse_macro_input!(input as DeriveInput);
54 expand_filterable(&input)
55 .unwrap_or_else(syn::Error::into_compile_error)
56 .into()
57}