wasmer_cli/commands/namespace/
create.rs

1use std::io::IsTerminal as _;
2
3use crate::{commands::AsyncCliCommand, config::WasmerEnv, opts::ItemFormatOpts};
4use dialoguer::theme::ColorfulTheme;
5
6/// Create a new namespace.
7#[derive(clap::Parser, Debug)]
8pub struct CmdNamespaceCreate {
9    #[clap(flatten)]
10    fmt: ItemFormatOpts,
11
12    #[clap(flatten)]
13    env: WasmerEnv,
14
15    /// Do not prompt for user input.
16    #[clap(long, default_value_t = !std::io::stdin().is_terminal())]
17    non_interactive: bool,
18
19    /// Display name of the namespace.
20    #[clap(long)]
21    display_name: Option<String>,
22
23    /// Description of the namespace.
24    #[clap(long)]
25    description: Option<String>,
26
27    /// Name of the namespace.
28    name: Option<String>,
29}
30
31impl CmdNamespaceCreate {
32    fn can_prompt(&self) -> bool {
33        !self.non_interactive && std::io::stdin().is_terminal()
34    }
35
36    fn normalize_optional_value(value: Option<&str>) -> Option<String> {
37        value.and_then(|value| {
38            let value = value.trim();
39            (!value.is_empty()).then(|| value.to_string())
40        })
41    }
42
43    fn prompt_optional_value(
44        &self,
45        message: &str,
46        default: Option<&str>,
47    ) -> anyhow::Result<Option<String>> {
48        let theme = ColorfulTheme::default();
49        let value = dialoguer::Input::<String>::with_theme(&theme)
50            .with_prompt(message)
51            .with_initial_text(default.unwrap_or_default())
52            .interact_text()?;
53
54        Ok(Self::normalize_optional_value(Some(&value)))
55    }
56
57    fn get_name(&self) -> anyhow::Result<String> {
58        if let Some(name) = Self::normalize_optional_value(self.name.as_deref()) {
59            return Ok(name);
60        }
61
62        if !self.can_prompt() {
63            anyhow::bail!("No namespace name given. Provide one as a positional argument.")
64        }
65
66        crate::utils::prompts::prompt_for_ident("Enter the namespace name", None)
67    }
68
69    fn get_display_name(&self) -> anyhow::Result<Option<String>> {
70        if self.display_name.is_some() || !self.can_prompt() {
71            return Ok(Self::normalize_optional_value(self.display_name.as_deref()));
72        }
73
74        self.prompt_optional_value("Enter the namespace display name", None)
75    }
76
77    fn get_description(&self) -> anyhow::Result<Option<String>> {
78        if self.description.is_some() || !self.can_prompt() {
79            return Ok(Self::normalize_optional_value(self.description.as_deref()));
80        }
81
82        self.prompt_optional_value("Enter the namespace description", None)
83    }
84}
85
86#[async_trait::async_trait]
87impl AsyncCliCommand for CmdNamespaceCreate {
88    type Output = ();
89
90    async fn run_async(self) -> Result<(), anyhow::Error> {
91        let client = self.env.client()?;
92
93        let vars = wasmer_backend_api::types::CreateNamespaceVars {
94            input: wasmer_backend_api::types::CreateNamespaceInput {
95                name: self.get_name()?,
96                display_name: self.get_display_name()?,
97                description: self.get_description()?,
98                avatar: None,
99                client_mutation_id: None,
100            },
101        };
102        let namespace = wasmer_backend_api::query::create_namespace(&client, vars).await?;
103
104        println!("{}", self.fmt.get().render(&namespace));
105
106        Ok(())
107    }
108}