warg_cli/commands/
config.rs

1use super::CommonOptions;
2use anyhow::{bail, Context, Result};
3use clap::Args;
4use std::path::PathBuf;
5use warg_client::{keyring::Keyring, Config, RegistryUrl};
6
7/// Creates a new warg configuration file.
8#[derive(Args)]
9pub struct ConfigCommand {
10    /// The common command options.
11    #[clap(flatten)]
12    pub common: CommonOptions,
13
14    /// The path to the registries directory to use.
15    #[clap(long, value_name = "REGISTRIES")]
16    pub registries_dir: Option<PathBuf>,
17
18    /// The path to the content directory to use.
19    #[clap(long, value_name = "CONTENT")]
20    pub content_dir: Option<PathBuf>,
21
22    /// Ignore federation hints.
23    #[clap(long)]
24    pub ignore_federation_hints: Option<bool>,
25
26    /// Disable auto accept federation hints.
27    #[clap(long)]
28    pub disable_auto_accept_federation_hints: Option<bool>,
29
30    /// Automatically attempt package initialize if does not exist
31    /// or ask the user to confirm first.
32    #[clap(long)]
33    pub disable_auto_package_init: Option<bool>,
34
35    /// Overwrite the existing configuration file.
36    #[clap(long)]
37    pub overwrite: bool,
38
39    /// The path to the configuration file to create.
40    ///
41    /// If not specified, the default of `$CONFIG_DIR/warg/config.json` is used.
42    #[clap(value_name = "PATH")]
43    pub path: Option<PathBuf>,
44
45    /// The path to the namespace map
46    #[clap(long, value_name = "NAMESPACE_PATH")]
47    pub namespace_path: Option<PathBuf>,
48
49    /// The backend to use for keyring access
50    #[clap(long, value_name = "KEYRING_BACKEND", value_parser = keyring_backend_parser, long_help = keyring_backend_help())]
51    pub keyring_backend: Option<String>,
52}
53
54impl ConfigCommand {
55    /// Executes the command.
56    pub async fn exec(self) -> Result<()> {
57        let path = self
58            .path
59            .map(Ok)
60            .unwrap_or_else(Config::default_config_path)?;
61        let cwd = std::env::current_dir().context("failed to determine current directory")?;
62
63        if self.overwrite && path.is_file() {
64            println!(
65                "Overwriting configuration file: `{path}`",
66                path = path.display()
67            );
68        }
69
70        let mut changing_home_registry = false;
71
72        let config = if self.overwrite {
73            let home_url = self
74                .common
75                .registry
76                .as_ref()
77                .map(RegistryUrl::new)
78                .transpose()?
79                .map(|u| u.to_string())
80                .ok_or(anyhow::anyhow!(
81                    "Please configure your home registry: warg config --registry <registry-url>"
82                ))?;
83
84            changing_home_registry = true;
85
86            Config {
87                home_url: Some(home_url),
88                registries_dir: self.registries_dir.map(|p| cwd.join(p)),
89                content_dir: self.content_dir.map(|p| cwd.join(p)),
90                namespace_map_path: self.namespace_path.map(|p| cwd.join(p)),
91                keys: self.common.read_config()?.keys,
92                keyring_auth: false,
93                ignore_federation_hints: self.ignore_federation_hints.unwrap_or_default(),
94                disable_auto_accept_federation_hints: self
95                    .disable_auto_accept_federation_hints
96                    .unwrap_or_default(),
97                disable_auto_package_init: self.disable_auto_package_init.unwrap_or_default(),
98                disable_interactive: false,
99                keyring_backend: self.keyring_backend,
100            }
101        } else {
102            let mut config = self.common.read_config()?;
103            if self.common.registry.is_some() {
104                let home_url = self
105                    .common
106                    .registry
107                    .as_ref()
108                    .map(RegistryUrl::new)
109                    .transpose()?
110                    .map(|u| u.to_string());
111                if home_url != config.home_url {
112                    changing_home_registry = true;
113                    config.home_url = home_url;
114                }
115            }
116            if config.home_url.is_none() {
117                bail!("Please configure your home registry: warg config --registry <registry-url>");
118            }
119            if self.registries_dir.is_some() {
120                config.registries_dir = self.registries_dir.map(|p| cwd.join(p));
121            }
122            if self.content_dir.is_some() {
123                config.content_dir = self.content_dir.map(|p| cwd.join(p));
124            }
125            if self.namespace_path.is_some() {
126                config.namespace_map_path = self.namespace_path.map(|p| cwd.join(p));
127            }
128            if let Some(ignore_federation_hints) = self.ignore_federation_hints {
129                config.ignore_federation_hints = ignore_federation_hints;
130            }
131            if let Some(disable_auto_accept_federation_hints) =
132                self.disable_auto_accept_federation_hints
133            {
134                config.disable_auto_accept_federation_hints = disable_auto_accept_federation_hints;
135            }
136            if let Some(disable_auto_package_init) = self.disable_auto_package_init {
137                config.disable_auto_package_init = disable_auto_package_init;
138            }
139            if self.keyring_backend.is_some() {
140                config.keyring_backend = self.keyring_backend;
141            }
142
143            config
144        };
145
146        // The paths specified on the command line are relative to the current
147        // directory.
148        //
149        // `write_to_file` will handle normalizing the paths to be relative to
150        // the configuration file's directory.
151        config.write_to_file(&path)?;
152
153        // reset when changing home registry
154        if changing_home_registry {
155            let client = self.common.create_client(&config).await?;
156            client.reset_namespaces().await?;
157            client.reset_registry().await?;
158        }
159
160        println!("Set configuration file `{path}`", path = path.display(),);
161
162        Ok(())
163    }
164}
165
166pub(crate) fn keyring_backend_parser(s: &str) -> Result<String, String> {
167    if Keyring::SUPPORTED_BACKENDS.contains(&s) {
168        Ok(s.to_string())
169    } else {
170        Err(format!("`{s}` is not a supported keyring backend."))
171    }
172}
173
174pub(crate) fn keyring_backend_help() -> clap::builder::StyledStr {
175    use std::fmt::Write as _;
176
177    let mut help = String::new();
178
179    writeln!(
180        &mut help,
181        "The backend to use for keyring access. The following options are supported:\n"
182    )
183    .unwrap();
184    for backend in Keyring::SUPPORTED_BACKENDS {
185        writeln!(
186            &mut help,
187            "{:16} {}",
188            backend,
189            Keyring::describe_backend(backend)
190        )
191        .unwrap();
192    }
193    writeln!(
194        &mut help,
195        "\nThe default is `{}`.",
196        Keyring::DEFAULT_BACKEND
197    )
198    .unwrap();
199
200    help.into()
201}