use std::io::IsTerminal;
use crate::catalog::artifacts;
use crate::catalog::capabilities;
use crate::catalog::tool_catalog;
use crate::catalog::tool_settings;
use crate::catalog::tool_usage;
use crate::catalog::wally_packages;
use crate::catalog_view;
use anyhow::Result;
pub fn run(key: Option<&str>) -> Result<()> {
match key {
Some(key) => show_one(key),
None if std::io::stdin().is_terminal() && std::io::stdout().is_terminal() => {
super::catalog_browser::run()
}
None => list_all(),
}
}
fn show_one(key: &str) -> Result<()> {
if let Some(detail) = catalog_view::lookup(key) {
println!("{}", detail.plain_text());
} else {
println!("No catalog entry named `{key}`. Run `rproj info` with no argument to browse.");
}
Ok(())
}
fn list_all() -> Result<()> {
println!("WALLY PACKAGES");
for category in wally_packages::Category::ALL {
println!(" {}", category.label());
for pkg in wally_packages::in_category(category) {
println!(" {:<18} {:<18} {}", pkg.key, pkg.author(), pkg.version());
}
}
println!("\nTOOLS");
for &family in tool_catalog::FAMILY_ORDER {
let mut entries = tool_catalog::in_family(family).peekable();
if entries.peek().is_none() {
continue;
}
println!(" {family}");
for tool in entries {
println!(
" {:<18} {:<32} ({})",
tool.key,
tool.kind.provider(),
tool.kind.label()
);
}
}
println!("\nCAPABILITIES (what `rproj new` asks about)");
for capability in capabilities::CAPABILITIES {
println!(
" {:<12} {:<18} off by default",
capability.key,
capability.default_implementation().display,
);
}
println!("\nGENERATED FILES (rproj info <key> for why a project gets one)");
for artifact in artifacts::ARTIFACTS {
println!(
" {:<24} {}",
artifact.key,
catalog_view::cause_line(artifact)
);
}
println!("\nCONFIGURABLE (rproj configure <key>)");
for tool in tool_settings::CONFIGURABLE_TOOLS {
println!(" {:<18} {}", tool.key, tool.display_name);
}
println!("\nTOPICS");
for topic in tool_usage::TOPICS {
println!(" {:<18} {}", topic.key, first_sentence(topic.what));
}
println!("\nRun `rproj info <key>` for what it does, the commands to use it, and the gotchas.");
Ok(())
}
fn first_sentence(text: &str) -> &str {
catalog_view::first_sentence(text)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flat_listing_and_details_share_artifact_causes() {
let artifact = artifacts::find("selene.toml").unwrap();
let detail = catalog_view::lookup("selene.toml").unwrap();
assert!(detail.body.contains(&catalog_view::cause_line(artifact)));
}
#[test]
fn catalog_section_labels_are_unique() {
let sections = catalog_view::sections();
let mut labels: Vec<&str> = sections.iter().map(|section| section.label()).collect();
let before = labels.len();
labels.sort_unstable();
labels.dedup();
assert_eq!(before, labels.len(), "duplicate section label");
}
}