use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
use crate::file_path::WorkspaceFilePath;
use crate::resolver::WorkspacePathResolver;
pub struct TestWorkspace {
_tempdir: TempDir,
workspace_root: PathBuf,
crates: HashMap<String, Vec<String>>,
resolver: WorkspacePathResolver,
}
impl TestWorkspace {
pub fn builder() -> TestWorkspaceBuilder {
TestWorkspaceBuilder::new()
}
pub fn workspace_root(&self) -> &Path {
&self.workspace_root
}
pub fn resolver(&self) -> &WorkspacePathResolver {
&self.resolver
}
pub fn resolve(&self, crate_name: &str, relative_path: &str) -> WorkspaceFilePath {
use crate::crate_name::CrateName;
let workspace_relative = Path::new(crate_name).join(relative_path);
let crate_name = CrateName::new_unchecked(crate_name);
self.resolver
.resolve_relative_with_crate(workspace_relative, crate_name)
}
pub fn files_in_crate(&self, crate_name: &str) -> Vec<WorkspaceFilePath> {
self.crates
.get(crate_name)
.map(|paths| paths.iter().map(|p| self.resolve(crate_name, p)).collect())
.unwrap_or_default()
}
}
pub struct TestWorkspaceBuilder {
crates: HashMap<String, HashMap<String, String>>,
}
impl TestWorkspaceBuilder {
pub fn new() -> Self {
Self {
crates: HashMap::new(),
}
}
pub fn crate_with_source(
mut self,
crate_name: &str,
relative_path: &str,
source: &str,
) -> Self {
let crate_files = self.crates.entry(crate_name.to_string()).or_default();
crate_files.insert(relative_path.to_string(), source.to_string());
self
}
pub fn add_file(mut self, crate_name: &str, relative_path: &str, source: &str) -> Self {
let crate_files = self.crates.entry(crate_name.to_string()).or_default();
crate_files.insert(relative_path.to_string(), source.to_string());
self
}
pub fn build(self) -> TestWorkspace {
let tempdir = TempDir::new().expect("Failed to create temp directory");
let workspace_root = tempdir.path().to_path_buf();
let members: Vec<String> = self.crates.keys().map(|s| format!("\"{}\"", s)).collect();
let workspace_toml = format!(
r#"[workspace]
members = [{}]
resolver = "2"
"#,
members.join(", ")
);
fs::write(workspace_root.join("Cargo.toml"), workspace_toml)
.expect("Failed to write workspace Cargo.toml");
let mut crate_paths: HashMap<String, Vec<String>> = HashMap::new();
for (crate_name, files) in &self.crates {
let crate_dir = workspace_root.join(crate_name);
fs::create_dir_all(&crate_dir).expect("Failed to create crate directory");
let crate_toml = format!(
r#"[package]
name = "{}"
version = "0.1.0"
edition = "2021"
"#,
crate_name
);
fs::write(crate_dir.join("Cargo.toml"), crate_toml)
.expect("Failed to write crate Cargo.toml");
let mut paths = Vec::new();
for (relative_path, source) in files {
let file_path = crate_dir.join(relative_path);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent).expect("Failed to create source directory");
}
fs::write(&file_path, source).expect("Failed to write source file");
paths.push(relative_path.clone());
}
crate_paths.insert(crate_name.clone(), paths);
}
let resolver = WorkspacePathResolver::new(workspace_root.clone());
TestWorkspace {
_tempdir: tempdir,
workspace_root,
crates: crate_paths,
resolver,
}
}
}
impl Default for TestWorkspaceBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_crate_workspace() {
let workspace = TestWorkspace::builder()
.crate_with_source("mylib", "src/lib.rs", "pub fn hello() {}")
.build();
let path = workspace.resolve("mylib", "src/lib.rs");
assert_eq!(path.crate_name().as_str(), "mylib");
assert!(path.as_relative().ends_with("src/lib.rs"));
}
#[test]
fn test_multi_crate_workspace() {
let workspace = TestWorkspace::builder()
.crate_with_source("crate_a", "src/lib.rs", "pub mod foo;")
.add_file("crate_a", "src/foo.rs", "pub fn foo() {}")
.crate_with_source("crate_b", "src/lib.rs", "pub fn bar() {}")
.build();
let path_a = workspace.resolve("crate_a", "src/lib.rs");
let path_b = workspace.resolve("crate_b", "src/lib.rs");
assert_eq!(path_a.crate_name().as_str(), "crate_a");
assert_eq!(path_b.crate_name().as_str(), "crate_b");
let files_a = workspace.files_in_crate("crate_a");
assert_eq!(files_a.len(), 2);
}
}