- CLI Rust (am) : list/install/uninstall/update/start/stop/restart/run/status/search/info/config/doctor/completion/export/import/self-update + shell interactif (Tab, historique) - 9 methodes d'installation (npm, pip, uv, cargo, go, bun, curl, binary, git) - catalogue de 72 agents, dependances, processus, securite (dry-run, checksums) - sondes de version paralleles + cache (am list ~25 ms a chaud) - installateurs one-liner : install.sh, install.ps1
63 lines
1.8 KiB
Rust
63 lines
1.8 KiB
Rust
//! search: keyword search over the catalog.
|
|
|
|
use super::*;
|
|
use crate::output::{print_json, Table};
|
|
use serde::Serialize;
|
|
|
|
#[derive(Serialize)]
|
|
struct SearchRow {
|
|
name: String,
|
|
display_name: String,
|
|
category: Option<String>,
|
|
installable: bool,
|
|
description: Option<String>,
|
|
website: Option<String>,
|
|
tags: Vec<String>,
|
|
}
|
|
|
|
pub fn run(app: &App, keyword: &str, category: Option<&str>) -> Result<i32> {
|
|
let hits = app.catalog.search(keyword, category);
|
|
if app.json() {
|
|
let rows: Vec<SearchRow> = hits
|
|
.iter()
|
|
.map(|a| SearchRow {
|
|
name: a.name.clone(),
|
|
display_name: a.title().to_string(),
|
|
category: a.category.clone(),
|
|
installable: a.installable,
|
|
description: a.description.clone(),
|
|
website: a.website.clone(),
|
|
tags: a.tags.clone(),
|
|
})
|
|
.collect();
|
|
print_json(&rows);
|
|
return Ok(0);
|
|
}
|
|
let mut table = Table::new(vec!["NAME", "CATEGORY", "INSTALLABLE", "DESCRIPTION"]);
|
|
for a in hits {
|
|
let desc = truncate(a.description.as_deref().unwrap_or(""), 56);
|
|
table.row(vec![
|
|
a.title().to_string(),
|
|
a.category.clone().unwrap_or_else(|| "—".to_string()),
|
|
if a.installable { "yes" } else { "no" }.to_string(),
|
|
desc,
|
|
]);
|
|
}
|
|
print!("{}", table.render());
|
|
if app.catalog.search(keyword, category).is_empty() {
|
|
app.log
|
|
.info(&format!("no agent matches '{keyword}' (categories: {})", app.catalog.categories().join(", ")));
|
|
}
|
|
Ok(0)
|
|
}
|
|
|
|
pub fn truncate(s: &str, max: usize) -> String {
|
|
if s.chars().count() <= max {
|
|
s.to_string()
|
|
} else {
|
|
let mut cut: String = s.chars().take(max - 1).collect();
|
|
cut.push('…');
|
|
cut
|
|
}
|
|
}
|