You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

147 lines
4.6 KiB

use crate::{
cache::JobCache,
executor::{FutRes, Waiter, DynFut},
models::{Agent, AssignedJob, JobMeta, JobType},
utils::{CombinedResult, OneOrMany},
UError,
};
use guess_host_triple::guess_host_triple;
use std::collections::HashMap;
pub struct JobBuilder {
jobs: Waiter,
}
impl JobBuilder {
pub fn from_request<J: OneOrMany<AssignedJob>>(job_requests: J) -> CombinedResult<Self> {
let job_requests = job_requests.into_vec();
let mut prepared: Vec<DynFut> = vec![];
let mut result = CombinedResult::new();
for req in job_requests {
let job_meta = JobCache::get(&req.job_id);
if job_meta.is_none() {
result.err(UError::NoJob(req.job_id));
continue;
}
let job_meta = job_meta.unwrap();
let built_req = (|| {
Ok(match job_meta.exec_type {
JobType::Shell => {
let meta = JobCache::get(&req.job_id).ok_or(UError::NoJob(req.job_id))?;
let curr_platform = guess_host_triple().unwrap_or("unknown").to_string();
if meta.platform != curr_platform {
return Err(UError::InsuitablePlatform(
meta.platform.clone(),
curr_platform,
));
}
let job = AssignedJob::new(req.job_id, Some(&req));
prepared.push(Box::pin(job.run()))
}
JobType::Manage => prepared.push(Box::pin(Agent::run())),
_ => todo!(),
})
})();
if let Err(e) = built_req {
result.err(e)
}
}
result.ok(Self {
jobs: Waiter::new(prepared),
});
result
}
pub fn from_meta<J: OneOrMany<JobMeta>>(job_metas: J) -> CombinedResult<Self> {
let job_requests = job_metas
.into_vec()
.into_iter()
.map(|jm| {
let j_uid = jm.id;
JobCache::insert(jm);
AssignedJob::new(j_uid, None)
})
.collect::<Vec<AssignedJob>>();
JobBuilder::from_request(job_requests)
}
/// Spawn jobs and pop results later
pub async fn spawn(mut self) -> Self {
self.jobs = self.jobs.spawn().await;
self
}
/// Spawn jobs and wait for result
pub async fn wait(self) -> Vec<FutRes> {
self.jobs.spawn().await.wait().await
}
/// Spawn one job and wait for result
pub async fn wait_one(self) -> FutRes {
self.jobs.spawn().await.wait().await.pop().unwrap()
}
}
/// Store jobs and get results by name
pub struct NamedJobBuilder {
builder: Option<JobBuilder>,
job_names: Vec<&'static str>,
results: HashMap<&'static str, FutRes>,
}
impl NamedJobBuilder {
pub fn from_shell<J: OneOrMany<(&'static str, &'static str)>>(
named_jobs: J,
) -> CombinedResult<Self> {
let mut result = CombinedResult::new();
let jobs: Vec<(&'static str, JobMeta)> = named_jobs
.into_vec()
.into_iter()
.filter_map(
|(alias, cmd)| match JobMeta::builder().with_shell(cmd).build() {
Ok(meta) => Some((alias, meta)),
Err(e) => {
result.err(e);
None
}
},
)
.collect();
result.ok(Self::from_meta(jobs));
result
}
pub fn from_meta<J: OneOrMany<(&'static str, JobMeta)>>(named_jobs: J) -> Self {
let mut job_names = vec![];
let job_metas: Vec<JobMeta> = named_jobs
.into_vec()
.into_iter()
.map(|(alias, meta)| {
job_names.push(alias);
meta
})
.collect();
Self {
builder: Some(JobBuilder::from_meta(job_metas).unwrap_one()),
job_names,
results: HashMap::new(),
}
}
pub async fn wait(mut self) -> Self {
let results = self.builder.take().unwrap().wait().await;
for (name, result) in self.job_names.iter().zip(results.into_iter()) {
self.results.insert(name, result);
}
self
}
pub fn pop_opt(&mut self, name: &'static str) -> Option<FutRes> {
self.results.remove(name)
}
pub fn pop(&mut self, name: &'static str) -> FutRes {
self.pop_opt(name).unwrap()
}
}