Skip to main content

api_testing_core/
cli_endpoint.rs

1use std::path::Path;
2
3use crate::{Result, cli_util, env_file};
4
5#[derive(Debug, Clone)]
6pub struct EndpointSelection {
7    pub url: String,
8    pub endpoint_label_used: String,
9    pub endpoint_value_used: String,
10}
11
12pub struct EndpointConfig<'a> {
13    pub explicit_url: Option<&'a str>,
14    pub env_name: Option<&'a str>,
15    pub endpoints_env: &'a Path,
16    pub endpoints_local: &'a Path,
17    pub endpoints_files: &'a [&'a Path],
18    pub url_env_var: &'a str,
19    pub env_default_var: &'a str,
20    pub url_prefix: &'a str,
21    pub default_url: &'a str,
22    pub setup_dir_label: &'a str,
23}
24
25pub fn resolve_cli_endpoint(cfg: EndpointConfig<'_>) -> Result<EndpointSelection> {
26    let env_default = if !cfg.endpoints_files.is_empty() {
27        env_file::read_var_last_wins(cfg.env_default_var, cfg.endpoints_files)?
28    } else {
29        None
30    };
31
32    let explicit_url = cfg.explicit_url.and_then(cli_util::trim_non_empty);
33    let env_name = cfg.env_name.and_then(cli_util::trim_non_empty);
34
35    let (url, endpoint_label_used, endpoint_value_used) = if let Some(url) = explicit_url {
36        (url.clone(), "url".to_string(), url)
37    } else if let Some(env_value) = env_name.as_deref() {
38        if env_value.starts_with("http://") || env_value.starts_with("https://") {
39            (
40                env_value.to_string(),
41                "url".to_string(),
42                env_value.to_string(),
43            )
44        } else {
45            if cfg.endpoints_files.is_empty() {
46                anyhow::bail!(
47                    "endpoints.env not found (expected under {setup_dir_label})",
48                    setup_dir_label = cfg.setup_dir_label
49                );
50            }
51
52            let env_key = cli_util::to_env_key(env_value);
53            let key = format!("{url_prefix}{env_key}", url_prefix = cfg.url_prefix);
54            let found = env_file::read_var_last_wins(&key, cfg.endpoints_files)?;
55            let Some(found) = found else {
56                let mut available =
57                    cli_util::list_available_suffixes(cfg.endpoints_env, cfg.url_prefix);
58                if cfg.endpoints_local.is_file() {
59                    available.extend(cli_util::list_available_suffixes(
60                        cfg.endpoints_local,
61                        cfg.url_prefix,
62                    ));
63                    available.sort();
64                    available.dedup();
65                }
66                let available = if available.is_empty() {
67                    "none".to_string()
68                } else {
69                    available.join(" ")
70                };
71                anyhow::bail!("Unknown --env '{env_value}' (available: {available})");
72            };
73
74            (found, "env".to_string(), env_value.to_string())
75        }
76    } else if let Some(v) = std::env::var(cfg.url_env_var)
77        .ok()
78        .and_then(|s| cli_util::trim_non_empty(&s))
79    {
80        (v.clone(), "url".to_string(), v)
81    } else if let Some(default_env) = env_default {
82        if cfg.endpoints_files.is_empty() {
83            anyhow::bail!(
84                "{env_default_var} is set but endpoints.env not found (expected under {setup_dir_label})",
85                env_default_var = cfg.env_default_var,
86                setup_dir_label = cfg.setup_dir_label
87            );
88        }
89        let env_key = cli_util::to_env_key(&default_env);
90        let key = format!("{url_prefix}{env_key}", url_prefix = cfg.url_prefix);
91        let found = env_file::read_var_last_wins(&key, cfg.endpoints_files)?;
92        let Some(found) = found else {
93            anyhow::bail!(
94                "{env_default_var} is '{}' but no matching {url_prefix}* was found.",
95                default_env,
96                env_default_var = cfg.env_default_var,
97                url_prefix = cfg.url_prefix
98            );
99        };
100        (found, "env".to_string(), default_env)
101    } else {
102        let url = cfg.default_url.to_string();
103        (url.clone(), "url".to_string(), url)
104    };
105
106    Ok(EndpointSelection {
107        url,
108        endpoint_label_used,
109        endpoint_value_used,
110    })
111}
112
113pub fn list_available_env_suffixes(
114    endpoints_env: &Path,
115    endpoints_local: &Path,
116    url_prefix: &str,
117    missing_message: &str,
118) -> Result<Vec<String>> {
119    if !endpoints_env.is_file() && !endpoints_local.is_file() {
120        anyhow::bail!("{missing_message}");
121    }
122
123    let mut out = Vec::new();
124    if endpoints_env.is_file() {
125        out.extend(cli_util::list_available_suffixes(endpoints_env, url_prefix));
126    }
127    if endpoints_local.is_file() {
128        out.extend(cli_util::list_available_suffixes(
129            endpoints_local,
130            url_prefix,
131        ));
132    }
133    out.sort();
134    out.dedup();
135
136    Ok(out)
137}