use crate::commands::cli_session::fetch_cloudflare_credentials_from_dashboard;
use crate::commands::cloudflare_config::{
ensure_cloudflare_credentials_interactive, is_interactive_terminal,
};
use crate::config::{resolve_cloudflare_account_id, resolve_cloudflare_api_token};
use crate::provider_support::cloudflare::CloudflareClient;
#[derive(Debug, Clone, Default)]
pub struct CloudflareCredentialOverrides {
pub token: Option<String>,
pub account_id: Option<String>,
}
pub async fn resolve_cloudflare_credentials(
overrides: CloudflareCredentialOverrides,
require_account_id: bool,
) -> Result<(String, String), String> {
match try_resolve_cloudflare_credentials(&overrides, require_account_id).await {
Ok(credentials) => Ok(credentials),
Err(error) if is_interactive_terminal() && overrides_are_empty(&overrides) => {
ensure_cloudflare_credentials_interactive().await?;
try_resolve_cloudflare_credentials(&overrides, require_account_id).await
.map_err(|_| error)
}
Err(error) => Err(error),
}
}
pub async fn build_cloudflare_client(
overrides: CloudflareCredentialOverrides,
require_account_id: bool,
) -> Result<CloudflareClient, String> {
let (token, account_id) =
resolve_cloudflare_credentials(overrides, require_account_id).await?;
CloudflareClient::new(token, account_id)
}
async fn try_resolve_cloudflare_credentials(
overrides: &CloudflareCredentialOverrides,
require_account_id: bool,
) -> Result<(String, String), String> {
let token_override = normalize_override(overrides.token.clone());
let account_id_override = normalize_override(overrides.account_id.clone());
if let Some(token) = token_override.clone() {
let account_id = resolve_account_id(account_id_override, require_account_id)?;
return Ok((token, account_id));
}
if let Some(token) = resolve_cloudflare_api_token() {
let account_id = resolve_account_id(account_id_override, require_account_id)?;
return Ok((token, account_id));
}
if let Some(credentials) = fetch_cloudflare_credentials_from_dashboard().await? {
let account_id = account_id_override.unwrap_or(credentials.account_id);
return Ok((credentials.access_token, account_id));
}
Err(missing_cloudflare_credentials_message())
}
fn resolve_account_id(
account_id_override: Option<String>,
require_account_id: bool,
) -> Result<String, String> {
if let Some(account_id) = account_id_override.or_else(resolve_cloudflare_account_id) {
return Ok(account_id);
}
if require_account_id {
return Err(missing_cloudflare_account_id_message());
}
Ok(String::new())
}
fn overrides_are_empty(overrides: &CloudflareCredentialOverrides) -> bool {
overrides.token.is_none() && overrides.account_id.is_none()
}
fn normalize_override(value: Option<String>) -> Option<String> {
value
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
}
fn missing_cloudflare_credentials_message() -> String {
"Cloudflare credentials are not configured.".to_string()
}
fn missing_cloudflare_account_id_message() -> String {
"Cloudflare account ID is not configured.".to_string()
}
#[cfg(test)]
mod tests {
use super::{normalize_override, overrides_are_empty, CloudflareCredentialOverrides};
#[test]
fn normalize_override_trims_and_rejects_empty_values() {
assert_eq!(
normalize_override(Some(" token ".to_string())),
Some("token".to_string())
);
assert_eq!(normalize_override(Some(" ".to_string())), None);
assert_eq!(normalize_override(None), None);
}
#[test]
fn detects_empty_overrides() {
assert!(overrides_are_empty(&CloudflareCredentialOverrides::default()));
assert!(!overrides_are_empty(&CloudflareCredentialOverrides {
token: Some("token".to_string()),
account_id: None,
}));
}
}