use crate::tasks::{Input, Mapping, ProjectReference, Task, TaskDependency, TaskNode};
use std::collections::HashMap;
use std::path::PathBuf;
use tempfile::TempDir;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PackageManager {
Bun,
Npm,
Pnpm,
Yarn,
}
pub fn create_task(name: &str, deps: Vec<&str>, labels: Vec<&str>) -> Task {
Task {
command: format!("echo {}", name),
depends_on: deps.into_iter().map(TaskDependency::from_name).collect(),
description: Some(format!("Test task {}", name)),
labels: labels.into_iter().map(String::from).collect(),
..Default::default()
}
}
pub fn create_task_ref(ref_str: &str, deps: Vec<&str>) -> Task {
let mut task = Task::from_task_ref(ref_str);
task.depends_on = deps.into_iter().map(TaskDependency::from_name).collect();
task
}
pub fn create_task_with_project_ref(
name: &str,
deps: Vec<&str>,
project: &str,
task: &str,
mappings: Vec<(&str, &str)>,
) -> Task {
Task {
command: format!("echo {}", name),
depends_on: deps.into_iter().map(TaskDependency::from_name).collect(),
description: Some(format!("Test task {}", name)),
inputs: vec![Input::Project(ProjectReference {
project: project.to_string(),
task: task.to_string(),
map: mappings
.into_iter()
.map(|(from, to)| Mapping {
from: from.to_string(),
to: to.to_string(),
})
.collect(),
})],
..Default::default()
}
}
pub fn create_test_hook(order: i32, command: &str) -> cuenv_hooks::Hook {
cuenv_hooks::Hook {
order,
propagate: false,
command: command.to_string(),
args: vec![],
dir: None,
inputs: vec![],
source: None,
}
}
#[must_use]
pub fn create_temp_project(cue_content: &str) -> TempDir {
let dir = tempfile::Builder::new()
.prefix("cuenv_test_")
.tempdir()
.expect("Failed to create temp directory");
std::fs::write(dir.path().join("env.cue"), cue_content).expect("Failed to write env.cue");
dir
}
#[must_use]
pub fn create_mock_workspace(manager: PackageManager) -> TempDir {
let dir = tempfile::Builder::new()
.prefix("cuenv_workspace_test_")
.tempdir()
.expect("Failed to create temp directory");
let package_json = r#"{"name": "test-workspace", "version": "1.0.0", "dependencies": {}}"#;
std::fs::write(dir.path().join("package.json"), package_json)
.expect("Failed to write package.json");
match manager {
PackageManager::Bun => {
std::fs::write(
dir.path().join("bun.lock"),
r#"{"lockfileVersion": 1, "workspaces": {"": {"name": "test-workspace"}}}"#,
)
.expect("Failed to write bun.lock");
}
PackageManager::Npm => {
std::fs::write(
dir.path().join("package-lock.json"),
r#"{"name": "test-workspace", "version": "1.0.0", "lockfileVersion": 3}"#,
)
.expect("Failed to write package-lock.json");
}
PackageManager::Pnpm => {
std::fs::write(
dir.path().join("pnpm-lock.yaml"),
"lockfileVersion: '9.0'\n",
)
.expect("Failed to write pnpm-lock.yaml");
}
PackageManager::Yarn => {
std::fs::write(dir.path().join("yarn.lock"), "# yarn lockfile v1\n")
.expect("Failed to write yarn.lock");
}
}
dir
}
#[must_use]
pub fn task_names(tasks: &HashMap<String, TaskNode>) -> Vec<String> {
let mut names: Vec<_> = tasks.keys().cloned().collect();
names.sort();
names
}
#[must_use]
pub fn tasks_have_same_names(a: &HashMap<String, TaskNode>, b: &HashMap<String, TaskNode>) -> bool {
task_names(a) == task_names(b)
}
#[must_use]
pub fn get_task_deps(tasks: &HashMap<String, TaskNode>, name: &str) -> Option<Vec<String>> {
tasks.get(name).map(|node| match node {
TaskNode::Task(task) => task
.depends_on
.iter()
.map(|d| d.task_name().to_string())
.collect(),
TaskNode::Group(group) => group
.depends_on
.iter()
.map(|d| d.task_name().to_string())
.collect(),
TaskNode::Sequence(_) => Vec::new(), })
}
#[must_use]
pub fn build_dep_graph(tasks: &HashMap<String, TaskNode>) -> HashMap<String, Vec<String>> {
tasks
.iter()
.map(|(name, node)| {
let deps = match node {
TaskNode::Task(task) => task
.depends_on
.iter()
.map(|d| d.task_name().to_string())
.collect(),
TaskNode::Group(group) => group
.depends_on
.iter()
.map(|d| d.task_name().to_string())
.collect(),
TaskNode::Sequence(_) => Vec::new(), };
(name.clone(), deps)
})
.collect()
}
#[must_use]
pub fn get_workspace_root() -> PathBuf {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
std::path::Path::new(manifest_dir)
.parent() .and_then(|p| p.parent()) .expect("Failed to find project root")
.to_path_buf()
}
#[must_use]
pub fn get_examples_dir() -> PathBuf {
get_workspace_root().join("examples")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_temp_project() {
let dir = create_temp_project("package test\n");
assert!(dir.path().join("env.cue").exists());
let content = std::fs::read_to_string(dir.path().join("env.cue")).unwrap();
assert_eq!(content, "package test\n");
}
#[test]
fn test_create_mock_workspace_bun() {
let dir = create_mock_workspace(PackageManager::Bun);
assert!(dir.path().join("package.json").exists());
assert!(dir.path().join("bun.lock").exists());
}
#[test]
fn test_create_mock_workspace_npm() {
let dir = create_mock_workspace(PackageManager::Npm);
assert!(dir.path().join("package.json").exists());
assert!(dir.path().join("package-lock.json").exists());
}
#[test]
fn test_create_mock_workspace_pnpm() {
let dir = create_mock_workspace(PackageManager::Pnpm);
assert!(dir.path().join("package.json").exists());
assert!(dir.path().join("pnpm-lock.yaml").exists());
}
#[test]
fn test_create_mock_workspace_yarn() {
let dir = create_mock_workspace(PackageManager::Yarn);
assert!(dir.path().join("package.json").exists());
assert!(dir.path().join("yarn.lock").exists());
}
#[test]
fn test_task_names() {
let mut tasks = HashMap::new();
tasks.insert("b".to_string(), TaskNode::Task(Box::default()));
tasks.insert("a".to_string(), TaskNode::Task(Box::default()));
tasks.insert("c".to_string(), TaskNode::Task(Box::default()));
let names = task_names(&tasks);
assert_eq!(names, vec!["a", "b", "c"]);
}
#[test]
fn test_get_task_deps() {
let mut tasks = HashMap::new();
tasks.insert(
"build".to_string(),
TaskNode::Task(Box::new(Task {
depends_on: vec![TaskDependency::from_name("setup")],
..Default::default()
})),
);
let deps = get_task_deps(&tasks, "build");
assert_eq!(deps, Some(vec!["setup".to_string()]));
let no_deps = get_task_deps(&tasks, "nonexistent");
assert_eq!(no_deps, None);
}
#[test]
fn test_get_workspace_root() {
let root = get_workspace_root();
assert!(root.join("Cargo.toml").exists());
}
#[test]
fn test_get_examples_dir() {
let examples = get_examples_dir();
assert!(examples.exists());
assert!(examples.is_dir());
}
}