sofie_cli/
tasks_command.rs1use sofie_model::naming::task_name;
2use sofie_model::{App, Module, Task};
3
4use crate::routes_command::text_table;
5use crate::sorted;
6
7const HEADERS: [&str; 4] = ["NAME", "ARGUMENTS", "FUNCTION", "DESCRIPTION"];
8
9#[must_use]
10pub fn task_table(app: &App) -> String {
11 let mut rows: Vec<[String; 4]> = sorted::modules(app)
12 .into_iter()
13 .flat_map(|module| module.tasks.iter().map(move |task| row(module, task)))
14 .collect();
15 rows.sort();
16 text_table(HEADERS, &rows)
17}
18
19fn row(module: &Module, task: &Task) -> [String; 4] {
20 let function = module.code.function(&task.function);
21 [
22 task_name(module, &task.function),
23 task_arguments(module, task),
24 format!("{}::{}", module.krate.crate_name, task.function.join("::")),
25 function
26 .and_then(|function| function.doc.clone())
27 .unwrap_or_default(),
28 ]
29}
30
31pub(crate) fn task_arguments(module: &Module, task: &Task) -> String {
33 let Some(input) = module
34 .code
35 .function(&task.function)
36 .and_then(|function| module.code.input_of(function))
37 else {
38 return String::new();
39 };
40 let fields: Vec<String> = input
41 .fields
42 .iter()
43 .map(|field| match field.ty.arguments.as_slice() {
44 [inner] if field.ty.name == "Option" => format!("[--{} {inner}]", field.name),
45 _ => format!("--{} {}", field.name, field.ty),
46 })
47 .collect();
48 fields.join(" ")
49}
50
51#[cfg(test)]
52mod tests {
53 use std::path::PathBuf;
54
55 use sofie_model::testing::{app, field, function, module, type_name};
56 use sofie_model::{InputDeclaration, Task};
57
58 use super::task_table;
59
60 #[test]
61 fn lists_each_task_with_the_arguments_its_input_reads() {
62 let mut order = module("order", "shop/modules/order");
63 let file = "shop/modules/order/src/tasks.rs";
64 let mut backfill = function("tasks::backfill", file, 3);
65 backfill.arguments = vec![type_name("Read"), type_name("Since")];
66 backfill.doc = Some("Fills totals of orders placed before totals were kept.".to_owned());
67 order.tasks = vec![Task {
68 file: PathBuf::from(file),
69 line: 3,
70 function: backfill.path.clone(),
71 }];
72 order.code.functions = vec![backfill];
73 order.code.inputs = vec![InputDeclaration {
74 name: "Since".to_owned(),
75 path: vec!["tasks".to_owned(), "Since".to_owned()],
76 from: None,
77 fields: vec![field("since", "Date", 1), field("limit", "Option<u32>", 1)],
78 unconstrained: Vec::new(),
79 file: PathBuf::from(file),
80 line: 1,
81 }];
82
83 let table = task_table(&app("shop", vec![order]));
84
85 let row: Vec<&str> = table
86 .lines()
87 .nth(1)
88 .unwrap()
89 .split(" ")
90 .filter(|cell| !cell.is_empty())
91 .map(str::trim)
92 .collect();
93 assert_eq!(
94 row,
95 [
96 "order.backfill",
97 "--since Date [--limit u32]",
98 "order::tasks::backfill",
99 "Fills totals of orders placed before totals were kept."
100 ],
101 "{table}"
102 );
103 }
104}