use clap::{Args, Subcommand};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
config::client::parse_resources,
entity::state::{TaskExecState, TaskState},
schema::{
ChangeTaskReq, RemoteResourceDownload, SubmitTaskReq, TaskSpec, TasksCancelByFilterReq,
TasksCancelByUuidsReq, TasksQueryReq, TasksSubmitReq, UpdateTaskLabelsReq,
},
};
use super::{parse_key_val, parse_watch_task, ArtifactsArgs};
#[derive(Serialize, Debug, Deserialize, Args, derive_more::From, Clone)]
pub struct TasksArgs {
#[command(subcommand)]
pub command: TasksCommands,
}
#[derive(Subcommand, Serialize, Debug, Deserialize, derive_more::From, Clone)]
pub enum TasksCommands {
Submit(SubmitTaskArgs),
SubmitMany(SubmitTasksArgs),
Get(GetTaskArgs),
Query(QueryTasksArgs),
Cancel(CancelTaskArgs),
CancelMany(CancelTasksArgs),
CancelList(CancelTasksByUuidsArgs),
UpdateLabels(UpdateTaskLabelsArgs),
Change(ChangeTaskArgs),
Artifacts(ArtifactsArgs),
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct SubmitTaskArgs {
#[arg(short = 'g', long = "group")]
pub group_name: Option<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub tags: Vec<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub labels: Vec<String>,
#[arg(long, value_parser = humantime_serde::re::humantime::parse_duration, default_value="10min")]
pub timeout: std::time::Duration,
#[arg(short, long, default_value_t = 0)]
pub priority: i32,
#[arg(short, long, num_args = 0.., value_delimiter = ',', value_parser = parse_key_val::<String, String>)]
pub envs: Vec<(String, String)>,
#[arg(long = "terminal")]
pub terminal_output: bool,
#[arg(last = true)]
pub command: Vec<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',', value_parser = parse_resources)]
pub resources: Vec<RemoteResourceDownload>,
#[arg(long, value_parser = parse_watch_task::<Uuid, TaskExecState>)]
pub watch: Option<(Uuid, TaskExecState)>,
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct GetTaskArgs {
pub uuid: Uuid,
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct QueryTasksArgs {
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub creators: Vec<String>,
#[arg(short, long)]
pub group: Option<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub tags: Vec<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub labels: Vec<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub state: Vec<TaskState>,
#[arg(short, long)]
pub exit_status: Option<String>,
#[arg(short, long)]
pub priority: Option<String>,
#[arg(long)]
pub reporter_uuid: Option<Uuid>,
#[arg(long)]
pub limit: Option<u64>,
#[arg(long)]
pub offset: Option<u64>,
#[arg(short, long)]
pub verbose: bool,
#[arg(long)]
pub count: bool,
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct CancelTaskArgs {
pub uuid: Uuid,
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct CancelTasksArgs {
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub creators: Vec<String>,
#[arg(short, long)]
pub group: Option<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub tags: Vec<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub labels: Vec<String>,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub state: Vec<TaskState>,
#[arg(short, long)]
pub exit_status: Option<String>,
#[arg(short, long)]
pub priority: Option<String>,
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct CancelTasksByUuidsArgs {
#[arg(num_args = 1.., value_delimiter = ',')]
pub uuids: Vec<Uuid>,
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct UpdateTaskLabelsArgs {
pub uuid: Uuid,
#[arg(num_args = 0..)]
pub labels: Vec<String>,
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct ChangeTaskArgs {
pub uuid: Uuid,
#[arg(short, long, num_args = 0.., value_delimiter = ',')]
pub tags: Vec<String>,
#[arg(long, value_parser = humantime_serde::re::humantime::parse_duration)]
pub timeout: Option<std::time::Duration>,
#[arg(short, long)]
pub priority: Option<i32>,
#[arg(short, long, num_args = 0.., value_delimiter = ',', value_parser = parse_key_val::<String, String>)]
pub envs: Vec<(String, String)>,
#[arg(long = "terminal")]
pub terminal_output: bool,
#[arg(last = true)]
pub command: Vec<String>,
#[arg(skip)]
pub resources: Vec<RemoteResourceDownload>,
#[arg(long, value_parser = parse_watch_task::<Uuid, TaskExecState>)]
pub watch: Option<(Uuid, TaskExecState)>,
}
impl From<QueryTasksArgs> for TasksQueryReq {
fn from(args: QueryTasksArgs) -> Self {
Self {
reporter_uuid: args.reporter_uuid,
creator_usernames: if args.creators.is_empty() {
None
} else {
Some(args.creators.into_iter().collect())
},
group_name: args.group,
tags: if args.tags.is_empty() {
None
} else {
Some(args.tags.into_iter().collect())
},
labels: if args.labels.is_empty() {
None
} else {
Some(args.labels.into_iter().collect())
},
states: if args.state.is_empty() {
None
} else {
Some(args.state.into_iter().collect())
},
exit_status: args.exit_status,
priority: args.priority,
limit: args.limit,
offset: args.offset,
count: args.count,
}
}
}
impl From<ChangeTaskArgs> for ChangeTaskReq {
fn from(args: ChangeTaskArgs) -> Self {
let task_spec = if args.command.is_empty() {
None
} else {
Some(TaskSpec::new(
args.command,
args.envs,
args.resources,
args.terminal_output,
args.watch,
))
};
Self {
tags: if args.tags.is_empty() {
None
} else {
Some(args.tags.into_iter().collect())
},
timeout: args.timeout,
priority: args.priority,
task_spec,
}
}
}
impl From<UpdateTaskLabelsArgs> for UpdateTaskLabelsReq {
fn from(args: UpdateTaskLabelsArgs) -> Self {
Self {
labels: args.labels.into_iter().collect(),
}
}
}
impl From<CancelTasksArgs> for TasksCancelByFilterReq {
fn from(args: CancelTasksArgs) -> Self {
Self {
creator_usernames: if args.creators.is_empty() {
None
} else {
Some(args.creators.into_iter().collect())
},
group_name: args.group,
tags: if args.tags.is_empty() {
None
} else {
Some(args.tags.into_iter().collect())
},
labels: if args.labels.is_empty() {
None
} else {
Some(args.labels.into_iter().collect())
},
states: if args.state.is_empty() {
None
} else {
Some(args.state.into_iter().collect())
},
exit_status: args.exit_status,
priority: args.priority,
}
}
}
impl From<CancelTasksByUuidsArgs> for TasksCancelByUuidsReq {
fn from(args: CancelTasksByUuidsArgs) -> Self {
Self { uuids: args.uuids }
}
}
#[derive(Serialize, Debug, Deserialize, Args, Clone)]
pub struct SubmitTasksArgs {
#[arg(short, long)]
pub file: std::path::PathBuf,
}
impl SubmitTasksArgs {
pub fn load_tasks(&self) -> crate::error::Result<Vec<SubmitTaskReq>> {
let content = std::fs::read_to_string(&self.file)?;
let tasks: Vec<SubmitTaskReq> = serde_json::from_str(&content)?;
Ok(tasks)
}
}
impl TryFrom<SubmitTasksArgs> for TasksSubmitReq {
type Error = crate::error::Error;
fn try_from(args: SubmitTasksArgs) -> Result<Self, Self::Error> {
let tasks = args.load_tasks()?;
Ok(Self { tasks })
}
}