wasmer_cli/commands/
config.rs

1use crate::{
2    VERSION,
3    config::{UpdateRegistry, WasmerConfig, WasmerEnv},
4};
5use anyhow::{Context, Result};
6use clap::Parser;
7use std::str::ParseBoolError;
8
9use super::AsyncCliCommand;
10
11#[derive(Debug, Parser)]
12/// The options for the `wasmer config` subcommand: `wasmer config get --OPTION` or `wasmer config set [FLAG]`
13pub struct Config {
14    #[clap(flatten)]
15    env: WasmerEnv,
16
17    #[clap(flatten)]
18    flags: Flags,
19    /// Subcommand for `wasmer config get | set`
20    #[clap(subcommand)]
21    set: Option<GetOrSet>,
22}
23
24/// Normal configuration
25#[derive(Debug, Parser)]
26pub struct Flags {
27    /// Print the installation prefix.
28    #[clap(long, conflicts_with = "pkg_config")]
29    prefix: bool,
30
31    /// Directory containing Wasmer executables.
32    #[clap(long, conflicts_with = "pkg_config")]
33    bindir: bool,
34
35    /// Directory containing Wasmer headers.
36    #[clap(long, conflicts_with = "pkg_config")]
37    includedir: bool,
38
39    /// Directory containing Wasmer libraries.
40    #[clap(long, conflicts_with = "pkg_config")]
41    libdir: bool,
42
43    /// Libraries needed to link against Wasmer components.
44    #[clap(long, conflicts_with = "pkg_config")]
45    libs: bool,
46
47    /// C compiler flags for files that include Wasmer headers.
48    #[clap(long, conflicts_with = "pkg_config")]
49    cflags: bool,
50
51    /// Print the path to the wasmer configuration file where all settings are stored
52    #[clap(long, conflicts_with = "pkg_config")]
53    config_path: bool,
54
55    /// Outputs the necessary details for compiling
56    /// and linking a program to Wasmer, using the `pkg-config` format.
57    #[clap(long)]
58    pkg_config: bool,
59}
60
61/// Subcommand for `wasmer config set`
62#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
63pub enum GetOrSet {
64    /// `wasmer config get $KEY`
65    #[clap(subcommand)]
66    Get(RetrievableConfigField),
67    /// `wasmer config set $KEY $VALUE`
68    #[clap(subcommand)]
69    Set(StorableConfigField),
70}
71
72/// Subcommand for `wasmer config get`
73#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
74pub enum RetrievableConfigField {
75    /// Print the registry URL of the currently active registry
76    #[clap(name = "registry.url")]
77    RegistryUrl,
78    /// Print the token for the currently active registry or nothing if not logged in
79    #[clap(name = "registry.token")]
80    RegistryToken,
81    /// Print whether telemetry is currently enabled
82    #[clap(name = "telemetry.enabled")]
83    TelemetryEnabled,
84    /// Print whether update notifications are enabled
85    #[clap(name = "update-notifications.enabled")]
86    UpdateNotificationsEnabled,
87    /// Print the proxy URL
88    #[clap(name = "proxy.url")]
89    ProxyUrl,
90}
91
92/// Setting that can be stored in the wasmer config
93#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
94pub enum StorableConfigField {
95    /// Set the registry URL of the currently active registry
96    #[clap(name = "registry.url")]
97    RegistryUrl(SetRegistryUrl),
98    /// Set the token for the currently active registry or nothing if not logged in
99    #[clap(name = "registry.token")]
100    RegistryToken(SetRegistryToken),
101    /// Set whether telemetry is currently enabled
102    #[clap(name = "telemetry.enabled")]
103    TelemetryEnabled(SetTelemetryEnabled),
104    /// Set whether update notifications are enabled
105    #[clap(name = "update-notifications.enabled")]
106    UpdateNotificationsEnabled(SetUpdateNotificationsEnabled),
107    /// Set the active proxy URL
108    #[clap(name = "proxy.url")]
109    ProxyUrl(SetProxyUrl),
110}
111
112/// Set the current active registry URL
113#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
114pub struct SetRegistryUrl {
115    /// Url of the registry
116    #[clap(name = "URL")]
117    pub url: String,
118}
119
120/// Set or change the token for the current active registry
121#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
122pub struct SetRegistryToken {
123    /// Token to set
124    #[clap(name = "TOKEN")]
125    pub token: String,
126}
127
128/// Set if update notifications are enabled
129#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
130pub struct SetUpdateNotificationsEnabled {
131    /// Whether to enable update notifications
132    ///
133    /// ("true" | "false")
134    #[clap(name = "ENABLED")]
135    pub enabled: BoolString,
136}
137
138/// "true" or "false" for handling input in the CLI
139#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
140pub struct BoolString(pub bool);
141
142impl std::str::FromStr for BoolString {
143    type Err = ParseBoolError;
144    fn from_str(s: &str) -> Result<Self, Self::Err> {
145        Ok(Self(bool::from_str(s)?))
146    }
147}
148
149/// Set if telemetry is enabled
150#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
151pub struct SetTelemetryEnabled {
152    /// Whether to enable telemetry
153    ///
154    /// ("true" | "false")
155    #[clap(name = "ENABLED")]
156    pub enabled: BoolString,
157}
158
159/// Set if a proxy URL should be used
160#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
161pub struct SetProxyUrl {
162    /// Set if a proxy URL should be used (empty = unset proxy)
163    #[clap(name = "URL")]
164    pub url: String,
165}
166
167#[async_trait::async_trait]
168impl AsyncCliCommand for Config {
169    type Output = ();
170
171    /// Runs logic for the `config` subcommand
172    async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
173        self.inner_execute()
174            .await
175            .context("failed to retrieve the wasmer config".to_string())
176    }
177}
178
179impl Config {
180    async fn inner_execute(&self) -> Result<()> {
181        if let Some(s) = self.set.as_ref() {
182            return s.execute(&self.env).await;
183        }
184
185        let flags = &self.flags;
186
187        let prefix = self.env.dir();
188
189        let prefixdir = prefix.display().to_string();
190        let bindir = prefix.join("bin").display().to_string();
191        let includedir = prefix.join("include").display().to_string();
192        let libdir = prefix.join("lib").display().to_string();
193        let cflags = format!("-I{includedir}");
194        let libs = format!("-L{libdir} -lwasmer");
195
196        if flags.pkg_config {
197            println!("prefix={prefixdir}");
198            println!("exec_prefix={bindir}");
199            println!("includedir={includedir}");
200            println!("libdir={libdir}");
201            println!();
202            println!("Name: wasmer");
203            println!("Description: The Wasmer library for running WebAssembly");
204            println!("Version: {VERSION}");
205            println!("Cflags: {cflags}");
206            println!("Libs: {libs}");
207            return Ok(());
208        }
209
210        if flags.prefix {
211            println!("{prefixdir}");
212        }
213        if flags.bindir {
214            println!("{bindir}");
215        }
216        if flags.includedir {
217            println!("{includedir}");
218        }
219        if flags.libdir {
220            println!("{libdir}");
221        }
222        if flags.libs {
223            println!("{libs}");
224        }
225        if flags.cflags {
226            println!("{cflags}");
227        }
228
229        if flags.config_path {
230            let path = WasmerConfig::get_file_location(self.env.dir());
231            println!("{}", path.display());
232        }
233
234        Ok(())
235    }
236}
237
238impl GetOrSet {
239    async fn execute(&self, env: &WasmerEnv) -> Result<()> {
240        let config_file = WasmerConfig::get_file_location(env.dir());
241        let mut config = env.config()?;
242
243        match self {
244            GetOrSet::Get(g) => match g {
245                RetrievableConfigField::RegistryUrl => {
246                    println!("{}", config.registry.get_current_registry());
247                }
248                RetrievableConfigField::RegistryToken => {
249                    if let Some(s) = config
250                        .registry
251                        .get_login_token_for_registry(&config.registry.get_current_registry())
252                    {
253                        println!("{s}");
254                    }
255                }
256                RetrievableConfigField::TelemetryEnabled => {
257                    println!("{:?}", config.telemetry_enabled);
258                }
259                RetrievableConfigField::UpdateNotificationsEnabled => {
260                    println!("{:?}", config.update_notifications_enabled);
261                }
262                RetrievableConfigField::ProxyUrl => {
263                    if let Some(s) = config.proxy.url.as_ref() {
264                        println!("{s}");
265                    } else {
266                        println!("none");
267                    }
268                }
269            },
270            GetOrSet::Set(s) => {
271                match s {
272                    StorableConfigField::RegistryUrl(s) => {
273                        config.registry.set_current_registry(&s.url).await;
274                        let current_registry = config.registry.get_current_registry();
275                        if let Ok(client) = env.client() {
276                            if let Some(u) =
277                                wasmer_backend_api::query::current_user(&client).await?
278                            {
279                                println!(
280                                    "Successfully logged into registry {current_registry:?} as user {u:?}"
281                                );
282                            }
283                        }
284                    }
285                    StorableConfigField::RegistryToken(t) => {
286                        config.registry.set_login_token_for_registry(
287                            &config.registry.get_current_registry(),
288                            &t.token,
289                            UpdateRegistry::LeaveAsIs,
290                        );
291                    }
292                    StorableConfigField::TelemetryEnabled(t) => {
293                        config.telemetry_enabled = t.enabled.0;
294                    }
295                    StorableConfigField::ProxyUrl(p) => {
296                        if p.url == "none" || p.url.is_empty() {
297                            config.proxy.url = None;
298                        } else {
299                            config.proxy.url = Some(p.url.clone());
300                        }
301                    }
302                    StorableConfigField::UpdateNotificationsEnabled(u) => {
303                        config.update_notifications_enabled = u.enabled.0;
304                    }
305                }
306                config
307                    .save(config_file)
308                    .with_context(|| anyhow::anyhow!("could not save config file"))?;
309            }
310        }
311        Ok(())
312    }
313}