comparison libpam-sys/libpam-sys-impls/src/lib.rs @ 110:2346fd501b7a

Add tests for constants and do other macro niceties. - Adds tests for all the constants. Pretty sweet. - Moves documentation for cfg-pam-impl macro to `libpam-sys`. - Renames `Illumos` to `Sun`. - other stuff
author Paul Fisher <paul@pfish.zone>
date Sun, 29 Jun 2025 02:15:46 -0400
parents bb465393621f
children 178310336596
comparison
equal deleted inserted replaced
109:bb465393621f 110:2346fd501b7a
1 use proc_macro as pm; 1 use proc_macro as pm;
2 use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree}; 2 use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
3 use quote::{format_ident, quote}; 3 use quote::{format_ident, quote};
4 use std::fmt::Display; 4 use std::fmt::Display;
5 use std::str::FromStr;
5 use syn::Lit; 6 use syn::Lit;
6 7
7 /// A `cfg`-like attribute macro for code specific to one PAM implementation. 8 // For documentation on this, see the `libpam-sys` crate.
8 ///
9 /// Different versions of PAM export different functions and have some
10 /// meaningful internal implementation differences, like the way `pam_conv`
11 /// is handled (see [the Linux-PAM man page for details][man7]).
12 ///
13 /// ```
14 /// # use libpam_sys_impls::cfg_pam_impl;
15 /// #[cfg_pam_impl("Illumos")]
16 /// fn do_something() { /* illumos-only code */ }
17 ///
18 /// #[cfg_pam_impl(not("Illumos"))]
19 /// fn do_something() { /* non-illumos code */ }
20 ///
21 /// #[cfg_pam_impl(any("LinuxPam", "MinimalOpenPam"))]
22 /// fn do_something_else() { /* Linux-PAM or minimal OpenPAM */ }
23 ///
24 /// #[cfg_pam_impl(not(any("Illumos", "OpenPam")))]
25 /// fn do_a_third_thing() { /* Neither Illumos nor OpenPAM */ }
26 ///
27 /// #[cfg_pam_impl(any())]
28 /// fn this_will_never_build() { /* why would you do this? */ }
29 ///
30 /// #[cfg_pam_impl(not(any()))]
31 /// fn this_will_always_build() { /* this is technically legal */ }
32 /// ```
33 ///
34 /// [man7]: https://man7.org/linux/man-pages/man3/pam_conv.3.html
35 #[proc_macro_attribute] 9 #[proc_macro_attribute]
36 pub fn cfg_pam_impl(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream { 10 pub fn cfg_pam_impl(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
37 Predicate::parse(attr.into(), None) 11 Predicate::parse(attr.into(), None)
38 .map(|p| { 12 .map(|p| {
39 if p.matches(pam_impl_str()) { 13 if p.matches(pam_impl_str()) {
43 } 17 }
44 }) 18 })
45 .unwrap_or_else(|e| syn::Error::from(e).into_compile_error().into()) 19 .unwrap_or_else(|e| syn::Error::from(e).into_compile_error().into())
46 } 20 }
47 21
48 /// Outputs the `PamImpl` enum and `LIBPAMSYS_IMPL` constant. Private. 22 /// Outputs the `PamImpl` enum and `LIBPAMSYS_IMPL` constant.
23 /// For use only in `libpam-sys`.
24 ///
25 /// The tokens passed into the macro are pasted immediately before the enum.
49 #[proc_macro] 26 #[proc_macro]
50 pub fn pam_impl_enum(data: pm::TokenStream) -> pm::TokenStream { 27 pub fn __pam_impl_enum__(data: pm::TokenStream) -> pm::TokenStream {
51 if !data.is_empty() {
52 panic!("unexpected stuff in pam_impl_enum!()")
53 }
54
55 let variant = format_ident!("{}", pam_impl_str()); 28 let variant = format_ident!("{}", pam_impl_str());
56 29 TokenStream::from_iter([
57 quote!( 30 data.into(),
58 /// The PAM implementations supported by `libpam-sys`. 31 TokenStream::from_str(include_str!(concat!(env!("OUT_DIR"), "/pam_impl_enum.rs"))).unwrap(),
59 #[non_exhaustive] 32 quote!(
60 #[derive(Clone, Copy, Debug, PartialEq)] 33 impl PamImpl {
61 pub enum PamImpl { 34 #[doc = concat!("The PAM implementation this was built for (currently `", stringify!(#variant), ")`.")]
62 /// [Linux-PAM] is provided by most Linux implementations. 35 pub const CURRENT: Self = Self::#variant;
63 /// 36 }
64 /// [Linux-PAM]: https://github.com/linux-pam/linux-pam 37 ),
65 LinuxPam, 38 ]).into()
66 /// [OpenPAM] is used by most BSD distributions, including Mac OS X. 39 }
67 /// 40
68 /// [OpenPAM]: https://git.des.dev/OpenPAM/OpenPAM 41 /// The name of the PAM implementation. For use only in `libpam-sys`.
69 OpenPam,
70 /// [Illumos PAM] is used on Illumos and Solaris systems.
71 ///
72 /// [Illumos PAM]: https://code.illumos.org/plugins/gitiles/illumos-gate/+/refs/heads/master/usr/src/lib/libpam
73 Illumos,
74 /// Only the functionality in [the PAM spec],
75 /// with OpenPAM/Illumos constants.
76 ///
77 /// [the PAM spec]: https://pubs.opengroup.org/onlinepubs/8329799/toc.htm
78 MinimalOpenPam,
79 }
80
81 #[doc = concat!("This version of libpam-sys was built for **", stringify!(#variant), "**.")]
82 pub const LIBPAMSYS_IMPL: PamImpl = PamImpl::#variant;
83 )
84 .into()
85 }
86
87 /// The name of the PAM implementation. Used only in `libpam-sys`. Private.
88 #[proc_macro] 42 #[proc_macro]
89 pub fn pam_impl_name(data: pm::TokenStream) -> pm::TokenStream { 43 pub fn __pam_impl_name__(data: pm::TokenStream) -> pm::TokenStream {
90 if !data.is_empty() { 44 if !data.is_empty() {
91 panic!("pam_impl_name! does not take any input") 45 panic!("pam_impl_name! does not take any input")
92 } 46 }
93 pm::TokenTree::Literal(pm::Literal::string(pam_impl_str())).into() 47 pm::TokenTree::Literal(pm::Literal::string(pam_impl_str())).into()
94 } 48 }
294 ($(($e:expr, ($($i:tt)*))),* $(,)?) => { 248 ($(($e:expr, ($($i:tt)*))),* $(,)?) => {
295 [$(($e, quote!($($i)*))),*] 249 [$(($e, quote!($($i)*))),*]
296 } 250 }
297 } 251 }
298 let matching = cases![ 252 let matching = cases![
299 ("Illumos", (any("Illumos", "OpenPam"))), 253 ("Sun", (any("Sun", "OpenPam"))),
300 ("OpenPam", (any("Illumos", "OpenPam"))), 254 ("OpenPam", (any("Sun", "OpenPam"))),
301 ("LinuxPam", (not("OpenPam"))), 255 ("LinuxPam", (not("OpenPam"))),
302 ("MinimalOpenPam", (not("OpenPam"))), 256 ("MinimalOpenPam", (not("OpenPam"))),
303 ("Other", (not(any("This", "That")))), 257 ("Other", (not(any("This", "That")))),
304 ("OpenPam", (not(not("OpenPam")))), 258 ("OpenPam", (not(not("OpenPam")))),
305 ("Anything", (not(any()))), 259 ("Anything", (not(any()))),
309 assert!(pred.matches(good)) 263 assert!(pred.matches(good))
310 } 264 }
311 265
312 let nonmatching = cases![ 266 let nonmatching = cases![
313 ("LinuxPam", (not("LinuxPam"))), 267 ("LinuxPam", (not("LinuxPam"))),
314 ("Illumos", ("LinuxPam")), 268 ("Sun", ("LinuxPam")),
315 ("OpenPam", (any("LinuxPam", "Illumos"))), 269 ("OpenPam", (any("LinuxPam", "Sun"))),
316 ("One", (not(any("One", "Another")))), 270 ("One", (not(any("One", "Another")))),
317 ("Negatory", (not(not("Affirmative")))), 271 ("Negatory", (not(not("Affirmative")))),
272 ("MinimalOpenPam", ("OpenPam")),
273 ("OpenPam", (("MinimalOpenPam"))),
318 ]; 274 ];
319 for (bad, tree) in nonmatching { 275 for (bad, tree) in nonmatching {
320 let pred = parse(tree); 276 let pred = parse(tree);
321 assert!(!pred.matches(bad)) 277 assert!(!pred.matches(bad))
322 } 278 }