cfgmatic-paths 2.0.1

Cross-platform configuration path discovery following XDG and platform conventions
Documentation
//! macOS GUI directory finder.

use super::DirectoryFinder;
use crate::env::Env;
use std::path::PathBuf;

/// macOS GUI application directory finder.
///
/// Uses `~/Library/Application Support/` and follows macOS conventions
/// for GUI applications with bundle IDs.
#[derive(Debug, Clone)]
pub struct MacOSGuiDirectoryFinder {
    /// macOS bundle identifier (e.g., "com.example.myapp").
    bundle_id: String,
}

impl MacOSGuiDirectoryFinder {
    /// Create a new finder for macOS GUI apps.
    pub fn new(bundle_id: impl Into<String>) -> Self {
        Self {
            bundle_id: bundle_id.into(),
        }
    }

    /// Get the Application Support directory.
    fn application_support(&self, env: &dyn Env) -> PathBuf {
        env.home_dir().map_or_else(
            || PathBuf::from("/Library/Application Support").join(&self.bundle_id),
            |h| {
                h.join("Library")
                    .join("Application Support")
                    .join(&self.bundle_id)
            },
        )
    }

    /// Get the Preferences directory.
    fn preferences(&self, env: &dyn Env) -> PathBuf {
        env.home_dir()
            .map_or_else(
                || PathBuf::from("/Library/Preferences"),
                |h| h.join("Library").join("Preferences"),
            )
            .join(format!("{}.plist", self.bundle_id))
    }
}

impl DirectoryFinder for MacOSGuiDirectoryFinder {
    fn user_dirs(&self, env: &dyn Env) -> Vec<PathBuf> {
        vec![
            // Application Support
            self.application_support(env),
            // Preferences (legacy plist)
            self.preferences(env),
        ]
    }

    fn local_dirs(&self, _env: &dyn Env) -> Vec<PathBuf> {
        // macOS doesn't have a strong concept of "local" (machine-specific)
        // outside of user directories
        Vec::new()
    }

    fn system_dirs(&self, _env: &dyn Env) -> Vec<PathBuf> {
        vec![
            // System-wide Application Support
            PathBuf::from("/Library/Application Support").join(&self.bundle_id),
            // Network (shared across domain)
            PathBuf::from("/Network/Library/Application Support").join(&self.bundle_id),
        ]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    struct TestEnv {
        home: PathBuf,
    }

    impl TestEnv {
        fn new() -> Self {
            Self {
                home: PathBuf::from("/Users/testuser"),
            }
        }
    }

    impl Env for TestEnv {
        fn get(&self, _key: &str) -> Option<String> {
            None
        }

        fn home_dir(&self) -> Option<PathBuf> {
            Some(self.home.clone())
        }
    }

    #[test]
    fn test_user_dirs() {
        let env = TestEnv::new();
        let finder = MacOSGuiDirectoryFinder::new("com.example.myapp");
        let dirs = finder.user_dirs(&env);

        assert_eq!(
            dirs[0],
            PathBuf::from("/Users/testuser/Library/Application Support/com.example.myapp")
        );
        assert_eq!(
            dirs[1],
            PathBuf::from("/Users/testuser/Library/Preferences/com.example.myapp.plist")
        );
    }

    #[test]
    fn test_system_dirs() {
        let env = TestEnv::new();
        let finder = MacOSGuiDirectoryFinder::new("com.example.myapp");
        let dirs = finder.system_dirs(&env);

        assert_eq!(
            dirs[0],
            PathBuf::from("/Library/Application Support/com.example.myapp")
        );
        assert_eq!(
            dirs[1],
            PathBuf::from("/Network/Library/Application Support/com.example.myapp")
        );
    }
}