apm-core 0.1.14

Core library for APM — a git-native project manager for parallel AI coding agents.
Documentation
pub fn resolve(name: &str, keychain_service: Option<&str>) -> anyhow::Result<String> {
    if let Ok(v) = std::env::var(name) {
        if !v.is_empty() {
            return Ok(v);
        }
    }
    #[cfg(target_os = "macos")]
    if let Some(service) = keychain_service {
        let out = std::process::Command::new("security")
            .args(["find-generic-password", "-s", service, "-w"])
            .output()?;
        if out.status.success() {
            let val = String::from_utf8(out.stdout)?.trim().to_string();
            if !val.is_empty() {
                return Ok(val);
            }
        }
    }
    #[cfg(not(target_os = "macos"))]
    let _ = keychain_service;
    anyhow::bail!("credential {:?} not found in environment or keychain", name);
}

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

    static ENV_LOCK: Mutex<()> = Mutex::new(());

    #[test]
    fn prefers_env_var() {
        let _g = ENV_LOCK.lock().unwrap();
        std::env::set_var("TEST_CRED_0038", "from-env");
        let result = resolve("TEST_CRED_0038", Some("some-service"));
        std::env::remove_var("TEST_CRED_0038");
        assert_eq!(result.unwrap(), "from-env");
    }

    #[test]
    fn empty_env_var_is_skipped() {
        let _g = ENV_LOCK.lock().unwrap();
        std::env::set_var("TEST_CRED_0038_EMPTY", "");
        let result = resolve("TEST_CRED_0038_EMPTY", None);
        std::env::remove_var("TEST_CRED_0038_EMPTY");
        assert!(result.is_err());
    }

    #[test]
    fn missing_credential_fails() {
        let _g = ENV_LOCK.lock().unwrap();
        std::env::remove_var("TEST_CRED_0038_MISSING");
        let result = resolve("TEST_CRED_0038_MISSING", None);
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("TEST_CRED_0038_MISSING"));
    }
}