use std::path::Path;
use cuenv_core::Result;
use cuenv_core::manifest::Project;
use cuenv_core::tasks::{TaskNode, Tasks};
use crate::commands::{CommandExecutor, relative_path_from_root};
pub fn normalize_labels(labels: &[String]) -> Vec<String> {
let mut normalized: Vec<String> = labels
.iter()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
normalized.sort();
normalized.dedup();
normalized
}
pub fn find_tasks_with_labels(tasks: &Tasks, labels: &[String]) -> Vec<String> {
let required_labels = normalize_labels(labels);
let mut matching: Vec<String> = tasks
.tasks
.iter()
.filter_map(|(name, node)| match node {
TaskNode::Task(task)
if required_labels
.iter()
.all(|label| task.labels.contains(label)) =>
{
Some(name.clone())
}
_ => None,
})
.collect();
matching.sort();
matching
}
pub fn format_label_root(labels: &[String]) -> String {
let sorted = normalize_labels(labels);
format!("__cuenv_labels__{}", sorted.join("+"))
}
pub fn evaluate_manifest(dir: &Path, package: &str, executor: &CommandExecutor) -> Result<Project> {
let _ = package;
let target_path = dir.canonicalize().map_err(|e| cuenv_core::Error::Io {
source: e,
path: Some(dir.to_path_buf().into_boxed_path()),
operation: "canonicalize path".to_string(),
})?;
tracing::debug!("Using cached module evaluation from executor");
let module = executor.get_module(&target_path)?;
let rel_path = relative_path_from_root(&module.root, &target_path);
let instance = module.get(&rel_path).ok_or_else(|| {
cuenv_core::Error::configuration(format!(
"No CUE instance found at path: {} (relative: {})",
target_path.display(),
rel_path.display()
))
})?;
match instance.kind {
cuenv_core::InstanceKind::Project => instance.deserialize(),
cuenv_core::InstanceKind::Base => {
Err(cuenv_core::Error::configuration(
"This directory uses schema.#Base which doesn't support tasks.\n\
To use tasks, update your env.cue to use schema.#Project:\n\n\
schema.#Project\n\
name: \"your-project-name\"",
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalize_labels_basic() {
let labels = vec!["b".to_string(), "a".to_string(), "c".to_string()];
let normalized = normalize_labels(&labels);
assert_eq!(normalized, vec!["a", "b", "c"]);
}
#[test]
fn test_normalize_labels_deduplicates() {
let labels = vec!["test".to_string(), "build".to_string(), "test".to_string()];
let normalized = normalize_labels(&labels);
assert_eq!(normalized, vec!["build", "test"]);
}
#[test]
fn test_normalize_labels_filters_empty() {
let labels = vec![
String::new(),
"valid".to_string(),
" ".to_string(),
"another".to_string(),
];
let normalized = normalize_labels(&labels);
assert_eq!(normalized, vec!["another", "valid"]);
}
#[test]
fn test_normalize_labels_trims_whitespace() {
let labels = vec![" test ".to_string(), " build".to_string()];
let normalized = normalize_labels(&labels);
assert_eq!(normalized, vec!["build", "test"]);
}
#[test]
fn test_normalize_labels_all_empty_returns_empty() {
let labels = vec![String::new(), " ".to_string()];
let normalized = normalize_labels(&labels);
assert!(normalized.is_empty());
}
#[test]
fn test_format_label_root() {
let labels = vec!["build".to_string(), "test".to_string()];
let root = format_label_root(&labels);
assert_eq!(root, "__cuenv_labels__build+test");
}
#[test]
fn test_format_label_root_sorts() {
let labels = vec!["z".to_string(), "a".to_string(), "m".to_string()];
let root = format_label_root(&labels);
assert_eq!(root, "__cuenv_labels__a+m+z");
}
}