1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! List Edge apps.

use std::pin::Pin;

use futures::{Stream, StreamExt};
use wasmer_backend_api::types::{DeployApp, DeployAppsSortBy};

use crate::{commands::AsyncCliCommand, config::WasmerEnv, opts::ListFormatOpts};

/// List apps belonging to a namespace
#[derive(clap::Parser, Debug)]
pub struct CmdAppList {
    #[clap(flatten)]
    fmt: ListFormatOpts,

    #[clap(flatten)]
    env: WasmerEnv,

    /// Get apps in a specific namespace.
    ///
    /// Will fetch the apps owned by the current user otherwise.
    #[clap(short = 'n', long)]
    namespace: Option<String>,

    /// Get all apps that are accessible by the current user, including apps
    /// directly owned by the user and apps in namespaces the user can access.
    #[clap(short = 'a', long)]
    all: bool,

    /// Maximum number of apps to display
    #[clap(long, default_value = "1000")]
    max: usize,

    /// Asks whether to display the next page or not
    #[clap(long, default_value = "false")]
    paging_mode: bool,

    /// Sort order for apps.
    ///
    /// Use: --newest, --oldest or --last-updated
    #[clap(long, default_value = "last-updated")]
    sort: AppSort,
}

#[derive(clap::ValueEnum, Clone, Copy, Debug)]
pub enum AppSort {
    Newest,
    Oldest,
    LastUpdated,
}

#[async_trait::async_trait]
impl AsyncCliCommand for CmdAppList {
    type Output = ();

    async fn run_async(self) -> Result<(), anyhow::Error> {
        let client = self.env.client()?;

        let sort = match self.sort {
            AppSort::Newest => DeployAppsSortBy::Newest,
            AppSort::Oldest => DeployAppsSortBy::Oldest,
            AppSort::LastUpdated => DeployAppsSortBy::MostActive,
        };

        let apps_stream: Pin<
            Box<dyn Stream<Item = Result<Vec<DeployApp>, anyhow::Error>> + Send + Sync>,
        > = if let Some(ns) = self.namespace.clone() {
            Box::pin(wasmer_backend_api::query::namespace_apps(&client, ns, sort).await)
        } else if self.all {
            Box::pin(wasmer_backend_api::query::user_accessible_apps(&client, sort).await?)
        } else {
            Box::pin(wasmer_backend_api::query::user_apps(&client, sort).await)
        };

        let mut apps_stream = std::pin::pin!(apps_stream);

        let mut rem = self.max;

        let mut display_apps = vec![];

        'list: while let Some(apps) = apps_stream.next().await {
            let mut apps = apps?;

            let limit = std::cmp::min(apps.len(), rem);

            if limit == 0 {
                break;
            }

            rem -= limit;

            if self.paging_mode {
                println!("{}", self.fmt.format.render(&apps));

                loop {
                    println!("next page? [y, n]");

                    let mut rsp = String::new();
                    std::io::stdin().read_line(&mut rsp)?;

                    if rsp.trim() == "y" {
                        continue 'list;
                    }
                    if rsp.trim() == "n" {
                        break 'list;
                    }

                    println!("uknown response: {rsp}");
                }
            }

            display_apps.extend(apps.drain(..limit));
        }

        if !display_apps.is_empty() {
            println!("{}", self.fmt.format.render(&display_apps));
        }

        Ok(())
    }
}