pixi_outdated/
lockfile.rs

1use anyhow::{Context, Result};
2use rattler_lock::LockFile;
3use std::path::Path;
4
5/// Read platforms from the pixi.lock file for a specific environment
6pub fn get_platforms_from_lockfile(
7    manifest_path: Option<&str>,
8    environment: Option<&str>,
9) -> Result<Vec<String>> {
10    // Find the lockfile path
11    let lockfile_path = if let Some(manifest) = manifest_path {
12        let manifest_dir = Path::new(manifest)
13            .parent()
14            .context("Failed to get manifest directory")?;
15        manifest_dir.join("pixi.lock")
16    } else {
17        Path::new("pixi.lock").to_path_buf()
18    };
19
20    // Read the lockfile
21    let lockfile = LockFile::from_path(&lockfile_path)
22        .with_context(|| format!("Failed to read lockfile at {}", lockfile_path.display()))?;
23
24    // Find the specified environment (or use default)
25    let env_name = environment.unwrap_or("default");
26
27    let (_name, env) = lockfile
28        .environments()
29        .find(|(name, _env)| *name == env_name)
30        .with_context(|| format!("Environment '{}' not found in lockfile", env_name))?;
31
32    // Extract platforms from the environment
33    let platforms: Vec<String> = env.platforms().map(|p| p.to_string()).collect();
34
35    if platforms.is_empty() {
36        anyhow::bail!("No platforms found for environment '{}'", env_name);
37    }
38
39    Ok(platforms)
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use std::fs;
46    use tempfile::TempDir;
47
48    fn get_example_manifest_path() -> String {
49        // Get the path to the example manifest relative to the project root
50        let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
51            .expect("CARGO_MANIFEST_DIR should be set during tests");
52        format!("{}/examples/pixi.toml", manifest_dir)
53    }
54
55    #[test]
56    fn test_get_platforms_from_example_project() {
57        // Test with the actual example project
58        let manifest_path = get_example_manifest_path();
59        let result = get_platforms_from_lockfile(Some(&manifest_path), None);
60
61        assert!(result.is_ok());
62        let platforms = result.unwrap();
63
64        assert!(platforms.len() >= 2);
65        assert!(platforms.contains(&"linux-64".to_string()));
66        assert!(platforms.contains(&"osx-arm64".to_string()));
67    }
68
69    #[test]
70    fn test_get_platforms_missing_env() {
71        // Use the example project but request a non-existent environment
72        let manifest_path = get_example_manifest_path();
73        let result = get_platforms_from_lockfile(Some(&manifest_path), Some("nonexistent"));
74
75        assert!(result.is_err());
76        let error_msg = result.unwrap_err().to_string();
77        assert!(
78            error_msg.contains("Environment 'nonexistent' not found"),
79            "Expected error message to contain \"Environment 'nonexistent' not found\", but got: {}",
80            error_msg
81        );
82    }
83
84    #[test]
85    fn test_get_platforms_missing_lockfile() {
86        let temp_dir = TempDir::new().unwrap();
87        let manifest_path = temp_dir.path().join("pixi.toml");
88        fs::write(&manifest_path, "").unwrap();
89
90        let result = get_platforms_from_lockfile(Some(manifest_path.to_str().unwrap()), None);
91
92        assert!(result.is_err());
93        assert!(result
94            .unwrap_err()
95            .to_string()
96            .contains("Failed to read lockfile"));
97    }
98
99    #[test]
100    fn test_get_platforms_invalid_lockfile() {
101        let temp_dir = TempDir::new().unwrap();
102        let manifest_path = temp_dir.path().join("pixi.toml");
103        let lockfile_path = temp_dir.path().join("pixi.lock");
104
105        fs::write(&manifest_path, "").unwrap();
106        fs::write(&lockfile_path, "invalid yaml {[}").unwrap();
107
108        let result = get_platforms_from_lockfile(Some(manifest_path.to_str().unwrap()), None);
109
110        assert!(result.is_err());
111    }
112
113    #[test]
114    fn test_get_platforms_without_manifest_path() {
115        // Create a temp dir and copy the example lockfile
116        let temp_dir = TempDir::new().unwrap();
117        let example_lock = fs::read_to_string("examples/pixi.lock").unwrap();
118        fs::write(temp_dir.path().join("pixi.lock"), example_lock).unwrap();
119
120        // Change to temp directory
121        let original_dir = std::env::current_dir().unwrap();
122        std::env::set_current_dir(temp_dir.path()).unwrap();
123
124        // Should find lockfile in current directory
125        let result = get_platforms_from_lockfile(None, None);
126
127        // Restore directory
128        std::env::set_current_dir(original_dir).unwrap();
129
130        assert!(result.is_ok());
131        let platforms = result.unwrap();
132        assert!(platforms.len() >= 2);
133    }
134}