use crate::api::ApiClient;
use crate::cache::Cache;
use crate::cask::CaskState;
use crate::error::{Result, WaxError};
use crate::install::InstallState;
use console::style;
use std::collections::HashSet;
use tracing::instrument;
#[instrument(skip(api_client, cache))]
pub async fn info(api_client: &ApiClient, cache: &Cache, name: &str, cask: bool) -> Result<()> {
cache.ensure_fresh().await?;
if cask {
return info_cask(api_client, cache, name).await;
}
let formulae = cache.load_all_formulae().await?;
let formula_exists = formulae
.iter()
.any(|f| f.name == name || f.full_name == name);
if !formula_exists {
let casks = cache.load_casks().await?;
let cask_exists = casks
.iter()
.any(|c| c.token == name || c.full_token == name);
if cask_exists {
return info_cask(api_client, cache, name).await;
}
return Err(WaxError::FormulaNotFound(format!(
"{} (not found as formula or cask)",
name
)));
}
let formula = formulae
.iter()
.find(|f| f.name == name || f.full_name == name)
.ok_or_else(|| WaxError::FormulaNotFound(name.to_string()))?;
let installed_suffix = if let Some(installed) = &formula.installed {
if !installed.is_empty() {
let installed_versions: Vec<_> = installed.iter().map(|i| i.version.as_str()).collect();
if installed_versions.len() == 1 {
if installed_versions[0] == formula.versions.stable {
" · installed".to_string()
} else {
format!(" · installed ({})", installed_versions[0])
}
} else {
format!(" · installed ({})", installed_versions.join(", "))
}
} else {
String::new()
}
} else {
String::new()
};
println!();
println!(
"{} · {}{}",
style(&formula.name).magenta(),
style(&formula.versions.stable).dim(),
style(&installed_suffix).dim()
);
if let Some(desc) = &formula.desc {
println!("{}", desc);
}
println!();
println!("{}", &formula.homepage);
if let Some(deps) = &formula.dependencies {
if !deps.is_empty() {
println!();
println!("{}", style("dependencies:").dim());
for dep in deps {
println!(" {}", dep);
}
}
}
if let Some(build_deps) = &formula.build_dependencies {
if !build_deps.is_empty() {
println!();
println!("{}", style("build dependencies:").dim());
for dep in build_deps {
println!(" {}", dep);
}
}
}
if !formula.versions.bottle {
println!();
println!("no precompiled bottle available (will build from source)");
}
let state = InstallState::new()?;
let installed_packages = state.load().await?;
if let Some(pkg) = installed_packages.get(name) {
println!();
let installed_names: HashSet<String> = installed_packages.keys().cloned().collect();
let dependents: Vec<&str> = formulae
.iter()
.filter(|f| {
installed_names.contains(&f.name)
&& f.dependencies
.as_deref()
.unwrap_or_default()
.iter()
.any(|d| d == name)
})
.map(|f| f.name.as_str())
.collect();
if dependents.is_empty() {
println!("{} installed explicitly", style("installed:").dim());
} else {
println!("{} required by:", style("installed:").dim());
for dep in &dependents {
println!(" {} {}", style("←").dim(), style(dep).cyan());
}
}
if pkg.from_source {
println!(" built from source");
}
if pkg.pinned {
println!(" {}", style("pinned").yellow());
}
let cellar_path = pkg.install_mode.cellar_path()?;
let package_path = cellar_path.join(&pkg.name).join(&pkg.version);
println!();
println!("{} {}", style("path:").dim(), package_path.display());
}
Ok(())
}
#[instrument(skip(api_client, cache))]
async fn info_cask(api_client: &ApiClient, cache: &Cache, name: &str) -> Result<()> {
cache.ensure_fresh().await?;
let casks = cache.load_casks().await?;
let _cask_summary = casks
.iter()
.find(|c| c.token == name || c.full_token == name)
.ok_or_else(|| WaxError::CaskNotFound(name.to_string()))?;
let cask = api_client.fetch_cask_details(name).await?;
let display_name = cask.name.first().unwrap_or(&cask.token);
let state = CaskState::new()?;
let installed_casks = state.load().await?;
let installed_version = installed_casks.get(name).map(|i| &i.version);
let installed_suffix = if let Some(installed_ver) = installed_version {
if installed_ver == &cask.version {
" · installed".to_string()
} else {
format!(" · installed ({})", installed_ver)
}
} else {
String::new()
};
println!();
println!(
"{} · {} {}{}",
style(display_name).magenta(),
style(&cask.version).dim(),
style("(cask)").yellow(),
style(installed_suffix).dim()
);
if let Some(desc) = &cask.desc {
println!("{}", desc);
}
println!();
println!("{}", &cask.homepage);
println!();
println!("{}", &cask.url);
if let Some(artifacts) = &cask.artifacts {
let artifact_types: Vec<String> = artifacts
.iter()
.map(|a| match a {
crate::api::CaskArtifact::App { .. } => "app".to_string(),
crate::api::CaskArtifact::Pkg { .. } => "pkg".to_string(),
crate::api::CaskArtifact::Binary { .. } => "binary".to_string(),
crate::api::CaskArtifact::Font { .. } => "font".to_string(),
crate::api::CaskArtifact::Manpage { .. } => "manpage".to_string(),
crate::api::CaskArtifact::Dictionary { .. } => "dictionary".to_string(),
crate::api::CaskArtifact::Colorpicker { .. } => "colorpicker".to_string(),
crate::api::CaskArtifact::Prefpane { .. } => "prefpane".to_string(),
crate::api::CaskArtifact::Qlplugin { .. } => "qlplugin".to_string(),
crate::api::CaskArtifact::ScreenSaver { .. } => "screen_saver".to_string(),
crate::api::CaskArtifact::Service { .. } => "service".to_string(),
crate::api::CaskArtifact::Suite { .. } => "suite".to_string(),
crate::api::CaskArtifact::Artifact { .. } => "artifact".to_string(),
crate::api::CaskArtifact::BashCompletion { .. } => "bash_completion".to_string(),
crate::api::CaskArtifact::ZshCompletion { .. } => "zsh_completion".to_string(),
crate::api::CaskArtifact::FishCompletion { .. } => "fish_completion".to_string(),
crate::api::CaskArtifact::Uninstall { .. } => "uninstall".to_string(),
crate::api::CaskArtifact::Zap { .. } => "zap".to_string(),
crate::api::CaskArtifact::Preflight { .. } => "preflight".to_string(),
crate::api::CaskArtifact::Postflight { .. } => "postflight".to_string(),
crate::api::CaskArtifact::Other(_) => "other".to_string(),
})
.collect();
if !artifact_types.is_empty() {
println!();
println!("{}:", style("artifacts").dim());
for artifact_type in artifact_types {
println!(" {}", artifact_type);
}
}
if installed_version.is_some() {
let user_caskroom = CaskState::user_caskroom_dir()?;
let global_caskroom = CaskState::caskroom_dir();
let cask_path = if user_caskroom.join(name).exists() {
user_caskroom.join(name)
} else {
global_caskroom.join(name)
};
println!();
println!("{} {}", style("path:").dim(), cask_path.display());
}
}
Ok(())
}