diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4dd3e47da..0ee5707c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,3 +73,17 @@ git commit -m "[optional scope]: " # An example: git commit -m "feat: add xxx to the pixi.toml" ``` + +## Color decisions in the ui code +We use the `console::style` function to colorize the output of the ui. +```rust +use console::style; +println!("{} {}", style("Hello").green(), style("world").red()); +``` + +To sync the colors of the different parts of the ui, we use the following rules: +- `style("environment").magenta()`: The environment name +- `style("feature").cyan()`: The feature name +- `style("task").blue()`: The task name + +These styles are put in the `consts` module or are a `.fancy_display()` on the names. If you want to add a new generic color, please add it there. diff --git a/src/cli/info.rs b/src/cli/info.rs index d1b2ad919..e76e3f347 100644 --- a/src/cli/info.rs +++ b/src/cli/info.rs @@ -2,6 +2,7 @@ use std::{fmt::Display, fs, path::PathBuf}; use chrono::{DateTime, Local}; use clap::Parser; +use itertools::Itertools; use miette::IntoDiagnostic; use rattler_conda_types::{GenericVirtualPackage, Platform}; use rattler_virtual_packages::VirtualPackage; @@ -11,7 +12,8 @@ use serde_with::DisplayFromStr; use tokio::task::spawn_blocking; use crate::progress::await_in_progress; -use crate::{config, Project}; +use crate::task::TaskName; +use crate::{config, EnvironmentName, FeatureName, Project}; static WIDTH: usize = 18; @@ -41,14 +43,14 @@ pub struct ProjectInfo { #[derive(Serialize)] pub struct EnvironmentInfo { - name: String, - features: Vec, + name: EnvironmentName, + features: Vec, solve_group: Option, environment_size: Option, dependencies: Vec, pypi_dependencies: Vec, platforms: Vec, - tasks: Vec, + tasks: Vec, channels: Vec, // prefix: Option, add when PR 674 is merged } @@ -58,14 +60,18 @@ impl Display for EnvironmentInfo { let bold = console::Style::new().bold(); writeln!( f, - "{}", - console::style(self.name.clone()).bold().blue().underlined() + "{:>WIDTH$}: {}", + bold.apply_to("Environment"), + self.name.fancy_display().bold() )?; writeln!( f, "{:>WIDTH$}: {}", bold.apply_to("Features"), - self.features.join(", ") + self.features + .iter() + .map(|feature| feature.fancy_display()) + .format(", ") )?; if let Some(solve_group) = &self.solve_group { writeln!( @@ -80,12 +86,7 @@ impl Display for EnvironmentInfo { writeln!(f, "{:>WIDTH$}: {}", bold.apply_to("Environment size"), size)?; } if !self.channels.is_empty() { - let channels_list = self - .channels - .iter() - .map(|c| c.to_string()) - .collect::>() - .join(", "); + let channels_list = self.channels.iter().map(|c| c.to_string()).format(", "); writeln!( f, "{:>WIDTH$}: {}", @@ -100,12 +101,7 @@ impl Display for EnvironmentInfo { self.dependencies.len() )?; if !self.dependencies.is_empty() { - let dependencies_list = self - .dependencies - .iter() - .map(|d| d.to_string()) - .collect::>() - .join(", "); + let dependencies_list = self.dependencies.iter().map(|d| d.to_string()).format(", "); writeln!( f, "{:>WIDTH$}: {}", @@ -119,8 +115,7 @@ impl Display for EnvironmentInfo { .pypi_dependencies .iter() .map(|d| d.to_string()) - .collect::>() - .join(", "); + .format(", "); writeln!( f, "{:>WIDTH$}: {}", @@ -130,12 +125,7 @@ impl Display for EnvironmentInfo { } if !self.platforms.is_empty() { - let platform_list = self - .platforms - .iter() - .map(|p| p.to_string()) - .collect::>() - .join(", "); + let platform_list = self.platforms.iter().map(|p| p.to_string()).format(", "); writeln!( f, "{:>WIDTH$}: {}", @@ -144,12 +134,7 @@ impl Display for EnvironmentInfo { )?; } if !self.tasks.is_empty() { - let tasks_list = self - .tasks - .iter() - .map(|t| t.to_string()) - .collect::>() - .join(", "); + let tasks_list = self.tasks.iter().map(|t| t.fancy_display()).format(", "); writeln!(f, "{:>WIDTH$}: {}", bold.apply_to("Tasks"), tasks_list)?; } Ok(()) @@ -171,7 +156,6 @@ pub struct Info { } impl Display for Info { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let blue = console::Style::new().blue(); let bold = console::Style::new().bold(); let cache_dir = match &self.cache_dir { Some(path) => path.to_string_lossy().to_string(), @@ -182,7 +166,7 @@ impl Display for Info { f, "{:>WIDTH$}: {}", bold.apply_to("Pixi version"), - blue.apply_to(&self.version) + console::style(&self.version).green() )?; writeln!( f, @@ -310,12 +294,15 @@ pub async fn execute(args: Args) -> miette::Result<()> { let tasks = env .tasks(None, true) .ok() - .map(|t| t.into_keys().map(|k| k.to_string()).collect()) + .map(|t| t.into_keys().cloned().collect()) .unwrap_or_default(); EnvironmentInfo { - name: env.name().as_str().to_string(), - features: env.features(true).map(|f| f.name.to_string()).collect(), + name: env.name().clone(), + features: env + .features(true) + .map(|feature| feature.name.clone()) + .collect(), solve_group: env.manifest().solve_group.clone(), environment_size: None, dependencies: env diff --git a/src/cli/project/channel/list.rs b/src/cli/project/channel/list.rs index 24f97fea4..eaef209c0 100644 --- a/src/cli/project/channel/list.rs +++ b/src/cli/project/channel/list.rs @@ -15,8 +15,8 @@ pub fn execute(project: Project, args: Args) -> miette::Result<()> { .map(|e| { println!( "{} {}", - console::style("Environment:").bold().blue(), - console::style(e.name()).bold() + console::style("Environment:").bold().bright(), + e.name().fancy_display() ); e.channels() }) diff --git a/src/cli/run.rs b/src/cli/run.rs index 104ee10c3..7c8f5a361 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -14,7 +14,7 @@ use crate::activation::get_environment_variables; use crate::project::errors::UnsupportedPlatformError; use crate::task::{ AmbiguousTask, ExecutableTask, FailedToParseShellScript, InvalidWorkingDirectory, - SearchEnvironments, TaskAndEnvironment, TaskGraph, + SearchEnvironments, TaskAndEnvironment, TaskGraph, TaskName, }; use crate::{Project, UpdateLockFileOptions}; @@ -113,9 +113,11 @@ pub async fn execute(args: Args) -> miette::Result<()> { "{}{}{}{}{}", console::Emoji("✨ ", ""), console::style("Pixi task (").bold(), - console::style(executable_task.run_environment.name()) - .bold() - .cyan(), + executable_task + .run_environment + .name() + .fancy_display() + .bold(), console::style("): ").bold(), executable_task.display_command(), ); @@ -153,26 +155,26 @@ pub async fn execute(args: Args) -> miette::Result<()> { /// Called when a command was not found. fn command_not_found<'p>(project: &'p Project, explicit_environment: Option>) { - let available_tasks: HashSet = if let Some(explicit_environment) = explicit_environment - { - explicit_environment - .tasks(Some(Platform::current()), true) - .into_iter() - .flat_map(|tasks| tasks.into_keys()) - .map(ToOwned::to_owned) - .collect() - } else { - project - .environments() - .into_iter() - .flat_map(|env| { - env.tasks(Some(Platform::current()), true) - .into_iter() - .flat_map(|tasks| tasks.into_keys()) - .map(ToOwned::to_owned) - }) - .collect() - }; + let available_tasks: HashSet = + if let Some(explicit_environment) = explicit_environment { + explicit_environment + .tasks(Some(Platform::current()), true) + .into_iter() + .flat_map(|tasks| tasks.into_keys()) + .map(ToOwned::to_owned) + .collect() + } else { + project + .environments() + .into_iter() + .flat_map(|env| { + env.tasks(Some(Platform::current()), true) + .into_iter() + .flat_map(|tasks| tasks.into_keys()) + .map(ToOwned::to_owned) + }) + .collect() + }; if !available_tasks.is_empty() { eprintln!( @@ -181,7 +183,7 @@ fn command_not_found<'p>(project: &'p Project, explicit_environment: Option( .iter() .map(|(env, _)| env.name()) .collect_vec(); - dialoguer::Select::with_theme(&ColorfulTheme::default()) + let theme = ColorfulTheme { + active_item_style: console::Style::new().for_stderr().magenta(), + ..ColorfulTheme::default() + }; + + dialoguer::Select::with_theme(&theme) .with_prompt(format!( "The task '{}' {}can be run in multiple environments.\n\nPlease select an environment to run the task in:", - problem.task_name, + problem.task_name.fancy_display(), if let Some(dependency) = &problem.depended_on_by { - format!("(depended on by '{}') ", dependency.0) + format!("(depended on by '{}') ", dependency.0.fancy_display()) } else { String::new() } diff --git a/src/cli/task.rs b/src/cli/task.rs index 5ea309732..57300a379 100644 --- a/src/cli/task.rs +++ b/src/cli/task.rs @@ -1,5 +1,5 @@ use crate::project::manifest::{EnvironmentName, FeatureName}; -use crate::task::{quote, Alias, CmdArgs, Execute, Task}; +use crate::task::{quote, Alias, CmdArgs, Execute, Task, TaskName}; use crate::Project; use clap::Parser; use itertools::Itertools; @@ -32,7 +32,7 @@ pub enum Operation { #[clap(arg_required_else_help = true)] pub struct RemoveArgs { /// Task names to remove - pub names: Vec, + pub names: Vec, /// The platform for which the task should be removed #[arg(long, short)] @@ -47,7 +47,7 @@ pub struct RemoveArgs { #[clap(arg_required_else_help = true)] pub struct AddArgs { /// Task name - pub name: String, + pub name: TaskName, /// One or more commands to actually execute #[clap(required = true, num_args = 1..)] @@ -56,7 +56,7 @@ pub struct AddArgs { /// Depends on these other commands #[clap(long)] #[clap(num_args = 1..)] - pub depends_on: Option>, + pub depends_on: Option>, /// The platform for which the task should be added #[arg(long, short)] @@ -75,11 +75,11 @@ pub struct AddArgs { #[clap(arg_required_else_help = true)] pub struct AliasArgs { /// Alias name - pub alias: String, + pub alias: TaskName, /// Depends on these tasks to execute #[clap(required = true, num_args = 1..)] - pub depends_on: Vec, + pub depends_on: Vec, /// The platform for which the alias should be added #[arg(long, short)] @@ -161,12 +161,12 @@ pub fn execute(args: Args) -> miette::Result<()> { .map_or(FeatureName::Default, FeatureName::Named); project .manifest - .add_task(name, task.clone(), args.platform, &feature)?; + .add_task(name.clone(), task.clone(), args.platform, &feature)?; project.save()?; eprintln!( "{}Added task `{}`: {}", console::style(console::Emoji("✔ ", "+")).green(), - console::style(&name).bold(), + name.fancy_display().bold(), task, ); } @@ -180,25 +180,21 @@ pub fn execute(args: Args) -> miette::Result<()> { if !project .manifest .tasks(Some(platform), &feature)? - .contains_key(name.as_str()) + .contains_key(name) { eprintln!( "{}Task '{}' does not exist on {}", console::style(console::Emoji("❌ ", "X")).red(), - console::style(&name).bold(), + name.fancy_display().bold(), console::style(platform.as_str()).bold(), ); continue; } - } else if !project - .manifest - .tasks(None, &feature)? - .contains_key(name.as_str()) - { + } else if !project.manifest.tasks(None, &feature)?.contains_key(name) { eprintln!( "{}Task `{}` does not exist for the `{}` feature", console::style(console::Emoji("❌ ", "X")).red(), - console::style(&name).bold(), + name.fancy_display().bold(), console::style(&feature).bold(), ); continue; @@ -226,26 +222,31 @@ pub fn execute(args: Args) -> miette::Result<()> { } for (name, platform) in to_remove { - project.manifest.remove_task(name, platform, &feature)?; + project + .manifest + .remove_task(name.clone(), platform, &feature)?; project.save()?; eprintln!( "{}Removed task `{}` ", console::style(console::Emoji("✔ ", "+")).green(), - console::style(&name).bold(), + name.fancy_display().bold(), ); } } Operation::Alias(args) => { let name = &args.alias; let task: Task = args.clone().into(); - project - .manifest - .add_task(name, task.clone(), args.platform, &FeatureName::Default)?; + project.manifest.add_task( + name.clone(), + task.clone(), + args.platform, + &FeatureName::Default, + )?; project.save()?; eprintln!( "{} Added alias `{}`: {}", console::style("@").blue(), - console::style(&name).bold(), + name.fancy_display().bold(), task, ); } @@ -265,9 +266,9 @@ pub fn execute(args: Args) -> miette::Result<()> { .sorted() .map(|name| { if args.summary { - format!("{} ", console::style(name)) + format!("{} ", name.as_str(),) } else { - format!("* {}\n", console::style(name).bold()) + format!("* {}\n", name.fancy_display().bold(),) } }) .collect(); @@ -297,7 +298,13 @@ impl From for Item { if !process.depends_on.is_empty() { table.insert( "depends_on", - Value::Array(Array::from_iter(process.depends_on)), + Value::Array(Array::from_iter( + process + .depends_on + .into_iter() + .map(String::from) + .map(Value::from), + )), ); } if let Some(cwd) = process.cwd { @@ -309,7 +316,13 @@ impl From for Item { let mut table = Table::new().into_inline_table(); table.insert( "depends_on", - Value::Array(Array::from_iter(alias.depends_on)), + Value::Array(Array::from_iter( + alias + .depends_on + .into_iter() + .map(String::from) + .map(Value::from), + )), ); Item::Value(Value::InlineTable(table)) } diff --git a/src/consts.rs b/src/consts.rs index 45f2fbe4f..d0be1c96b 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,3 +1,6 @@ +use console::Style; +use lazy_static::lazy_static; + pub const PROJECT_MANIFEST: &str = "pixi.toml"; pub const PROJECT_LOCK_FILE: &str = "pixi.lock"; pub const PIXI_DIR: &str = ".pixi"; @@ -8,3 +11,7 @@ pub const PYPI_DEPENDENCIES: &str = "pypi-dependencies"; pub const DEFAULT_ENVIRONMENT_NAME: &str = "default"; pub const DEFAULT_FEATURE_NAME: &str = DEFAULT_ENVIRONMENT_NAME; + +lazy_static! { + pub static ref TASK_STYLE: Style = Style::new().blue(); +} diff --git a/src/environment.rs b/src/environment.rs index dd6c3957a..5a6efdd5d 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -495,7 +495,7 @@ impl<'p> OutdatedEnvironments<'p> { else { tracing::info!( "environment '{0}' is out of date because it does not exist in the lock-file.", - environment.name().as_str() + environment.name().fancy_display() ); outdated_conda @@ -511,7 +511,7 @@ impl<'p> OutdatedEnvironments<'p> { { tracing::info!( "environment '{0}' is out of date because {unsat}", - environment.name().as_str() + environment.name().fancy_display() ); outdated_conda @@ -529,7 +529,7 @@ impl<'p> OutdatedEnvironments<'p> { Err(unsat @ PlatformUnsat::UnsatisfiableRequirement(_, _)) => { tracing::info!( "the pypi dependencies of environment '{0}' for platform {platform} are out of date because {unsat}", - environment.name().as_str() + environment.name().fancy_display() ); outdated_pypi @@ -540,7 +540,7 @@ impl<'p> OutdatedEnvironments<'p> { Err(unsat) => { tracing::info!( "the dependencies of environment '{0}' for platform {platform} are out of date because {unsat}", - environment.name().as_str() + environment.name().fancy_display() ); outdated_conda @@ -1029,7 +1029,7 @@ async fn ensure_up_to_date_lock_file( tracing::info!( "solved conda packages for '{}' '{}'", - environment.name(), + environment.name().fancy_display(), platform ); } @@ -1047,7 +1047,7 @@ async fn ensure_up_to_date_lock_file( tracing::info!( "updated conda packages in the '{}' prefix", - environment.name() + environment.name().fancy_display() ); } TaskResult::PypiSolved(environment, platform, records) => { @@ -1066,7 +1066,7 @@ async fn ensure_up_to_date_lock_file( tracing::info!( "solved pypi packages for '{}' '{}'", - environment.name(), + environment.name().fancy_display(), platform ); } diff --git a/src/lib.rs b/src/lib.rs index 72baabfdc..90f8b429a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ mod progress; mod project; mod prompt; mod repodata; -mod task; +pub mod task; #[cfg(unix)] pub mod unix; pub mod util; diff --git a/src/project/environment.rs b/src/project/environment.rs index 668bcd077..158f1bce4 100644 --- a/src/project/environment.rs +++ b/src/project/environment.rs @@ -4,6 +4,7 @@ use super::{ manifest::{self, EnvironmentName, Feature, FeatureName, SystemRequirements}, PyPiRequirement, SpecType, }; +use crate::task::TaskName; use crate::{task::Task, Project}; use indexmap::{IndexMap, IndexSet}; use itertools::{Either, Itertools}; @@ -166,21 +167,25 @@ impl<'p> Environment<'p> { &self, platform: Option, include_default: bool, - ) -> Result, UnsupportedPlatformError> { + ) -> Result, UnsupportedPlatformError> { self.validate_platform_support(platform)?; let result = self .features(include_default) .flat_map(|feature| feature.targets.resolve(platform)) .rev() // Reverse to get the most specific targets last. .flat_map(|target| target.tasks.iter()) - .map(|(name, task)| (name.as_str(), task)) + .map(|(name, task)| (name, task)) .collect(); Ok(result) } /// Returns the task with the given `name` and for the specified `platform` or an `UnknownTask` /// which explains why the task was not available. - pub fn task(&self, name: &str, platform: Option) -> Result<&'p Task, UnknownTask> { + pub fn task( + &self, + name: &TaskName, + platform: Option, + ) -> Result<&'p Task, UnknownTask> { match self .tasks(platform, true) .map(|tasks| tasks.get(name).copied()) @@ -189,7 +194,7 @@ impl<'p> Environment<'p> { project: self.project, environment: self.name().clone(), platform, - task_name: name.to_string(), + task_name: name.clone(), }), Ok(Some(task)) => Ok(task), } @@ -381,7 +386,7 @@ mod tests { let task = manifest .default_environment() - .task("foo", None) + .task(&"foo".into(), None) .unwrap() .as_single_command() .unwrap(); @@ -390,7 +395,7 @@ mod tests { let task_osx = manifest .default_environment() - .task("foo", Some(Platform::Linux64)) + .task(&"foo".into(), Some(Platform::Linux64)) .unwrap() .as_single_command() .unwrap(); diff --git a/src/project/errors.rs b/src/project/errors.rs index a7997e37e..eba29b6c7 100644 --- a/src/project/errors.rs +++ b/src/project/errors.rs @@ -1,4 +1,5 @@ use crate::project::manifest::EnvironmentName; +use crate::task::TaskName; use crate::Project; use itertools::Itertools; use miette::{Diagnostic, LabeledSpan}; @@ -61,7 +62,7 @@ impl Diagnostic for UnsupportedPlatformError { /// - Include names that might have been meant instead /// - If the tasks is only available for a certain platform, explain that. #[derive(Debug, Clone, Diagnostic, Error)] -#[error("the task '{task_name}' could not be found")] +#[error("the task '{0}' could not be found", task_name.fancy_display())] pub struct UnknownTask<'p> { /// The project that the platform is not supported for. pub project: &'p Project, @@ -73,5 +74,5 @@ pub struct UnknownTask<'p> { pub platform: Option, /// The name of the task - pub task_name: String, + pub task_name: TaskName, } diff --git a/src/project/manifest/environment.rs b/src/project/manifest/environment.rs index 8f9877a58..6200b237f 100644 --- a/src/project/manifest/environment.rs +++ b/src/project/manifest/environment.rs @@ -3,7 +3,7 @@ use crate::utils::spanned::PixiSpanned; use lazy_static::lazy_static; use miette::Diagnostic; use regex::Regex; -use serde::{self, Deserialize, Deserializer}; +use serde::{self, Deserialize, Deserializer, Serialize}; use std::borrow::Borrow; use std::fmt; use std::hash::{Hash, Hasher}; @@ -11,7 +11,7 @@ use std::str::FromStr; use thiserror::Error; /// The name of an environment. This is either a string or default for the default environment. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)] pub enum EnvironmentName { Default, Named(String), @@ -32,6 +32,11 @@ impl EnvironmentName { EnvironmentName::Named(name) => name.as_str(), } } + + /// Returns a styled version of the environment name for display in the console. + pub fn fancy_display(&self) -> console::StyledObject<&str> { + console::style(self.as_str()).magenta() + } } impl Borrow for EnvironmentName { diff --git a/src/project/manifest/feature.rs b/src/project/manifest/feature.rs index 12e170544..a629326e1 100644 --- a/src/project/manifest/feature.rs +++ b/src/project/manifest/feature.rs @@ -3,20 +3,20 @@ use crate::consts; use crate::project::manifest::channel::{PrioritizedChannel, TomlPrioritizedChannelStrOrMap}; use crate::project::manifest::target::Targets; use crate::project::SpecType; -use crate::task::Task; +use crate::task::{Task, TaskName}; use crate::utils::spanned::PixiSpanned; use indexmap::IndexMap; use itertools::Either; use rattler_conda_types::{NamelessMatchSpec, PackageName, Platform}; use serde::de::Error; -use serde::{Deserialize, Deserializer}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_with::{serde_as, DisplayFromStr, PickFirst}; use std::borrow::{Borrow, Cow}; use std::collections::HashMap; use std::fmt; /// The name of a feature. This is either a string or default for the default feature. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize)] pub enum FeatureName { Default, Named(String), @@ -56,6 +56,11 @@ impl FeatureName { pub fn as_str(&self) -> &str { self.name().unwrap_or(consts::DEFAULT_FEATURE_NAME) } + + /// Returns a styled version of the feature name for display in the console. + pub fn fancy_display(&self) -> console::StyledObject<&str> { + console::style(self.as_str()).cyan() + } } impl Borrow for FeatureName { @@ -248,7 +253,7 @@ impl<'de> Deserialize<'de> for Feature { /// Target specific tasks to run in the environment #[serde(default)] - tasks: HashMap, + tasks: HashMap, } let inner = FeatureInner::deserialize(deserializer)?; diff --git a/src/project/manifest/mod.rs b/src/project/manifest/mod.rs index 962d3a1d8..175a16730 100644 --- a/src/project/manifest/mod.rs +++ b/src/project/manifest/mod.rs @@ -11,6 +11,7 @@ mod validation; use crate::project::manifest::channel::PrioritizedChannel; use crate::project::manifest::environment::TomlEnvironmentMapOrSeq; +use crate::task::TaskName; use crate::{consts, project::SpecType, task::Task, utils::spanned::PixiSpanned}; use ::serde::{Deserialize, Deserializer}; pub use activation::Activation; @@ -136,7 +137,7 @@ impl Manifest { &self, platform: Option, feature_name: &FeatureName, - ) -> Result, GetFeatureError> { + ) -> Result, GetFeatureError> { Ok(self .feature(feature_name) // Return error if feature does not exist @@ -147,22 +148,22 @@ impl Manifest { .into_iter() .rev() .flat_map(|target| target.tasks.iter()) - .map(|(name, task)| (name.as_str(), task)) + .map(|(name, task)| (name.clone(), task)) .collect()) } /// Add a task to the project pub fn add_task( &mut self, - name: impl AsRef, + name: TaskName, task: Task, platform: Option, feature_name: &FeatureName, ) -> miette::Result<()> { // Check if the task already exists if let Ok(tasks) = self.tasks(platform, feature_name) { - if tasks.contains_key(name.as_ref()) { - miette::bail!("task {} already exists", name.as_ref()); + if tasks.contains_key(&name) { + miette::bail!("task {} already exists", name.fancy_display()); } } @@ -170,14 +171,14 @@ impl Manifest { let table = get_or_insert_toml_table(&mut self.document, platform, feature_name, "tasks")?; // Add the task to the table - table.insert(name.as_ref(), task.clone().into()); + table.insert(name.as_str(), task.clone().into()); // Add the task to the manifest self.default_feature_mut() .targets .for_opt_target_or_default_mut(platform.map(TargetSelector::from).as_ref()) .tasks - .insert(name.as_ref().to_string(), task); + .insert(name, task); Ok(()) } @@ -185,27 +186,27 @@ impl Manifest { /// Remove a task from the project, and the tasks that depend on it pub fn remove_task( &mut self, - name: impl AsRef, + name: TaskName, platform: Option, feature_name: &FeatureName, ) -> miette::Result<()> { self.tasks(platform, feature_name)? - .get(name.as_ref()) - .ok_or_else(|| miette::miette!("task {} does not exist", name.as_ref()))?; + .get(&name) + .ok_or_else(|| miette::miette!("task {} does not exist", name.fancy_display()))?; // Get the task table either from the target platform or the default tasks. let tasks_table = get_or_insert_toml_table(&mut self.document, platform, feature_name, "tasks")?; // If it does not exist in toml, consider this ok as we want to remove it anyways - tasks_table.remove(name.as_ref()); + tasks_table.remove(name.as_str()); // Remove the task from the internal manifest self.feature_mut(feature_name) .expect("feature should exist") .targets .for_opt_target_mut(platform.map(TargetSelector::from).as_ref()) - .map(|target| target.tasks.remove(name.as_ref())); + .map(|target| target.tasks.remove(&name)); Ok(()) } @@ -786,7 +787,7 @@ impl<'de> Deserialize<'de> for ProjectManifest { /// Target specific tasks to run in the environment #[serde(default)] - tasks: HashMap, + tasks: HashMap, /// The features defined in the project. #[serde(default)] @@ -1167,8 +1168,9 @@ mod tests { selector.map_or_else(|| String::from("default"), ToString::to_string); target.tasks.iter().filter_map(move |(name, task)| { Some(format!( - "{}/{name} = {}", + "{}/{} = {}", &selector_name, + name.as_str(), task.as_single_command()? )) }) @@ -1938,7 +1940,7 @@ platforms = ["linux-64", "win-64"] .targets .default() .tasks - .get("warmup") + .get(&"warmup".into()) .unwrap() .as_single_command() .unwrap(), @@ -1986,7 +1988,7 @@ test = "test initial" manifest .add_task( - "default", + "default".into(), Task::Plain("echo default".to_string()), None, &FeatureName::Default, @@ -1994,7 +1996,7 @@ test = "test initial" .unwrap(); manifest .add_task( - "target_linux", + "target_linux".into(), Task::Plain("echo target_linux".to_string()), Some(Platform::Linux64), &FeatureName::Default, @@ -2002,7 +2004,7 @@ test = "test initial" .unwrap(); manifest .add_task( - "feature_test", + "feature_test".into(), Task::Plain("echo feature_test".to_string()), None, &FeatureName::Named("test".to_string()), @@ -2010,7 +2012,7 @@ test = "test initial" .unwrap(); manifest .add_task( - "feature_test_target_linux", + "feature_test_target_linux".into(), Task::Plain("echo feature_test_target_linux".to_string()), Some(Platform::Linux64), &FeatureName::Named("test".to_string()), diff --git a/src/project/manifest/target.rs b/src/project/manifest/target.rs index 72d14c7be..5dbe2d429 100644 --- a/src/project/manifest/target.rs +++ b/src/project/manifest/target.rs @@ -1,4 +1,5 @@ use crate::project::manifest::activation::Activation; +use crate::task::TaskName; use crate::utils::spanned::PixiSpanned; use crate::{ project::{manifest::error::SpecIsMissing, manifest::PyPiRequirement, SpecType}, @@ -28,7 +29,7 @@ pub struct Target { pub activation: Option, /// Target specific tasks to run in the environment - pub tasks: HashMap, + pub tasks: HashMap, } impl Target { @@ -197,7 +198,7 @@ impl<'de> Deserialize<'de> for Target { /// Target specific tasks to run in the environment #[serde(default)] - tasks: HashMap, + tasks: HashMap, } let target = TomlTarget::deserialize(deserializer)?; diff --git a/src/project/manifest/validation.rs b/src/project/manifest/validation.rs index 4bd31754e..ae818cf5f 100644 --- a/src/project/manifest/validation.rs +++ b/src/project/manifest/validation.rs @@ -47,7 +47,7 @@ impl ProjectManifest { if name != &FeatureName::Default && !features_used.contains(&name.to_string()) { tracing::warn!( "The feature '{}' is defined but not used in any environment", - console::style(name).bold().blue() + name.fancy_display(), ); } } diff --git a/src/project/mod.rs b/src/project/mod.rs index 6e1f466da..5094b355d 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -30,6 +30,7 @@ use crate::{ use manifest::{EnvironmentName, Manifest, PyPiRequirement, SystemRequirements}; use url::Url; +use crate::task::TaskName; pub use dependencies::Dependencies; pub use environment::Environment; @@ -279,7 +280,7 @@ impl Project { /// Get the tasks of this project /// /// TODO: Remove this function and use the tasks from the default environment instead. - pub fn tasks(&self, platform: Option) -> HashMap<&str, &Task> { + pub fn tasks(&self, platform: Option) -> HashMap<&TaskName, &Task> { self.default_environment() .tasks(platform, true) .unwrap_or_default() @@ -290,7 +291,7 @@ impl Project { /// platform. /// /// TODO: Remove this function and use the `task` function from the default environment instead. - pub fn task_opt(&self, name: &str, platform: Option) -> Option<&Task> { + pub fn task_opt(&self, name: &TaskName, platform: Option) -> Option<&Task> { self.default_environment().task(name, platform).ok() } diff --git a/src/project/snapshots/pixi__project__tests__target_specific_tasks-2.snap b/src/project/snapshots/pixi__project__tests__target_specific_tasks-2.snap index caaa06def..f2f5f8fd7 100644 --- a/src/project/snapshots/pixi__project__tests__target_specific_tasks-2.snap +++ b/src/project/snapshots/pixi__project__tests__target_specific_tasks-2.snap @@ -1,9 +1,11 @@ --- source: src/project/mod.rs -expression: "project.manifest.tasks(Some(Platform::Win64))" +expression: "project.manifest.tasks(Some(Platform::Win64), &FeatureName::Default).unwrap()" --- { - "test": Plain( + TaskName( + "test", + ): Plain( "test win", ), } diff --git a/src/project/snapshots/pixi__project__tests__target_specific_tasks-3.snap b/src/project/snapshots/pixi__project__tests__target_specific_tasks-3.snap index e3ac9ba57..116cdadb7 100644 --- a/src/project/snapshots/pixi__project__tests__target_specific_tasks-3.snap +++ b/src/project/snapshots/pixi__project__tests__target_specific_tasks-3.snap @@ -1,9 +1,11 @@ --- source: src/project/mod.rs -expression: "project.manifest.tasks(Some(Platform::Linux64))" +expression: "project.manifest.tasks(Some(Platform::Linux64),\n &FeatureName::Default).unwrap()" --- { - "test": Plain( + TaskName( + "test", + ): Plain( "test linux", ), } diff --git a/src/project/snapshots/pixi__project__tests__target_specific_tasks.snap b/src/project/snapshots/pixi__project__tests__target_specific_tasks.snap index 904f52ee2..157375b1d 100644 --- a/src/project/snapshots/pixi__project__tests__target_specific_tasks.snap +++ b/src/project/snapshots/pixi__project__tests__target_specific_tasks.snap @@ -1,9 +1,11 @@ --- source: src/project/mod.rs -expression: "project.manifest.tasks(Some(Platform::Osx64))" +expression: "project.manifest.tasks(Some(Platform::Osx64), &FeatureName::Default).unwrap()" --- { - "test": Plain( + TaskName( + "test", + ): Plain( "test multi", ), } diff --git a/src/task/error.rs b/src/task/error.rs index 15cb6e470..77b2bcfcc 100644 --- a/src/task/error.rs +++ b/src/task/error.rs @@ -1,25 +1,30 @@ use crate::project::manifest::EnvironmentName; +use crate::task::TaskName; use itertools::Itertools; use miette::Diagnostic; use std::fmt::{Display, Formatter}; use thiserror::Error; #[derive(Debug, Error, Diagnostic)] -#[error("could not find the task '{task_name}'")] +#[error("could not find the task '{0}'", task_name.fancy_display())] pub struct MissingTaskError { - pub task_name: String, + pub task_name: TaskName, } // TODO: We should make this error much better #[derive(Debug, Error)] pub struct AmbiguousTaskError { - pub task_name: String, + pub task_name: TaskName, pub environments: Vec, } impl Display for AmbiguousTaskError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "the task '{}' is ambiguous", &self.task_name) + write!( + f, + "the task '{}' is ambiguous", + &self.task_name.fancy_display() + ) } } @@ -30,7 +35,7 @@ impl Diagnostic for AmbiguousTaskError { self.environments.iter().map(|env| env.as_str()).format(", "), env!("CARGO_PKG_NAME"), self.environments.first().expect("there should be at least two environment"), - task_name=&self.task_name + task_name=&self.task_name.fancy_display() ))) } } diff --git a/src/task/executable_task.rs b/src/task/executable_task.rs index 821b282dd..7c98b5040 100644 --- a/src/task/executable_task.rs +++ b/src/task/executable_task.rs @@ -1,4 +1,6 @@ +use crate::consts::TASK_STYLE; use crate::project::Environment; +use crate::task::TaskName; use crate::{ task::task_graph::{TaskGraph, TaskId}, task::{quote_arguments, Task}, @@ -7,6 +9,7 @@ use crate::{ use deno_task_shell::{ execute_with_pipes, parser::SequentialList, pipe, ShellPipeWriter, ShellState, }; +use itertools::Itertools; use miette::Diagnostic; use std::{ borrow::Cow, @@ -51,7 +54,7 @@ pub enum TaskExecutionError { #[derive(Clone)] pub struct ExecutableTask<'p> { pub project: &'p Project, - pub name: Option, + pub name: Option, pub task: Cow<'p, Task>, pub run_environment: Environment<'p>, pub additional_args: Vec, @@ -72,7 +75,7 @@ impl<'p> ExecutableTask<'p> { /// Returns the name of the task or `None` if this is an anonymous task. pub fn name(&self) -> Option<&str> { - self.name.as_deref() + self.name.as_ref().map(|name| name.as_str()) } /// Returns the task description from the project. @@ -192,15 +195,15 @@ impl<'p, 't> Display for ExecutableTaskConsoleDisplay<'p, 't> { write!( f, "{}", - console::style(command.as_deref().unwrap_or("")) - .blue() + TASK_STYLE + .apply_to(command.as_deref().unwrap_or("")) .bold() )?; if !self.task.additional_args.is_empty() { write!( f, " {}", - console::style(self.task.additional_args.join(" ")).blue() + TASK_STYLE.apply_to(self.task.additional_args.iter().format(" ")) )?; } Ok(()) diff --git a/src/task/mod.rs b/src/task/mod.rs index 5e2ac872c..6bcbeedd3 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use serde_with::{formats::PreferMany, serde_as, OneOrMany}; use std::borrow::Cow; use std::fmt::{Display, Formatter}; @@ -20,6 +20,35 @@ pub use task_environment::{ }; pub use task_graph::{TaskGraph, TaskGraphError, TaskId, TaskNode}; +/// Represents a task name +#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct TaskName(String); + +impl TaskName { + pub fn as_str(&self) -> &str { + self.0.as_str() + } + /// Returns a styled version of the task name for display in the console. + pub fn fancy_display(&self) -> console::StyledObject<&str> { + console::style(self.as_str()).blue() + } +} +impl From<&str> for TaskName { + fn from(name: &str) -> Self { + TaskName(name.to_string()) + } +} +impl From for TaskName { + fn from(name: String) -> Self { + TaskName(name) + } +} +impl From for String { + fn from(task_name: TaskName) -> Self { + task_name.0 // Assuming TaskName is a tuple struct with the first element as String + } +} + /// Represents different types of scripts #[derive(Debug, Clone, Deserialize)] #[serde(untagged)] @@ -35,7 +64,7 @@ pub enum Task { impl Task { /// Returns the names of the task that this task depends on - pub fn depends_on(&self) -> &[String] { + pub fn depends_on(&self) -> &[TaskName] { match self { Task::Plain(_) | Task::Custom(_) => &[], Task::Execute(cmd) => &cmd.depends_on, @@ -123,7 +152,7 @@ pub struct Execute { /// A list of commands that should be run before this one #[serde(default)] #[serde_as(deserialize_as = "OneOrMany<_, PreferMany>")] - pub depends_on: Vec, + pub depends_on: Vec, /// The working directory for the command relative to the root of the project. pub cwd: Option, @@ -194,7 +223,7 @@ impl CmdArgs { pub struct Alias { /// A list of commands that should be run before this one #[serde_as(deserialize_as = "OneOrMany<_, PreferMany>")] - pub depends_on: Vec, + pub depends_on: Vec, } impl Display for Task { @@ -218,9 +247,17 @@ impl Display for Task { let depends_on = self.depends_on(); if !depends_on.is_empty() { if depends_on.len() == 1 { - write!(f, "depends_on = '{}'", depends_on.iter().join(",")) + write!( + f, + "depends_on = '{}'", + depends_on.iter().map(|t| t.fancy_display()).join(",") + ) } else { - write!(f, "depends_on = [{}]", depends_on.iter().join(",")) + write!( + f, + "depends_on = [{}]", + depends_on.iter().map(|t| t.fancy_display()).join(",") + ) } } else { Ok(()) diff --git a/src/task/task_environment.rs b/src/task/task_environment.rs index 14c927c37..56cb02a7b 100644 --- a/src/task/task_environment.rs +++ b/src/task/task_environment.rs @@ -1,5 +1,6 @@ use crate::project::Environment; use crate::task::error::{AmbiguousTaskError, MissingTaskError}; +use crate::task::TaskName; use crate::{Project, Task}; use itertools::Itertools; use miette::Diagnostic; @@ -10,7 +11,7 @@ use thiserror::Error; #[derive(Debug, Clone)] pub enum FindTaskSource<'p> { CmdArgs, - DependsOn(String, &'p Task), + DependsOn(TaskName, &'p Task), } pub type TaskAndEnvironment<'p> = (Environment<'p>, &'p Task); @@ -47,8 +48,8 @@ pub struct SearchEnvironments<'p, D: TaskDisambiguation<'p> = NoDisambiguation> /// Information about an task that was found when searching for a task pub struct AmbiguousTask<'p> { - pub task_name: String, - pub depended_on_by: Option<(String, &'p Task)>, + pub task_name: TaskName, + pub depended_on_by: Option<(TaskName, &'p Task)>, pub environments: Vec>, } @@ -113,7 +114,7 @@ impl<'p, D: TaskDisambiguation<'p>> SearchEnvironments<'p, D> { /// be found. pub fn find_task( &self, - name: &str, + name: TaskName, source: FindTaskSource<'p>, ) -> Result, FindTaskError> { // If the task was specified on the command line and there is no explicit environment and @@ -125,7 +126,7 @@ impl<'p, D: TaskDisambiguation<'p>> SearchEnvironments<'p, D> { .default_feature() .targets .resolve(self.platform) - .find_map(|target| target.tasks.get(name)) + .find_map(|target| target.tasks.get(&name)) { // None of the other environments can have this task. Otherwise, its still // ambiguous. @@ -135,7 +136,7 @@ impl<'p, D: TaskDisambiguation<'p>> SearchEnvironments<'p, D> { .into_iter() .flat_map(|env| env.features(false).collect_vec()) .flat_map(|feature| feature.targets.resolve(self.platform)) - .any(|target| target.tasks.contains_key(name)) + .any(|target| target.tasks.contains_key(&name)) { return Ok((self.project.default_environment(), task)); } @@ -157,7 +158,7 @@ impl<'p, D: TaskDisambiguation<'p>> SearchEnvironments<'p, D> { if let Some(task) = env .tasks(self.platform, include_default_feature) .ok() - .and_then(|tasks| tasks.get(name).copied()) + .and_then(|tasks| tasks.get(&name).copied()) { tasks.push((env.clone(), task)); } @@ -165,7 +166,7 @@ impl<'p, D: TaskDisambiguation<'p>> SearchEnvironments<'p, D> { match tasks.len() { 0 => Err(FindTaskError::MissingTask(MissingTaskError { - task_name: name.to_string(), + task_name: name, })), 1 => { let (env, task) = tasks.remove(0); @@ -173,7 +174,7 @@ impl<'p, D: TaskDisambiguation<'p>> SearchEnvironments<'p, D> { } _ => { let ambiguous_task = AmbiguousTask { - task_name: name.to_string(), + task_name: name, depended_on_by: match source { FindTaskSource::DependsOn(dep, task) => Some((dep, task)), _ => None, diff --git a/src/task/task_graph.rs b/src/task/task_graph.rs index cbc840594..03a0ba2e8 100644 --- a/src/task/task_graph.rs +++ b/src/task/task_graph.rs @@ -1,7 +1,7 @@ use crate::project::Environment; use crate::task::error::AmbiguousTaskError; use crate::task::task_environment::{FindTaskError, FindTaskSource, SearchEnvironments}; -use crate::task::TaskDisambiguation; +use crate::task::{TaskDisambiguation, TaskName}; use crate::{ task::{error::MissingTaskError, CmdArgs, Custom, Task}, Project, @@ -24,7 +24,7 @@ pub struct TaskId(usize); /// A node in the [`TaskGraph`]. pub struct TaskNode<'p> { /// The name of the task or `None` if the task is a custom task. - pub name: Option, + pub name: Option, /// The environment to run the task in pub run_environment: Environment<'p>, @@ -89,7 +89,7 @@ impl<'p> TaskGraph<'p> { let mut args = args; if let Some(name) = args.first() { - match search_envs.find_task(name, FindTaskSource::CmdArgs) { + match search_envs.find_task(TaskName(name.clone()), FindTaskSource::CmdArgs) { Err(FindTaskError::MissingTask(_)) => {} Err(FindTaskError::AmbiguousTask(err)) => { return Err(TaskGraphError::AmbiguousTask(err)) @@ -106,7 +106,7 @@ impl<'p> TaskGraph<'p> { project, search_envs, TaskNode { - name: Some(args.remove(0)), + name: Some(args.remove(0).into()), task: Cow::Borrowed(task), run_environment: run_env, additional_args: args, @@ -147,7 +147,7 @@ impl<'p> TaskGraph<'p> { search_environments: &SearchEnvironments<'p, D>, root: TaskNode<'p>, ) -> Result { - let mut task_name_to_node: HashMap = + let mut task_name_to_node: HashMap = HashMap::from_iter(root.name.clone().into_iter().map(|name| (name, TaskId(0)))); let mut nodes = vec![root]; @@ -169,7 +169,7 @@ impl<'p> TaskGraph<'p> { // Find the task in the project let node = &nodes[next_node_to_visit]; let (task_env, task_dependency) = match search_environments.find_task( - &dependency, + dependency.clone(), FindTaskSource::DependsOn( node.name .clone() @@ -202,7 +202,7 @@ impl<'p> TaskGraph<'p> { }); // Store the task id in the map to be able to look up the name later - task_name_to_node.insert(dependency, task_id); + task_name_to_node.insert(dependency.clone(), task_id); // Add the dependency to the node node_dependencies.push(task_id); diff --git a/tests/common/builders.rs b/tests/common/builders.rs index 835014507..4b98fc8d2 100644 --- a/tests/common/builders.rs +++ b/tests/common/builders.rs @@ -24,6 +24,7 @@ //! ``` use futures::FutureExt; +use pixi::task::TaskName; use pixi::{ cli::{add, init, install, project, task}, DependencyType, SpecType, @@ -155,8 +156,8 @@ impl TaskAddBuilder { } /// Depends on these commands - pub fn with_depends_on(mut self, depends: impl IntoIterator>) -> Self { - self.args.depends_on = Some(string_from_iter(depends)); + pub fn with_depends_on(mut self, depends: Vec) -> Self { + self.args.depends_on = Some(depends); self } @@ -182,8 +183,8 @@ pub struct TaskAliasBuilder { impl TaskAliasBuilder { /// Depends on these commands - pub fn with_depends_on(mut self, depends: impl IntoIterator>) -> Self { - self.args.depends_on = string_from_iter(depends); + pub fn with_depends_on(mut self, depends: Vec) -> Self { + self.args.depends_on = depends; self } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 6f50ce669..62638f5b6 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -22,6 +22,7 @@ use rattler_conda_types::{MatchSpec, Platform}; use miette::{Diagnostic, IntoDiagnostic}; use pixi::cli::run::get_task_env; use pixi::cli::LockFileUsageArgs; +use pixi::task::TaskName; use pixi::FeatureName; use pixi::TaskExecutionError; use rattler_lock::{LockFile, Package}; @@ -316,7 +317,7 @@ impl TasksControl<'_> { /// Add a task pub fn add( &self, - name: impl ToString, + name: TaskName, platform: Option, feature_name: FeatureName, ) -> TaskAddBuilder { @@ -324,7 +325,7 @@ impl TasksControl<'_> { TaskAddBuilder { manifest_path: Some(self.pixi.manifest_path()), args: AddArgs { - name: name.to_string(), + name, commands: vec![], depends_on: None, platform, @@ -337,14 +338,14 @@ impl TasksControl<'_> { /// Remove a task pub async fn remove( &self, - name: impl ToString, + name: TaskName, platform: Option, feature_name: Option, ) -> miette::Result<()> { task::execute(task::Args { manifest_path: Some(self.pixi.manifest_path()), operation: task::Operation::Remove(task::RemoveArgs { - names: vec![name.to_string()], + names: vec![name], platform, feature: feature_name, }), @@ -352,12 +353,12 @@ impl TasksControl<'_> { } /// Alias one or multiple tasks - pub fn alias(&self, name: impl ToString, platform: Option) -> TaskAliasBuilder { + pub fn alias(&self, name: TaskName, platform: Option) -> TaskAliasBuilder { TaskAliasBuilder { manifest_path: Some(self.pixi.manifest_path()), args: AliasArgs { platform, - alias: name.to_string(), + alias: name, depends_on: vec![], }, } diff --git a/tests/task_tests.rs b/tests/task_tests.rs index ead60d6a0..4cb47f367 100644 --- a/tests/task_tests.rs +++ b/tests/task_tests.rs @@ -1,5 +1,6 @@ use crate::common::PixiControl; use pixi::cli::run::Args; +use pixi::task::TaskName; use pixi::FeatureName; use pixi::{CmdArgs, Task}; use rattler_conda_types::Platform; @@ -15,18 +16,21 @@ pub async fn add_remove_task() { // Simple task pixi.tasks() - .add("test", None, FeatureName::Default) + .add("test".into(), None, FeatureName::Default) .with_commands(["echo hello"]) .execute() .unwrap(); let project = pixi.project().unwrap(); let tasks = project.default_environment().tasks(None, true).unwrap(); - let task = tasks.get("test").unwrap(); + let task = tasks.get(&::from("test")).unwrap(); assert!(matches!(task, Task::Plain(s) if s == "echo hello")); // Remove the task - pixi.tasks().remove("test", None, None).await.unwrap(); + pixi.tasks() + .remove("test".into(), None, None) + .await + .unwrap(); assert_eq!( pixi.project() .unwrap() @@ -45,21 +49,21 @@ pub async fn add_command_types() { // Add a command with dependencies pixi.tasks() - .add("test", None, FeatureName::Default) + .add("test".into(), None, FeatureName::Default) .with_commands(["echo hello"]) .execute() .unwrap(); pixi.tasks() - .add("test2", None, FeatureName::Default) + .add("test2".into(), None, FeatureName::Default) .with_commands(["echo hello", "echo bonjour"]) - .with_depends_on(["test"]) + .with_depends_on(vec!["test".into()]) .execute() .unwrap(); let project = pixi.project().unwrap(); let tasks = project.default_environment().tasks(None, true).unwrap(); - let task2 = tasks.get("test2").unwrap(); - let task = tasks.get("test").unwrap(); + let task2 = tasks.get(&::from("test2")).unwrap(); + let task = tasks.get(&::from("test")).unwrap(); assert!(matches!(task2, Task::Execute(cmd) if matches!(cmd.cmd, CmdArgs::Single(_)))); assert!(matches!(task2, Task::Execute(cmd) if !cmd.depends_on.is_empty())); @@ -71,14 +75,14 @@ pub async fn add_command_types() { // Create an alias pixi.tasks() - .alias("testing", None) - .with_depends_on(["test", "test3"]) + .alias("testing".into(), None) + .with_depends_on(vec!["test".into(), "test3".into()]) .execute() .unwrap(); let project = pixi.project().unwrap(); let tasks = project.default_environment().tasks(None, true).unwrap(); - let task = tasks.get("testing").unwrap(); - assert!(matches!(task, Task::Alias(a) if a.depends_on.get(0).unwrap() == "test")); + let task = tasks.get(&::from("testing")).unwrap(); + assert!(matches!(task, Task::Alias(a) if a.depends_on.get(0).unwrap().as_str() == "test")); } #[tokio::test] @@ -87,20 +91,20 @@ async fn test_alias() { pixi.init().without_channels().await.unwrap(); pixi.tasks() - .add("hello", None, FeatureName::Default) + .add("hello".into(), None, FeatureName::Default) .with_commands(["echo hello"]) .execute() .unwrap(); pixi.tasks() - .add("world", None, FeatureName::Default) + .add("world".into(), None, FeatureName::Default) .with_commands(["echo world"]) .execute() .unwrap(); pixi.tasks() - .add("helloworld", None, FeatureName::Default) - .with_depends_on(["hello", "world"]) + .add("helloworld".into(), None, FeatureName::Default) + .with_depends_on(vec!["hello".into(), "world".into()]) .execute() .unwrap(); @@ -126,7 +130,7 @@ pub async fn add_remove_target_specific_task() { // Simple task pixi.tasks() - .add("test", Some(Platform::Win64), FeatureName::Default) + .add("test".into(), Some(Platform::Win64), FeatureName::Default) .with_commands(["echo only_on_windows"]) .execute() .unwrap(); @@ -136,20 +140,20 @@ pub async fn add_remove_target_specific_task() { .default_environment() .tasks(Some(Platform::Win64), true) .unwrap() - .get("test") + .get(&::from("test")) .unwrap(); assert!(matches!(task, Task::Plain(s) if s == "echo only_on_windows")); // Simple task pixi.tasks() - .add("test", None, FeatureName::Default) + .add("test".into(), None, FeatureName::Default) .with_commands(["echo hello"]) .execute() .unwrap(); // Remove the task pixi.tasks() - .remove("test", Some(Platform::Win64), None) + .remove("test".into(), Some(Platform::Win64), None) .await .unwrap(); assert_eq!( @@ -172,7 +176,7 @@ async fn test_cwd() { fs::create_dir(pixi.project_path().join("test")).unwrap(); pixi.tasks() - .add("pwd-test", None, FeatureName::Default) + .add("pwd-test".into(), None, FeatureName::Default) .with_commands(["pwd"]) .with_cwd(PathBuf::from("test")) .execute() @@ -192,7 +196,7 @@ async fn test_cwd() { // Test that an unknown cwd gives an error pixi.tasks() - .add("unknown-cwd", None, FeatureName::Default) + .add("unknown-cwd".into(), None, FeatureName::Default) .with_commands(["pwd"]) .with_cwd(PathBuf::from("tests")) .execute()