cuenv_workspaces/materializer/
cargo_deps.rs1use super::Materializer;
4use crate::core::types::{LockfileEntry, PackageManager, Workspace};
5use crate::error::{Error, Result};
6use std::path::Path;
7
8#[cfg(unix)]
9use std::os::unix::fs::symlink;
10#[cfg(windows)]
11use std::os::windows::fs::symlink_dir as symlink;
12
13pub struct CargoMaterializer;
15
16impl Materializer for CargoMaterializer {
17 fn materialize(
18 &self,
19 workspace: &Workspace,
20 _entries: &[LockfileEntry],
21 target_dir: &Path,
22 ) -> Result<()> {
23 if workspace.manager != PackageManager::Cargo {
24 return Ok(());
25 }
26
27 let workspace_target = workspace.root.join("target");
30 let env_target = target_dir.join("target");
31
32 if !workspace_target.exists() {
33 std::fs::create_dir_all(&workspace_target).map_err(|e| Error::Io {
34 source: e,
35 path: Some(workspace_target.clone()),
36 operation: "create workspace target directory".to_string(),
37 })?;
38 }
39
40 if env_target.exists() {
41 if env_target.is_symlink() || env_target.is_file() {
44 std::fs::remove_file(&env_target).map_err(|e| Error::Io {
45 source: e,
46 path: Some(env_target.clone()),
47 operation: "removing existing target symlink/file".to_string(),
48 })?;
49 } else {
50 std::fs::remove_dir_all(&env_target).map_err(|e| Error::Io {
51 source: e,
52 path: Some(env_target.clone()),
53 operation: "removing existing target directory".to_string(),
54 })?;
55 }
56 }
57
58 if let Err(e) = symlink(&workspace_target, &env_target) {
69 return Err(Error::Io {
70 source: e,
71 path: Some(env_target),
72 operation: "symlink target dir".to_string(),
73 });
74 }
75
76 Ok(())
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use std::fs;
84 use tempfile::TempDir;
85
86 fn make_workspace(root: &Path, manager: PackageManager) -> Workspace {
87 Workspace::new(root.to_path_buf(), manager)
88 }
89
90 #[test]
91 fn test_cargo_materializer_skips_non_cargo() {
92 let temp_dir = TempDir::new().unwrap();
93 let workspace = make_workspace(temp_dir.path(), PackageManager::Npm);
94 let target_dir = temp_dir.path().join("hermetic");
95 fs::create_dir_all(&target_dir).unwrap();
96
97 let materializer = CargoMaterializer;
98 let result = materializer.materialize(&workspace, &[], &target_dir);
99
100 assert!(result.is_ok());
101 assert!(!target_dir.join("target").exists());
103 }
104
105 #[test]
106 fn test_cargo_materializer_creates_target_dir() {
107 let temp_dir = TempDir::new().unwrap();
108 let workspace = make_workspace(temp_dir.path(), PackageManager::Cargo);
109 let target_dir = temp_dir.path().join("hermetic");
110 fs::create_dir_all(&target_dir).unwrap();
111
112 let materializer = CargoMaterializer;
113 let result = materializer.materialize(&workspace, &[], &target_dir);
114
115 assert!(result.is_ok());
116 assert!(temp_dir.path().join("target").exists());
118 assert!(target_dir.join("target").exists());
120 }
121
122 #[test]
123 fn test_cargo_materializer_replaces_existing_symlink() {
124 let temp_dir = TempDir::new().unwrap();
125 let workspace = make_workspace(temp_dir.path(), PackageManager::Cargo);
126 let target_dir = temp_dir.path().join("hermetic");
127 fs::create_dir_all(&target_dir).unwrap();
128
129 let other_dir = temp_dir.path().join("other");
131 fs::create_dir_all(&other_dir).unwrap();
132 let env_target = target_dir.join("target");
133 #[cfg(unix)]
134 std::os::unix::fs::symlink(&other_dir, &env_target).unwrap();
135 #[cfg(windows)]
136 std::os::windows::fs::symlink_dir(&other_dir, &env_target).unwrap();
137
138 let materializer = CargoMaterializer;
139 let result = materializer.materialize(&workspace, &[], &target_dir);
140
141 assert!(result.is_ok());
142 let workspace_target = temp_dir.path().join("target");
144 assert!(target_dir.join("target").exists());
145 assert!(workspace_target.exists());
146 }
147
148 #[test]
149 fn test_cargo_materializer_replaces_existing_directory() {
150 let temp_dir = TempDir::new().unwrap();
151 let workspace = make_workspace(temp_dir.path(), PackageManager::Cargo);
152 let target_dir = temp_dir.path().join("hermetic");
153 fs::create_dir_all(&target_dir).unwrap();
154
155 let env_target = target_dir.join("target");
157 fs::create_dir_all(&env_target).unwrap();
158 fs::write(env_target.join("somefile"), "content").unwrap();
159
160 let materializer = CargoMaterializer;
161 let result = materializer.materialize(&workspace, &[], &target_dir);
162
163 assert!(result.is_ok());
164 assert!(target_dir.join("target").exists());
166 }
167
168 #[test]
169 fn test_cargo_materializer_with_existing_workspace_target() {
170 let temp_dir = TempDir::new().unwrap();
171 let workspace = make_workspace(temp_dir.path(), PackageManager::Cargo);
172 let target_dir = temp_dir.path().join("hermetic");
173 fs::create_dir_all(&target_dir).unwrap();
174
175 let workspace_target = temp_dir.path().join("target");
177 fs::create_dir_all(&workspace_target).unwrap();
178 fs::write(workspace_target.join("marker"), "exists").unwrap();
179
180 let materializer = CargoMaterializer;
181 let result = materializer.materialize(&workspace, &[], &target_dir);
182
183 assert!(result.is_ok());
184 assert!(target_dir.join("target").exists());
186 assert!(workspace_target.join("marker").exists());
188 }
189}