mongodb_atlas_cli/config/
mod.rs

1use std::path::Path;
2
3use config::{Config, ConfigError, Environment};
4
5use serde::Deserialize;
6use source::{AtlasCLIGlobalConfigSource, AtlasCLIProfileConfigSource};
7
8use crate::path::GetCLICfgFilePathError;
9
10pub mod source;
11
12#[derive(thiserror::Error, Debug)]
13pub enum LoadCLIConfigError {
14    #[error("Failed to get config file path: {0}")]
15    FailedToGetConfigFilePath(#[from] GetCLICfgFilePathError),
16
17    #[error("Failed to build config: {0}")]
18    FailedToBuildConfig(#[from] ConfigError),
19
20    #[error("Unsupported config version: {0}")]
21    UnsupportedConfigVersion(u32),
22}
23
24#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)]
25pub struct AtlasCLIConfig {
26    /// The image to used for local deployments
27    pub local_deployment_image: Option<String>,
28    /// The path to the mongosh binary
29    pub mongosh_path: Option<String>,
30    /// Whether telemetry is enabled or not
31    pub telemetry_enabled: Option<bool>,
32    /// Whether to skip the update check
33    pub skip_update_check: Option<bool>,
34
35    /// The authentication type to use
36    pub auth_type: Option<AuthType>,
37    /// The organization ID to use
38    pub org_id: Option<String>,
39    /// The project ID to use
40    pub project_id: Option<String>,
41    /// The service to use
42    pub service: Option<Service>,
43    /// The output format to use
44    pub output: Option<OutputFormat>,
45}
46
47#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
48pub enum AuthType {
49    #[serde(rename = "user_account")]
50    UserAccount,
51    #[serde(rename = "api_keys")]
52    ApiKeys,
53    #[serde(rename = "service_account")]
54    ServiceAccount,
55}
56
57#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
58pub enum Service {
59    #[serde(rename = "cloud")]
60    Cloud,
61    #[serde(rename = "cloudgov")]
62    CloudGov,
63}
64
65#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
66pub enum OutputFormat {
67    #[serde(rename = "json")]
68    Json,
69    #[serde(rename = "plaintext")]
70    Plaintext,
71}
72
73/// Load the config from the default file and environment variables for a given profile
74/// If no profile name is provided, the default profile is loaded
75pub fn load_config(profile_name: Option<&str>) -> Result<AtlasCLIConfig, LoadCLIConfigError> {
76    // Get the default path to the config file
77    let config_file_path = crate::path::config_file()?;
78
79    // Load the config from the file
80    load_config_from_file(config_file_path, true, profile_name)
81}
82
83/// Load the config for a given file and profile, optionally loading environment variables
84pub fn load_config_from_file(
85    config_file_path: impl AsRef<Path>,
86    load_environment_variables: bool,
87    profile_name: Option<&str>,
88) -> Result<AtlasCLIConfig, LoadCLIConfigError> {
89    let config_file_path = config_file_path.as_ref();
90
91    // Build the config using the same logic as the current CLI implementation
92    //
93    // Layers (from lowest to highest precedence):
94    // 1. Load the global config
95    // 2. Load the profile config
96    // 3. Environment variables with legacy prefix MCLI_ (optionally loaded for testing)
97    // 4. Environment variables with new prefix "MONGODB_ATLAS_" (optionally loaded for testing)
98    let mut config_builder = Config::builder()
99        .add_source(AtlasCLIGlobalConfigSource::new(config_file_path))
100        .add_source(AtlasCLIProfileConfigSource::new(
101            config_file_path,
102            profile_name.unwrap_or("default"),
103        ));
104
105    // Add the environment variables if enabled
106    if load_environment_variables {
107        config_builder = config_builder
108            .add_source(Environment::with_prefix("MCLI"))
109            .add_source(Environment::with_prefix("MONGODB_ATLAS"));
110    }
111
112    // Build the config
113    let config = config_builder.build()?;
114
115    // Get the config version
116    let config_version = config
117        .get::<u32>("version")
118        .map_err(|_| LoadCLIConfigError::UnsupportedConfigVersion(0))?;
119    if config_version != 2 {
120        return Err(LoadCLIConfigError::UnsupportedConfigVersion(config_version));
121    }
122
123    // Get the config
124    let config = config.try_deserialize::<AtlasCLIConfig>()?;
125
126    Ok(config)
127}