soroban_cli/commands/plugin/
search.rs

1use serde::{Deserialize, Serialize};
2use std::io::Write;
3use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
4
5use crate::{commands::global, print::Print, utils::http};
6
7use super::super::config::locator;
8
9#[derive(thiserror::Error, Debug)]
10pub enum Error {
11    #[error(transparent)]
12    Config(#[from] locator::Error),
13
14    #[error("unable to retrieve the list of plugins from GitHub")]
15    Http(#[from] reqwest::Error),
16
17    #[error(transparent)]
18    Io(#[from] std::io::Error),
19}
20
21#[derive(Debug, clap::Parser, Clone)]
22#[group(skip)]
23pub struct Cmd;
24
25impl Cmd {
26    pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
27        let print = Print::new(global_args.quiet);
28        let url =
29            "https://api.github.com/search/repositories?q=topic%3Astellar-cli-plugin+fork%3Afalse+archived%3Afalse&per_page=100&sort=stars&order=desc";
30
31        let resp = http::client().get(url).send().await?;
32        let search: SearchResponse = resp.json().await?;
33
34        if search.total_count == 0 {
35            print.searchln("No plugins found.".to_string());
36            return Ok(());
37        }
38
39        let wording = if search.total_count == 1 {
40            "plugin"
41        } else {
42            "plugins"
43        };
44
45        print.searchln(format!(
46            "Found {total} {wording}:",
47            total = search.total_count
48        ));
49
50        let mut stdout = StandardStream::stdout(ColorChoice::Auto);
51
52        for item in search.items {
53            println!();
54            stdout.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?;
55            writeln!(&mut stdout, "  {}", item.full_name)?;
56            stdout.reset()?;
57
58            if let Some(description) = item.description {
59                writeln!(&mut stdout, "  {description}")?;
60            }
61
62            print.blankln(item.html_url.clone());
63        }
64        Ok(())
65    }
66}
67
68#[derive(Debug, Serialize, Deserialize)]
69struct SearchResponse {
70    total_count: u32,
71    incomplete_results: bool,
72    items: Vec<Repository>,
73}
74
75#[derive(Debug, Serialize, Deserialize)]
76struct Repository {
77    id: u64,
78    name: String,
79    full_name: String,
80    html_url: String,
81    description: Option<String>,
82}