cargo/ops/registry/
search.rs
1use std::cmp;
6
7use anyhow::Context as _;
8use url::Url;
9
10use crate::util::style;
11use crate::util::style::LITERAL;
12use crate::util::truncate_with_ellipsis;
13use crate::CargoResult;
14use crate::GlobalContext;
15
16use super::RegistryOrIndex;
17
18pub fn search(
19 query: &str,
20 gctx: &GlobalContext,
21 reg_or_index: Option<RegistryOrIndex>,
22 limit: u32,
23) -> CargoResult<()> {
24 let source_ids = super::get_source_id(gctx, reg_or_index.as_ref())?;
25 let (mut registry, _) =
26 super::registry(gctx, &source_ids, None, reg_or_index.as_ref(), false, None)?;
27 let (crates, total_crates) = registry.search(query, limit).with_context(|| {
28 format!(
29 "failed to retrieve search results from the registry at {}",
30 registry.host()
31 )
32 })?;
33
34 let names = crates
35 .iter()
36 .map(|krate| format!("{} = \"{}\"", krate.name, krate.max_version))
37 .collect::<Vec<String>>();
38
39 let description_margin = names.iter().map(|s| s.len()).max().unwrap_or_default() + 4;
40
41 let description_length = cmp::max(80, 128 - description_margin);
42
43 let descriptions = crates.iter().map(|krate| {
44 krate
45 .description
46 .as_ref()
47 .map(|desc| truncate_with_ellipsis(&desc.replace("\n", " "), description_length))
48 });
49
50 let mut shell = gctx.shell();
51 let stdout = shell.out();
52 let good = style::GOOD;
53
54 for (name, description) in names.into_iter().zip(descriptions) {
55 let line = match description {
56 Some(desc) => format!("{name: <description_margin$}# {desc}"),
57 None => name,
58 };
59 let mut fragments = line.split(query).peekable();
60 while let Some(fragment) = fragments.next() {
61 let _ = write!(stdout, "{fragment}");
62 if fragments.peek().is_some() {
63 let _ = write!(stdout, "{good}{query}{good:#}");
64 }
65 }
66 let _ = writeln!(stdout);
67 }
68
69 let search_max_limit = 100;
70 if total_crates > limit && limit < search_max_limit {
71 let _ = writeln!(
72 stdout,
73 "... and {} crates more (use --limit N to see more)",
74 total_crates - limit
75 );
76 } else if total_crates > limit && limit >= search_max_limit {
77 let extra = if source_ids.original.is_crates_io() {
78 let url = Url::parse_with_params("https://crates.io/search", &[("q", query)])?;
79 format!(" (go to {url} to see more)")
80 } else {
81 String::new()
82 };
83 let _ = writeln!(
84 stdout,
85 "... and {} crates more{}",
86 total_crates - limit,
87 extra
88 );
89 }
90
91 if total_crates > 0 {
92 let literal = LITERAL;
93 shell.note(format_args!(
94 "to learn more about a package, run `{literal}cargo info <name>{literal:#}`",
95 ))?;
96 }
97
98 Ok(())
99}