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)]
12pub struct Config {
14 #[clap(flatten)]
15 env: WasmerEnv,
16
17 #[clap(flatten)]
18 flags: Flags,
19 #[clap(subcommand)]
21 set: Option<GetOrSet>,
22}
23
24#[derive(Debug, Parser)]
26pub struct Flags {
27 #[clap(long, conflicts_with = "pkg_config")]
29 prefix: bool,
30
31 #[clap(long, conflicts_with = "pkg_config")]
33 bindir: bool,
34
35 #[clap(long, conflicts_with = "pkg_config")]
37 includedir: bool,
38
39 #[clap(long, conflicts_with = "pkg_config")]
41 libdir: bool,
42
43 #[clap(long, conflicts_with = "pkg_config")]
45 libs: bool,
46
47 #[clap(long, conflicts_with = "pkg_config")]
49 cflags: bool,
50
51 #[clap(long, conflicts_with = "pkg_config")]
53 config_path: bool,
54
55 #[clap(long)]
58 pkg_config: bool,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
63pub enum GetOrSet {
64 #[clap(subcommand)]
66 Get(RetrievableConfigField),
67 #[clap(subcommand)]
69 Set(StorableConfigField),
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
74pub enum RetrievableConfigField {
75 #[clap(name = "registry.url")]
77 RegistryUrl,
78 #[clap(name = "registry.token")]
80 RegistryToken,
81 #[clap(name = "telemetry.enabled")]
83 TelemetryEnabled,
84 #[clap(name = "update-notifications.enabled")]
86 UpdateNotificationsEnabled,
87 #[clap(name = "proxy.url")]
89 ProxyUrl,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
94pub enum StorableConfigField {
95 #[clap(name = "registry.url")]
97 RegistryUrl(SetRegistryUrl),
98 #[clap(name = "registry.token")]
100 RegistryToken(SetRegistryToken),
101 #[clap(name = "telemetry.enabled")]
103 TelemetryEnabled(SetTelemetryEnabled),
104 #[clap(name = "update-notifications.enabled")]
106 UpdateNotificationsEnabled(SetUpdateNotificationsEnabled),
107 #[clap(name = "proxy.url")]
109 ProxyUrl(SetProxyUrl),
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
114pub struct SetRegistryUrl {
115 #[clap(name = "URL")]
117 pub url: String,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
122pub struct SetRegistryToken {
123 #[clap(name = "TOKEN")]
125 pub token: String,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
130pub struct SetUpdateNotificationsEnabled {
131 #[clap(name = "ENABLED")]
135 pub enabled: BoolString,
136}
137
138#[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#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
151pub struct SetTelemetryEnabled {
152 #[clap(name = "ENABLED")]
156 pub enabled: BoolString,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
161pub struct SetProxyUrl {
162 #[clap(name = "URL")]
164 pub url: String,
165}
166
167#[async_trait::async_trait]
168impl AsyncCliCommand for Config {
169 type Output = ();
170
171 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}