diff libpam-sys/src/pam_impl.rs @ 134:6c1e1bdb4164

Use standard #[cfg] directives rather than custom proc macros. Instead of having to do a bunch of custom parsing and other logic that tools often choke on, this change introduces an easy way to depend upon custom #[cfg]s provided by the libpam-sys crate.
author Paul Fisher <paul@pfish.zone>
date Thu, 03 Jul 2025 11:03:36 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpam-sys/src/pam_impl.rs	Thu Jul 03 11:03:36 2025 -0400
@@ -0,0 +1,121 @@
+/// An enum that knows its own values.
+macro_rules! self_aware_enum {
+        (
+            $(#[$enumeta:meta])*
+            $viz:vis enum $name:ident {
+                $(
+                    $(#[$itemeta:meta])*
+                    $item:ident,
+                )*
+            }
+        ) => {
+            $(#[$enumeta])*
+            $viz enum $name {
+                $(
+                    $(#[$itemeta])*
+                    $item,
+                )*
+            }
+
+            // The implementations in this block are private for now
+            // to avoid putting a contract into the public API.
+            #[allow(dead_code)]
+            impl $name {
+                /// Iterator over the items in the enum. For internal use.
+                fn items() -> Vec<Self> {
+                    vec![$(Self::$item),*]
+                }
+
+                /// Attempts to parse the enum from the string. For internal use.
+                fn try_from(value: &str) -> Result<Self, String> {
+                    match value {
+                        $(stringify!($item) => Ok(Self::$item),)*
+                        _ => Err(value.into()),
+                    }
+                }
+            }
+        };
+    }
+
+self_aware_enum! {
+    /// The PAM implementations supported by `libpam-sys`.
+    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+    #[cfg_attr(pam_impl, non_exhaustive)]
+    pub enum PamImpl {
+        /// [Linux-PAM] is provided by most Linux distributions.
+        ///
+        /// [Linux-PAM]: https://github.com/linux-pam/linux-pam
+        LinuxPam,
+        /// [OpenPAM] is used by most BSDs, including Mac OS X.
+        ///
+        /// [OpenPAM]: https://git.des.dev/OpenPAM/OpenPAM
+        OpenPam,
+        /// Illumos and Solaris use a derivative of [Sun's implementation][sun].
+        ///
+        /// [sun]: https://code.illumos.org/plugins/gitiles/illumos-gate/+/refs/heads/master/usr/src/lib/libpam
+        Sun,
+        /// Only the functionality and constants in [the PAM spec].
+        ///
+        /// [the PAM spec]: https://pubs.opengroup.org/onlinepubs/8329799/toc.htm
+        XSso,
+    }
+}
+
+#[cfg(pam_impl)]
+include!(concat!(env!("OUT_DIR"), "/pam_impl_const.rs"));
+
+/// Generates `cargo` directives for build scripts to enable `cfg(pam_impl)`.
+///
+/// Print this in your `build.rs` script to be able to use the custom `pam_impl`
+/// configuration directive.
+///
+/// ```
+/// // build.rs
+/// use libpam_sys::pam_impl;
+/// fn main() {
+///     println!("{}", pam_impl::enable_pam_impl_cfg())
+///
+///     // Whatever other stuff you do in your build.rs.
+/// }
+/// ```
+///
+/// This will set the current `pam_impl` as well as registering all known
+/// PAM implementations with `rustc-check-cfg` so you can easily use them:
+///
+/// ```
+/// #[cfg(pam_impl = "OpenPam")]
+/// fn openpam_specific_func(handle: *const libpam_sys::pam_handle) {
+///     let environ = libpam_sys::pam_getenvlist(handle);
+///     // ...
+///     libpam_sys::openpam_free_envlist()
+/// }
+///
+/// // This will give you a warning since "UnknownImpl" is not in the cfg.
+/// #[cfg(not(pam_impl = "UnknownImpl"))]
+/// fn do_something() {
+///     // ...
+/// }
+/// ```
+#[cfg(pam_impl)]
+pub fn enable_pam_impl_cfg() -> String {
+    format!("{}\n{}", checkcfg(), implcfg(PamImpl::CURRENT_STR))
+}
+
+/// Generates the `cargo:rustc-check-cfg` directive to print in build scripts.
+fn checkcfg() -> String {
+    let impls: Vec<_> = PamImpl::items()
+        .into_iter()
+        .map(|i| format!(r#""{i:?}""#))
+        .collect();
+    format!(
+        "\
+            cargo:rustc-check-cfg=cfg(pam_impl)\n\
+            cargo:rustc-check-cfg=cfg(pam_impl, values({impls}))\
+        ",
+        impls = impls.join(",")
+    )
+}
+
+fn implcfg(name: &str) -> String {
+    format!("cargo:rustc-cfg=pam_impl\ncargo:rustc-cfg=pam_impl={name:?}")
+}