use shep_client::Client;
use shep_core::protocol::{ProcessInfo, Request, Response, SelectorSpec};
use crate::commands::lifecycle::any_restart_failed;
use crate::commands::lifecycle::selectors::request_each;
use crate::exit::ExitCode;
use crate::output::Streams;
fn is_live(info: &ProcessInfo) -> bool {
use shep_core::status::ProcStatus;
matches!(
info.status,
ProcStatus::Online | ProcStatus::Starting | ProcStatus::Stopping
)
}
pub(crate) async fn resume_all(
client: &Client,
streams: &mut Streams<'_>,
selector: Option<&str>,
matched: &[ProcessInfo],
started: &mut Vec<ProcessInfo>,
) -> ExitCode {
let (live, asleep): (Vec<&ProcessInfo>, Vec<&ProcessInfo>) =
matched.iter().partition(|info| is_live(info));
match live.as_slice() {
[] => {}
[one] => {
let retype = selector.unwrap_or(one.name.as_str());
let message = format!(
"{} is already {}; `shep restart {retype}` replaces it.",
one.name, one.status
);
streams.aside("start", &message);
}
several => {
let names: Vec<&str> = unique_names(several);
let retype = selector.map_or_else(|| names.join(" "), str::to_string);
let message = format!(
"{} are already running; `shep restart {retype}` replaces them.",
names.join(", ")
);
streams.aside("start", &message);
}
}
let mut failure: Option<ExitCode> = None;
for sheep in asleep {
let code = resume(client, streams, sheep, started).await;
if code != ExitCode::Success {
failure = failure.or(Some(code));
}
}
failure.unwrap_or(ExitCode::Success)
}
pub(crate) fn already_registered(streams: &mut Streams<'_>, matched: &[ProcessInfo]) -> ExitCode {
let refs: Vec<&ProcessInfo> = matched.iter().collect();
let names = unique_names(&refs);
let message = match names.as_slice() {
[] => return ExitCode::Success,
[one] => format!("{one} is already registered; nothing to add."),
several => format!(
"{} are already registered; nothing to add.",
several.join(", ")
),
};
streams.aside("add", &message);
ExitCode::Success
}
pub(crate) fn unique_names<'a>(infos: &[&'a ProcessInfo]) -> Vec<&'a str> {
let mut names: Vec<&str> = Vec::with_capacity(infos.len());
for info in infos {
if !names.contains(&info.name.as_str()) {
names.push(&info.name);
}
}
names
}
pub(crate) async fn resume(
client: &Client,
streams: &mut Streams<'_>,
sheep: &ProcessInfo,
started: &mut Vec<shep_core::protocol::ProcessInfo>,
) -> ExitCode {
let (procs, failure) = request_each(
client,
streams,
&[SelectorSpec::Id(sheep.id)],
None,
|selector| Request::Restart { selector },
|response| match response {
Response::Restarted { accepted, .. } => Some(accepted),
_ => None,
},
)
.await;
if any_restart_failed(&procs) {
let (name, id) = (&sheep.name, sheep.id);
let message = format!(
"{name} (id {id}) could not be started; see `shep bleats {id}` or its log files for why"
);
return streams.fail(ExitCode::SpawnFailed, &message);
}
started.extend(procs);
failure.unwrap_or(ExitCode::Success)
}