use crate::indicators::{BinaryCrateName, GroupEnd, SpawningMode};
use crate::output::{DynErr, DynErrResult, OptOutput, ProcessOutput};
use crate::task;
use core::borrow::Borrow;
use core::ops::{Deref, DerefMut};
use core::time::Duration;
use phantom_newtype::Id;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::io::{self, Result as IoResult, Write};
use std::process::Child;
use std::thread;
const SLEEP_BETWEEN_CHECKING_CHILDREN: Duration = Duration::from_millis(10);
pub type ChildProcess = Child;
pub type ChildProcessId = Id<ChildProcess, u32>;
pub type ChildInfo = String;
pub struct ChildInfoMeta<M>(ChildProcess, ChildInfo, M);
impl<M> ChildInfoMeta<M> {
pub fn child(&self) -> &ChildProcess {
&self.0
}
pub fn info(&self) -> &ChildInfo {
&self.1
}
pub fn meta(&self) -> &M {
&self.2
}
pub fn meta_mut(&mut self) -> &mut M {
&mut self.2
}
}
impl<M> ChildInfoMeta<M>
where
M: Copy,
{
pub fn meta_copy(&self) -> M {
self.2
}
}
pub type GroupOfChildren<M> = HashMap<ChildProcessId, ChildInfoMeta<M>>;
pub type Features<'a, S> = Vec<&'a S >;
pub type ParallelTasks<'a, S, M> = Vec<(
&'a S, /* subdir */
&'a BinaryCrateName<'a, S>,
Features<'a, S>,
ChildInfo,
M,
)>;
pub(crate) type GroupExecution<M> = (GroupOfChildren<M>, SpawningMode);
pub(crate) type GroupOfChildrenAndOptOutput<M> = (GroupOfChildren<M>, OptOutput<M>);
pub(crate) type GroupExecutionAndStartErrors<M> = (GroupExecution<M>, Vec<DynErr>);
pub fn start_parallel_tasks<'a, S, M>(
tasks: ParallelTasks<'a, S, M>,
parent_dir: &'a S,
until: &'a GroupEnd,
) -> GroupExecutionAndStartErrors<M>
where
S: Borrow<str> + 'a + ?Sized,
&'a S: Borrow<str>,
{
let mut children = GroupOfChildren::new();
let mut spawning_mode = SpawningMode::default();
let mut errors = Vec::with_capacity(0);
for (sub_dir, binary_crate, features, child_info, meta) in tasks {
let child_or_err = task::spawn(parent_dir, sub_dir, binary_crate, &features);
match child_or_err {
Ok(child) => {
children.insert(child.id().into(), ChildInfoMeta(child, child_info, meta));
}
Err(err) => {
spawning_mode = until.mode_after_error_in_same_group();
errors.push(err);
}
};
}
((children, spawning_mode), errors)
}
pub(crate) fn try_finished_child<M>(
children: &mut GroupOfChildren<M>,
) -> DynErrResult<Option<ChildProcessId>> {
for (child_id, ChildInfoMeta(child, _, _)) in children.iter_mut() {
let opt_status_or_err = child.try_wait();
match opt_status_or_err {
Ok(Some(_exit_status)) => {
return Ok(Some(*child_id));
}
Ok(None) => {}
Err(err) => return Err(Box::new(err)),
}
}
Ok(None)
}
pub(crate) fn print_output(output: &ProcessOutput) -> IoResult<()> {
{
let mut stdout = io::stdout().lock();
write!(stdout, "Exit status: {}", output.status)?;
stdout.write_all(&output.stdout)?;
}
{
let mut stderr = io::stderr().lock();
stderr.write_all(&output.stderr)?;
if !output.stderr.is_empty() {
stderr.flush()?;
}
}
Ok(())
}
#[must_use]
pub fn collect_finished_child<M>(
mut children: GroupOfChildren<M>,
) -> Option<GroupOfChildrenAndOptOutput<M>> {
let finished_result = try_finished_child(&mut children);
match finished_result {
Ok(Some(child_id)) => {
let ChildInfoMeta(child, child_info, meta) = children.remove(&child_id).unwrap();
let (child_output, err) = match child.wait_with_output() {
Ok(child_output) => (Some(child_output), None),
Err(err) => (
None,
Some({
let err: Box<dyn StdError> = Box::new(err);
err
}),
),
};
Some((
children,
Some((Some((child_output, child_info, meta)), err)),
))
}
Ok(None) => {
if children.is_empty() {
None
} else {
Some((children, None))
}
}
Err(err) => Some((children, Some((None, Some(err))))),
}
}
#[must_use]
pub fn life_cycle_step<M>(
(mut _children, mut _spawning_mode): GroupExecution<M>,
_until: &GroupEnd,
) -> DynErrResult<()> {
thread::sleep(SLEEP_BETWEEN_CHECKING_CHILDREN);
panic!()
}
#[must_use]
pub fn life_cycle_loop<M>(
(mut _children, mut _spawning_mode): GroupExecution<M>,
_until: &GroupEnd,
) -> DynErrResult<()> {
loop {
panic!();
}
}