use std::collections::BTreeMap;
use std::path::Path;
use shep_client::{Client, ConnectError};
use shep_core::paths::ShepPaths;
use shep_core::permissions::Grants;
use shep_core::protocol::{DogSource, EnvValue};
use crate::commands::shep_toml::{ShepToml, ShepTomlError};
use crate::exit::ExitCode;
use crate::output::{DogActionRow, DogActionStatus, Streams, emit, write_outcome};
use crate::version_guard::{VersionGuard, refuse_version_skew};
mod adopt;
mod barks;
mod consent;
mod depends;
mod disable;
mod enable;
mod entry_drift;
mod grant;
mod grants;
mod remove;
mod second_look;
mod upgrade;
mod vet;
#[cfg(all(test, unix))]
mod adopt_consent_tests;
#[cfg(all(test, unix))]
mod adopt_declined_tests;
#[cfg(test)]
mod consent_tests;
#[cfg(all(test, unix))]
mod defaults_adoption_tests;
#[cfg(all(test, unix))]
mod emit_dog_action_tests;
#[cfg(test)]
mod enable_builtin_tests;
#[cfg(test)]
mod grant_builtin_tests;
#[cfg(all(test, unix))]
mod grant_record_tests;
#[cfg(all(test, unix))]
mod grant_tests;
#[cfg(all(test, unix))]
mod manifest_adoption_tests;
#[cfg(test)]
mod second_look_race_tests;
#[cfg(test)]
mod second_look_tests;
#[cfg(test)]
mod test_support;
#[cfg(all(test, unix))]
mod upgrade_tests;
pub use adopt::adopt;
pub use barks::barks;
pub use disable::disable;
pub use enable::enable;
#[cfg(test)]
pub(crate) use entry_drift::test_support as entry_drift_support;
pub(crate) use entry_drift::{
DOG_ENTRY_KEPT_NOTICE, kept_entries, kept_entries_among, kept_entry_report,
};
pub(crate) use grant::{Change, GrantRefusal, change, record_change};
pub use remove::rehome;
pub(crate) use remove::{Removal, remove_dog};
pub(crate) use upgrade::{upgrade_grants, upgrade_notice};
pub use vet::{DogSchema, warn_of_a_dog_a_restart_would_break};
pub(crate) use adopt::collides_with_a_verb;
pub(crate) use vet::cached_manifest;
pub(crate) use disable::disable_in_config;
pub(crate) use enable::{EnableRefusal, enable_in_config, join_with_and};
pub(crate) use vet::{ManifestRead, VERSION_BUDGET, ask_schema, dog_env, read_manifest};
const NO_SHEPHERD_ENABLE_STATUS: DogActionStatus =
DogActionStatus::NoShepherd("will start with the next shepherd");
const DECLINED_STATUS: DogActionStatus =
DogActionStatus::NoShepherd("not enabled; nothing was granted");
const NO_SHEPHERD_DISABLE_STATUS: DogActionStatus =
DogActionStatus::NoShepherd("not running; will not start with the next shepherd");
const DISABLED_STATUS: DogActionStatus = DogActionStatus::Acted("stopped");
fn emit_dog_action(
streams: &mut Streams<'_>,
verb: &str,
name: &str,
source: impl Into<Option<DogSource>>,
status: DogActionStatus,
) -> ExitCode {
write_outcome(emit(
&mut *streams.out,
streams.fmt,
verb,
DogActionRow::new(name, source, status),
streams.style,
))
}
pub(crate) fn fail_config(streams: &mut Streams<'_>, err: &ShepTomlError) -> ExitCode {
let code = match err {
ShepTomlError::Io { .. } => ExitCode::Failure,
ShepTomlError::Parse { .. }
| ShepTomlError::WrongShape { .. }
| ShepTomlError::NoDogEntry { .. }
| ShepTomlError::BadGrants { .. } => ExitCode::InvalidConfig,
};
streams.fail(code, &err.to_string())
}
pub(crate) fn boot_first_in_config(
path: &Path,
name: &str,
first: bool,
) -> Result<(), ShepTomlError> {
ShepToml::try_edit(path, |cfg| cfg.set_boot_first_dog(name, first))
}
fn dog_source(cfg: &ShepToml, name: &str, grants: Option<Grants>) -> DogSource {
adopted_source(cfg, name, grants.clone()).unwrap_or(DogSource::BuiltIn { grants })
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct DogEntry {
pub source: DogSource,
pub env: BTreeMap<String, EnvValue>,
}
fn dog_entry(cfg: &ShepToml, name: &str, grants: Option<Grants>) -> DogEntry {
DogEntry {
source: dog_source(cfg, name, grants),
env: cfg
.adopted_dog_env(name)
.into_iter()
.map(|(key, value)| (key, value.into()))
.collect(),
}
}
fn adopted_source(cfg: &ShepToml, name: &str, grants: Option<Grants>) -> Option<DogSource> {
cfg.adopted_dog_path(name).map(|path| DogSource::Adopted {
grants,
path: path.display().to_string(),
channel: cfg.asked_for_channel(name),
})
}
pub(crate) async fn connect_or_absent(
paths: &ShepPaths,
streams: &mut Streams<'_>,
) -> Result<Option<Client>, ExitCode> {
match Client::connect(&paths.socket).await {
Ok(client) => {
refuse_version_skew(streams, &client, VersionGuard::Enforce)?;
Ok(Some(client))
}
Err(ConnectError::Connect { .. }) => Ok(None),
Err(err) => {
let code = ExitCode::from(&err);
Err(streams.fail(
code,
&format!(
"{err}; run `shep {}`",
crate::version_guard::VERSION_SKEW_REMEDY
),
))
}
}
}