use crate::config::Config;
use crate::task::{Deps, Task};
use crate::ui::style::{self};
use crate::ui::tree::print_tree;
use console::style;
use eyre::{eyre, Result};
use itertools::Itertools;
use petgraph::dot::Dot;
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct TasksDeps {
#[clap(verbatim_doc_comment)]
pub tasks: Option<Vec<String>>,
#[clap(long, verbatim_doc_comment)]
pub hidden: bool,
#[clap(long, alias = "dot", verbatim_doc_comment)]
pub dot: bool,
}
impl TasksDeps {
pub fn run(self) -> Result<()> {
let tasks = if self.tasks.is_none() {
self.get_all_tasks()?
} else {
self.get_task_lists()?
};
if self.dot {
self.print_deps_dot(tasks)?;
} else {
self.print_deps_tree(tasks)?;
}
Ok(())
}
fn get_all_tasks(&self) -> Result<Vec<Task>> {
Ok(Config::get()
.tasks()?
.values()
.filter(|t| self.hidden || !t.hide)
.cloned()
.collect())
}
fn get_task_lists(&self) -> Result<Vec<Task>> {
let config = Config::get();
let tasks = config.tasks()?;
let tasks = self.tasks.as_ref().map(|t| {
t.iter()
.map(|tn| match tasks.get(tn).cloned() {
Some(task) => Ok(task.clone()),
None => Err(self.err_no_task(tn.as_str())),
})
.collect::<Result<Vec<Task>>>()
});
match tasks {
Some(Ok(tasks)) => Ok(tasks),
Some(Err(e)) => Err(e),
None => Ok(vec![]),
}
}
fn print_deps_tree(&self, tasks: Vec<Task>) -> Result<()> {
let deps = Deps::new(tasks.clone())?;
let start_indexes = deps.graph.node_indices().filter(|&idx| {
let task = &deps.graph[idx];
tasks.iter().any(|t| t.name == task.name)
});
for idx in start_indexes {
print_tree(&(&deps.graph, idx))?;
}
Ok(())
}
fn print_deps_dot(&self, tasks: Vec<Task>) -> Result<()> {
let deps = Deps::new(tasks)?;
miseprintln!(
"{:?}",
Dot::with_attr_getters(
&deps.graph,
&[
petgraph::dot::Config::NodeNoLabel,
petgraph::dot::Config::EdgeNoLabel
],
&|_, _| String::new(),
&|_, nr| format!("label = \"{}\"", nr.1.name),
),
);
Ok(())
}
fn err_no_task(&self, t: &str) -> eyre::Report {
let config = Config::get();
let tasks = config
.tasks()
.map(|t| t.keys().collect::<Vec<_>>())
.unwrap_or_default();
let task_names = tasks.into_iter().map(style::ecyan).join(", ");
let t = style(&t).yellow().for_stderr();
eyre!("no tasks named `{t}` found. Available tasks: {task_names}")
}
}
static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
# Show dependencies for all tasks
$ <bold>mise tasks deps</bold>
# Show dependencies for the "lint", "test" and "check" tasks
$ <bold>mise tasks deps lint test check</bold>
# Show dependencies in DOT format
$ <bold>mise tasks deps --dot</bold>
"#
);