use std::collections::HashMap;
use std::env::args;
use std::sync::Arc;
use super::Input;
use wd_tools::sync::WaitGroup;
use crate::{Cmd,Flags};
use crate::interface::{Task, TaskInfo};
#[derive(Default)]
pub struct Application {
desc: String,
info : Vec<TaskInfo>,
tasks : HashMap<String,Arc<dyn Task>>
}
impl Application {
pub fn new(desc:impl ToString)->Self{
Self{desc:desc.to_string(),
..Default::default()
}
}
pub fn register_task<T:Task + 'static>(mut self, task:T) ->Self{
let info = task.info();
let name = info.name.clone();
self.info.push(info);
self.tasks.insert(name,Arc::new(task));self
}
fn build(&self,flags:Flags) ->Vec<(Arc<dyn Task>,Input)>{
let mut tasks = vec![];
for i in flags.cmds.iter() {
let task = match self.tasks.get(i.cmd.as_str()){
None => { wd_log::log_panic!("not found cmd[{}] handler",i.cmd.as_str())},
Some(s)=>s.clone(),
};
let input = Application::build_input(i, &flags,task.info());
tasks.push((task,input));
}
tasks
}
pub fn show(self){
println!("description:{}",self.desc);
println!("format:");
let app_name = args().next().unwrap_or("application".to_string());
println!("{} [OPTION] [[ARGS]]",app_name);
for i in self.info.iter() {
println!(" {} {}",i.name,i.help);
for (arg,def,des) in i.flags.iter() {
println!(" {} : {}. default[{}]",arg,des,def);
}
}
}
pub async fn run(self){
let flags = Flags::parse_args();
if flags.cmds.is_empty() {
self.show();
return;
}
let tasks = self.build(flags);
let wg = WaitGroup::default();
for (t,i) in tasks.iter(){
let task = t.clone();
let input = i.clone();
wg.defer(move ||async move{
if let Err(e) = task.run(input).await {
wd_log::log_error_ln!("run failed:{:?}",e);
}
})
}
Application::wait_exit_sigle(wg).await;
for (t,i) in tasks.into_iter().rev() { if let Err(e) = t.exit(i).await{
wd_log::log_error_ln!("exit error:{:?}",e);
}
}
wd_log::log_info_ln!("process exit success")
}
async fn wait_exit_sigle(wg:WaitGroup){
let exit = async {
if let Err(e) = tokio::signal::ctrl_c().await {
wd_log::log_error_ln!("lister exit sigle error:{:?}",e);
}
};
tokio::select! {
_= exit =>{
wd_log::log_debug_ln!("listed exit sigle, start quit process");
}
_= wg.wait() =>{
wd_log::log_debug_ln!("all task over, start quit process");
}
}
}
fn build_input(cmd:&Cmd,flags:&Flags,info:TaskInfo)-> Input {
let mut ctx = Input::default();
for (k,v) in flags.args.iter() {
ctx.store(k,v);
}
for (k,v,_) in info.flags.iter() {
ctx.store(k,v);
}
for (k,v) in cmd.args.iter() {
ctx.store(k,v);
}
return ctx
}
}