openauth-telemetry 0.0.4

Telemetry support for OpenAuth.
Documentation
//! Rust package manager detection.

use crate::types::DetectionInfo;

pub fn detect_package_manager() -> Option<DetectionInfo> {
    let cargo_manifest_present = std::env::var_os("CARGO_MANIFEST_DIR").is_some()
        || std::env::current_dir()
            .ok()
            .is_some_and(|dir| dir.join("Cargo.toml").exists());
    let cargo_version = std::env::var("CARGO_VERSION").ok();
    detect_cargo_package_manager(cargo_manifest_present, cargo_version.as_deref())
}

fn detect_cargo_package_manager(
    cargo_manifest_present: bool,
    cargo_version: Option<&str>,
) -> Option<DetectionInfo> {
    cargo_manifest_present.then(|| DetectionInfo {
        name: "cargo".to_owned(),
        version: cargo_version
            .filter(|version| !version.is_empty())
            .unwrap_or("unknown")
            .to_owned(),
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::{Mutex, MutexGuard, OnceLock};

    struct EnvRestore(Vec<(&'static str, Option<String>)>);

    impl EnvRestore {
        fn unset(keys: &[&'static str]) -> Self {
            let saved = keys
                .iter()
                .map(|key| (*key, std::env::var(key).ok()))
                .collect::<Vec<_>>();
            for key in keys {
                std::env::remove_var(key);
            }
            Self(saved)
        }
    }

    impl Drop for EnvRestore {
        fn drop(&mut self) {
            for (key, value) in &self.0 {
                match value {
                    Some(value) => std::env::set_var(key, value),
                    None => std::env::remove_var(key),
                }
            }
        }
    }

    fn env_lock() -> &'static Mutex<()> {
        static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
        LOCK.get_or_init(|| Mutex::new(()))
    }

    fn lock_env() -> MutexGuard<'static, ()> {
        env_lock()
            .lock()
            .unwrap_or_else(|poisoned| poisoned.into_inner())
    }

    #[test]
    fn detects_cargo_when_rust_manifest_env_exists() {
        assert_eq!(
            detect_cargo_package_manager(true, Some("1.85.0")),
            Some(DetectionInfo {
                name: "cargo".to_owned(),
                version: "1.85.0".to_owned(),
            })
        );
    }

    #[test]
    fn detects_unknown_cargo_version_when_version_env_is_empty() {
        assert_eq!(
            detect_cargo_package_manager(true, Some("")),
            Some(DetectionInfo {
                name: "cargo".to_owned(),
                version: "unknown".to_owned(),
            })
        );
    }

    #[test]
    fn detects_cargo_from_process_env() {
        let _guard = lock_env();
        let _restore = EnvRestore::unset(&["CARGO_MANIFEST_DIR", "CARGO_VERSION"]);
        std::env::set_var("CARGO_MANIFEST_DIR", env!("CARGO_MANIFEST_DIR"));
        std::env::set_var("CARGO_VERSION", "1.85.0");

        assert_eq!(
            detect_package_manager(),
            Some(DetectionInfo {
                name: "cargo".to_owned(),
                version: "1.85.0".to_owned(),
            })
        );
    }
}