1use std::collections::vec_deque::Iter;
2use std::collections::VecDeque;
3use std::iter::{Chain, Iterator, Once};
4use std::process::ExitStatus;
5use std::rc::Rc;
6use std::sync::Arc;
7
8use futures::future::join_all;
9use futures::join;
10
11use async_recursion::async_recursion;
12use async_trait::async_trait;
13
14use scriptplan_lang_utils::{has_parameters, apply_args};
15
16#[async_trait]
17pub trait Command {
18 async fn run(&self, args: VarArgs) -> Result<ExitStatus, ()>;
19}
20
21pub type VarArgs = VecDeque<Arc<String>>;
22#[derive(Debug)]
23pub struct ScriptGroup<CommandGeneric: Command> {
24 pub bail: bool,
25 pub first: Script<CommandGeneric>,
27 pub rest: VecDeque<Script<CommandGeneric>>,
28}
29
30impl<CommandGeneric: Command> ScriptGroup<CommandGeneric> {
31 fn iter(&self) -> Chain<Once<&'_ Script<CommandGeneric>>, Iter<'_, Script<CommandGeneric>>> {
32 std::iter::once(&self.first).chain(self.rest.iter())
33 }
34}
35
36fn clone_args(args: &VarArgs) -> VarArgs {
38 return args.iter().map(|x| x.clone()).collect();
39}
40
41#[derive(Debug)]
45pub enum CommandGroup<CommandGeneric: Command> {
46 Parallel(ScriptGroup<CommandGeneric>),
47 Series(ScriptGroup<CommandGeneric>),
48}
49
50fn merge_status(status1: ExitStatus, status2: ExitStatus) -> ExitStatus {
51 if !status1.success() {
52 return status2;
53 }
54 return status1;
55}
56
57
58impl<CommandGeneric: Command> CommandGroup<CommandGeneric> {
59 async fn run(
60 &self,
61 parser: &impl ScriptParser<CommandGeneric>,
62 args: VarArgs,
63 ) -> Result<ExitStatus, ()> {
64 async fn run_script<'a, CommandGeneric: Command>(script: &'a Script<CommandGeneric>, args: &VarArgs, parser: &impl ScriptParser<CommandGeneric>) -> Result<ExitStatus, ()> {
65 match script {
66 Script::Alias(alias) => alias.run(parser, if has_parameters(&alias.args) { clone_args(&args) } else { VecDeque::new( ) }).await,
67 default => default.run(parser, clone_args(&args)).await
68 }
69 }
70 match self {
72 Self::Parallel(group) => {
73 if group.bail {
74 println!("Warning: Bail in parallel groups are currently not supported");
75 }
76
77 let mut promises = Vec::<_>::new();
78
79 let mut group_iter = group.iter();
80 let mut command = group_iter.next().unwrap();
81
82 while let Some(next_command) = group_iter.next() {
83 promises.push(run_script(command, &args, parser));
84
85 command = next_command;
86 }
87
88 let last_result = run_script(command, &args, parser);
89 let (results, last) = join!(join_all(promises), last_result);
90
91 let final_status =
92 results
93 .into_iter()
94 .fold(last.unwrap(), |prev_exit_status, this_result| {
95 if let Ok(current_status) = this_result {
96 return merge_status(prev_exit_status, current_status);
97 } else {
98 return prev_exit_status;
100 }
101 });
102
103 return Ok(final_status);
104 }
105 Self::Series(group) => {
106 let mut rest_iter = group.rest.iter();
107 if let Some(last_command) = rest_iter.next_back() {
108 let mut exit_status = run_script(&group.first, &args, parser).await.unwrap();
109
110 for command in rest_iter {
111 exit_status = merge_status(
112 exit_status,
113 run_script(&command, &args, parser).await.unwrap(),
114 );
115 }
116
117 exit_status = merge_status(
118 exit_status,
119 run_script(last_command, &args, parser).await.unwrap(),
120 );
121
122 Ok(exit_status)
123 } else {
124 Ok(run_script(&group.first, &args, parser).await.unwrap())
125 }
126 }
127 }
128 }
129}
130
131#[derive(Debug)]
132pub struct Alias {
133 pub task: String,
134 pub args: VarArgs,
135}
136
137#[derive(Debug)]
141pub enum Script<CommandGeneric: Command> {
142 Command(CommandGeneric),
143 Group(Box<CommandGroup<CommandGeneric>>),
144 Alias(Alias),
145}
146
147impl Alias {
148 #[async_recursion(?Send)]
149 pub async fn run<CommandGeneric : Command>(
150 &self,
151 parser: &impl ScriptParser<CommandGeneric>,
152 args: VarArgs,
153 ) -> Result<ExitStatus, ()> {
154 let final_args = (|| {
155 let has_params = has_parameters(&self.args);
156
157 if has_params {
158 apply_args(&self.args, &args)
159 } else {
160 let joined_args: VecDeque<Arc<String>> = self
161 .args
162 .iter()
163 .into_iter()
164 .map(|arg| arg.clone())
165 .chain(args.into_iter())
166 .collect();
167
168 return joined_args;
169 }
170 })();
171
172 parser
173 .parse(self.task.as_str())
174 .unwrap()
175 .run(parser, final_args)
176 .await
177 }
178}
179
180impl<CommandGeneric: Command> Script<CommandGeneric> {
181 #[async_recursion(?Send)]
182 pub async fn run(
183 &self,
184 parser: &impl ScriptParser<CommandGeneric>,
185 args: VarArgs,
186 ) -> Result<ExitStatus, ()> {
187 match self {
188 Script::Command(cmd) => cmd.run(args).await,
189 Script::Group(group) => group.run(parser, args).await,
190 Script::Alias(alias) => alias.run(parser, args).await,
191 }
192 }
193}
194
195pub trait ScriptParser<CommandGeneric: Command> {
196 fn parse(&self, task: &str) -> Result<Rc<Script<CommandGeneric>>, ()>;
197}