mod action;
mod detect;
mod error;
mod install;
#[cfg(test)]
mod tests;
use std::path::Path;
use indexmap::IndexMap;
use smol_str::SmolStr;
use super::pkg_config_files::PkgConfigFiles;
pub(crate) use action::PkgShellConfig;
pub use action::{ActionKind, ShellInitAction};
pub use detect::DetectStrategy;
pub use error::Error;
#[allow(unused_imports)]
pub use install::BrewHint;
pub use install::InstallHint;
#[derive(serde::Deserialize, schemars::JsonSchema, Debug, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub(crate) enum Shell {
Bash,
Zsh,
}
#[derive(serde::Deserialize, schemars::JsonSchema, Debug, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Os {
Linux,
Macos,
}
impl Os {
pub fn current() -> Result<Self, Error> {
match std::env::consts::OS {
"linux" => Ok(Self::Linux),
"macos" => Ok(Self::Macos),
unk => Err(Error::UnknownOs(unk)),
}
}
}
#[derive(serde::Deserialize, schemars::JsonSchema, Debug, Clone, PartialEq, Default)]
#[serde(rename_all = "snake_case")]
pub(crate) enum PkgEnable {
#[default]
On,
Detect,
Disabled,
}
#[derive(serde::Deserialize, schemars::JsonSchema, Debug, Clone, PartialEq, Default)]
pub struct PkgConfig {
#[serde(default)]
pub(super) enable: PkgEnable,
#[serde(default)]
pub(super) detect: Option<DetectStrategy>,
#[serde(default)]
pub(super) inputs: IndexMap<SmolStr, SmolStr>,
#[serde(default)]
pub(super) supported_os: Vec<Os>,
#[serde(default)]
pub(super) supported_shells: Vec<Shell>,
#[serde(default)]
pub name: Option<SmolStr>,
#[serde(default)]
pub description: Option<String>,
pub install_hint: InstallHint,
#[serde(default)]
pub(crate) shell: PkgShellConfig,
#[serde(default)]
pub(super) configs: Vec<PkgConfigFiles>,
}
impl PkgConfig {
pub fn is_installed(
&self,
home: &Path,
system_inputs: &IndexMap<SmolStr, SmolStr>,
) -> Result<bool, Error> {
if !self.supports_current_os()? {
return Ok(false);
}
match self.enable {
PkgEnable::On | PkgEnable::Detect => {
let Some(detect) = self.detect.as_ref() else {
return Ok(true);
};
let lookup = [&self.inputs, system_inputs];
detect.check(home, &lookup)
}
PkgEnable::Disabled => Ok(false),
}
}
pub fn enable_on_but_detect_missing(
&self,
home: &Path,
system_inputs: &IndexMap<SmolStr, SmolStr>,
) -> Result<bool, Error> {
if !matches!(self.enable, PkgEnable::On) {
return Ok(false);
}
if !self.supports_current_os()? {
return Ok(false);
}
let Some(detect) = self.detect.as_ref() else {
return Ok(false);
};
let lookup = [&self.inputs, system_inputs];
detect.check(home, &lookup).map(|r| !r)
}
pub fn enable_on_and_detect_matches(
&self,
home: &Path,
system_inputs: &IndexMap<SmolStr, SmolStr>,
) -> Result<bool, Error> {
if !matches!(self.enable, PkgEnable::On) {
return Ok(false);
}
if !self.supports_current_os()? {
return Ok(false);
}
let Some(detect) = self.detect.as_ref() else {
return Ok(false);
};
let lookup = [&self.inputs, system_inputs];
detect.check(home, &lookup)
}
pub fn is_disabled(&self) -> bool {
matches!(self.enable, PkgEnable::Disabled)
}
pub fn matched_detect(
&self,
home: &Path,
system_inputs: &IndexMap<SmolStr, SmolStr>,
) -> Result<Option<&DetectStrategy>, Error> {
if !self.supports_current_os()? {
return Ok(None);
}
match self.enable {
PkgEnable::On | PkgEnable::Detect => {
if let Some(detect) = self.detect.as_ref() {
let lookup = [&self.inputs, system_inputs];
detect.check(home, &lookup).map(|r| r.then_some(detect))
} else {
Ok(None)
}
}
PkgEnable::Disabled => Ok(None),
}
}
pub(crate) fn supports_current_os(&self) -> Result<bool, Error> {
Ok(self.supported_os.is_empty() || self.supported_os.contains(&Os::current()?))
}
pub(crate) fn supports_shell(&self, shell: Option<Shell>) -> bool {
self.supported_shells.is_empty()
|| shell.is_some_and(|s| self.supported_shells.contains(&s))
}
pub(crate) fn inputs(&self) -> &IndexMap<SmolStr, SmolStr> {
&self.inputs
}
pub(super) fn configs(&self) -> &[PkgConfigFiles] {
&self.configs
}
}