1use clap::{Args, Subcommand};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5use crate::{
6 config::client::parse_resources,
7 entity::state::{TaskExecState, TaskState},
8 schema::{ChangeTaskReq, RemoteResourceDownload, TaskSpec, TasksQueryReq, UpdateTaskLabelsReq},
9};
10
11use super::{parse_key_val, parse_watch_task, ArtifactsArgs};
12
13#[derive(Serialize, Debug, Deserialize, Args, derive_more::From, Clone)]
14pub struct TasksArgs {
15 #[command(subcommand)]
16 pub command: TasksCommands,
17}
18
19#[derive(Subcommand, Serialize, Debug, Deserialize, derive_more::From, Clone)]
20pub enum TasksCommands {
21 Submit(SubmitTaskArgs),
23 Get(GetTaskArgs),
25 Query(QueryTasksArgs),
27 Cancel(CancelTaskArgs),
29 UpdateLabels(UpdateTaskLabelsArgs),
31 Change(ChangeTaskArgs),
33 Artifacts(ArtifactsArgs),
35}
36
37#[derive(Serialize, Debug, Deserialize, Args, Clone)]
38pub struct SubmitTaskArgs {
39 #[arg(short = 'g', long = "group")]
41 pub group_name: Option<String>,
42 #[arg(short, long, num_args = 0.., value_delimiter = ',')]
44 pub tags: Vec<String>,
45 #[arg(short, long, num_args = 0.., value_delimiter = ',')]
47 pub labels: Vec<String>,
48 #[arg(long, value_parser = humantime_serde::re::humantime::parse_duration, default_value="10min")]
50 pub timeout: std::time::Duration,
51 #[arg(short, long, default_value_t = 0)]
53 pub priority: i32,
54 #[arg(short, long, num_args = 0.., value_delimiter = ',', value_parser = parse_key_val::<String, String>)]
56 pub envs: Vec<(String, String)>,
57 #[arg(long = "terminal")]
59 pub terminal_output: bool,
60 #[arg(last = true)]
62 pub command: Vec<String>,
63 #[arg(short, long, num_args = 0.., value_delimiter = ',', value_parser = parse_resources)]
64 pub resources: Vec<RemoteResourceDownload>,
65 #[arg(long, value_parser = parse_watch_task::<Uuid, TaskExecState>)]
68 pub watch: Option<(Uuid, TaskExecState)>,
69}
70
71#[derive(Serialize, Debug, Deserialize, Args, Clone)]
72pub struct GetTaskArgs {
73 pub uuid: Uuid,
75}
76
77#[derive(Serialize, Debug, Deserialize, Args, Clone)]
78pub struct QueryTasksArgs {
79 #[arg(short, long, num_args = 0.., value_delimiter = ',')]
81 pub creators: Vec<String>,
82 #[arg(short, long)]
84 pub group: Option<String>,
85 #[arg(short, long, num_args = 0.., value_delimiter = ',')]
87 pub tags: Vec<String>,
88 #[arg(short, long, num_args = 0.., value_delimiter = ',')]
90 pub labels: Vec<String>,
91 #[arg(short, long, num_args = 0.., value_delimiter = ',')]
93 pub state: Vec<TaskState>,
94 #[arg(short, long)]
96 pub exit_status: Option<String>,
97 #[arg(short, long)]
99 pub priority: Option<String>,
100 #[arg(long)]
102 pub limit: Option<u64>,
103 #[arg(long)]
105 pub offset: Option<u64>,
106 #[arg(short, long)]
108 pub verbose: bool,
109 #[arg(long)]
111 pub count: bool,
112}
113
114#[derive(Serialize, Debug, Deserialize, Args, Clone)]
115pub struct CancelTaskArgs {
116 pub uuid: Uuid,
118}
119
120#[derive(Serialize, Debug, Deserialize, Args, Clone)]
121pub struct UpdateTaskLabelsArgs {
122 pub uuid: Uuid,
124 #[arg(num_args = 0..)]
126 pub labels: Vec<String>,
127}
128
129#[derive(Serialize, Debug, Deserialize, Args, Clone)]
130pub struct ChangeTaskArgs {
131 pub uuid: Uuid,
133 #[arg(short, long, num_args = 0.., value_delimiter = ',')]
135 pub tags: Vec<String>,
136 #[arg(long, value_parser = humantime_serde::re::humantime::parse_duration)]
138 pub timeout: Option<std::time::Duration>,
139 #[arg(short, long)]
141 pub priority: Option<i32>,
142 #[arg(short, long, num_args = 0.., value_delimiter = ',', value_parser = parse_key_val::<String, String>)]
144 pub envs: Vec<(String, String)>,
145 #[arg(long = "terminal")]
147 pub terminal_output: bool,
148 #[arg(last = true)]
150 pub command: Vec<String>,
151 #[arg(skip)]
152 pub resources: Vec<RemoteResourceDownload>,
153 #[arg(long, value_parser = parse_watch_task::<Uuid, TaskExecState>)]
156 pub watch: Option<(Uuid, TaskExecState)>,
157}
158
159impl From<QueryTasksArgs> for TasksQueryReq {
160 fn from(args: QueryTasksArgs) -> Self {
161 Self {
162 creator_usernames: if args.creators.is_empty() {
163 None
164 } else {
165 Some(args.creators.into_iter().collect())
166 },
167 group_name: args.group,
168 tags: if args.tags.is_empty() {
169 None
170 } else {
171 Some(args.tags.into_iter().collect())
172 },
173 labels: if args.labels.is_empty() {
174 None
175 } else {
176 Some(args.labels.into_iter().collect())
177 },
178 states: if args.state.is_empty() {
179 None
180 } else {
181 Some(args.state.into_iter().collect())
182 },
183 exit_status: args.exit_status,
184 priority: args.priority,
185 limit: args.limit,
186 offset: args.offset,
187 count: args.count,
188 }
189 }
190}
191
192impl From<ChangeTaskArgs> for ChangeTaskReq {
193 fn from(args: ChangeTaskArgs) -> Self {
194 let task_spec = if args.command.is_empty() {
195 None
196 } else {
197 Some(TaskSpec::new(
198 args.command,
199 args.envs,
200 args.resources,
201 args.terminal_output,
202 args.watch,
203 ))
204 };
205 Self {
206 tags: if args.tags.is_empty() {
207 None
208 } else {
209 Some(args.tags.into_iter().collect())
210 },
211 timeout: args.timeout,
212 priority: args.priority,
213 task_spec,
214 }
215 }
216}
217
218impl From<UpdateTaskLabelsArgs> for UpdateTaskLabelsReq {
219 fn from(args: UpdateTaskLabelsArgs) -> Self {
220 Self {
221 labels: args.labels.into_iter().collect(),
222 }
223 }
224}