wasmer_cli/commands/app/
list.rs

1//! List Edge apps.
2
3use std::pin::Pin;
4
5use futures::{Stream, StreamExt};
6use wasmer_backend_api::types::{DeployApp, DeployAppsSortBy};
7
8use crate::{commands::AsyncCliCommand, config::WasmerEnv, opts::ListFormatOpts};
9
10/// List apps belonging to a namespace
11#[derive(clap::Parser, Debug)]
12pub struct CmdAppList {
13    #[clap(flatten)]
14    fmt: ListFormatOpts,
15
16    #[clap(flatten)]
17    env: WasmerEnv,
18
19    /// Get apps in a specific namespace.
20    ///
21    /// Will fetch the apps owned by the current user otherwise.
22    #[clap(short = 'n', long)]
23    namespace: Option<String>,
24
25    /// Get all apps that are accessible by the current user, including apps
26    /// directly owned by the user and apps in namespaces the user can access.
27    #[clap(short = 'a', long)]
28    all: bool,
29
30    /// Maximum number of apps to display
31    #[clap(long, default_value = "1000")]
32    max: usize,
33
34    /// Asks whether to display the next page or not
35    #[clap(long, default_value = "false")]
36    paging_mode: bool,
37
38    /// Sort order for apps.
39    ///
40    /// Use: --newest, --oldest or --last-updated
41    #[clap(long, default_value = "last-updated")]
42    sort: AppSort,
43}
44
45#[derive(clap::ValueEnum, Clone, Copy, Debug)]
46pub enum AppSort {
47    Newest,
48    Oldest,
49    LastUpdated,
50}
51
52#[async_trait::async_trait]
53impl AsyncCliCommand for CmdAppList {
54    type Output = ();
55
56    async fn run_async(self) -> Result<(), anyhow::Error> {
57        let client = self.env.client()?;
58
59        let sort = match self.sort {
60            AppSort::Newest => DeployAppsSortBy::Newest,
61            AppSort::Oldest => DeployAppsSortBy::Oldest,
62            AppSort::LastUpdated => DeployAppsSortBy::MostActive,
63        };
64
65        let apps_stream: Pin<
66            Box<dyn Stream<Item = Result<Vec<DeployApp>, anyhow::Error>> + Send + Sync>,
67        > = if let Some(ns) = self.namespace.clone() {
68            Box::pin(wasmer_backend_api::query::namespace_apps(&client, ns, sort).await)
69        } else if self.all {
70            Box::pin(wasmer_backend_api::query::user_accessible_apps(&client, sort).await?)
71        } else {
72            Box::pin(wasmer_backend_api::query::user_apps(&client, sort).await)
73        };
74
75        let mut apps_stream = std::pin::pin!(apps_stream);
76
77        let mut rem = self.max;
78
79        let mut display_apps = vec![];
80
81        'list: while let Some(apps) = apps_stream.next().await {
82            let mut apps = apps?;
83
84            let limit = std::cmp::min(apps.len(), rem);
85
86            if limit == 0 {
87                break;
88            }
89
90            rem -= limit;
91
92            if self.paging_mode {
93                println!("{}", self.fmt.format.render(&apps));
94
95                loop {
96                    println!("next page? [y, n]");
97
98                    let mut rsp = String::new();
99                    std::io::stdin().read_line(&mut rsp)?;
100
101                    if rsp.trim() == "y" {
102                        continue 'list;
103                    }
104                    if rsp.trim() == "n" {
105                        break 'list;
106                    }
107
108                    println!("uknown response: {rsp}");
109                }
110            }
111
112            display_apps.extend(apps.drain(..limit));
113        }
114
115        if !display_apps.is_empty() {
116            println!("{}", self.fmt.format.render(&display_apps));
117        }
118
119        Ok(())
120    }
121}