use std::fmt;
use super::error::CredentialError;
pub use systemprompt_models::services::providers::{PROJECT_PLACEHOLDER, REGION_PLACEHOLDER};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthScheme {
Bearer,
ApiKey,
}
#[derive(Clone)]
pub struct AuthHeader {
pub scheme: AuthScheme,
pub value: String,
}
impl AuthHeader {
#[must_use]
pub const fn is_bearer(&self) -> bool {
matches!(self.scheme, AuthScheme::Bearer)
}
}
impl fmt::Debug for AuthHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AuthHeader")
.field("scheme", &self.scheme)
.field("value", &"<redacted>")
.finish()
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CredentialScope {
pub project: Option<String>,
pub region: Option<String>,
pub principal: Option<String>,
}
impl CredentialScope {
#[must_use]
pub const fn empty() -> Self {
Self {
project: None,
region: None,
principal: None,
}
}
fn value_for(&self, placeholder: &str) -> Option<&str> {
let value = match placeholder {
PROJECT_PLACEHOLDER => self.project.as_deref(),
REGION_PLACEHOLDER => self.region.as_deref(),
_ => None,
};
value.filter(|v| !v.is_empty())
}
}
const PLACEHOLDERS: &[(&str, &str)] = &[
(PROJECT_PLACEHOLDER, "project id"),
(REGION_PLACEHOLDER, "region"),
];
pub fn fill_endpoint(template: &str, scope: &CredentialScope) -> Result<String, CredentialError> {
let mut endpoint = template.to_owned();
for &(placeholder, field) in PLACEHOLDERS {
if !endpoint.contains(placeholder) {
continue;
}
let Some(value) = scope.value_for(placeholder) else {
return Err(CredentialError::MissingScope {
endpoint: template.to_owned(),
field,
placeholder,
});
};
endpoint = endpoint.replace(placeholder, value);
}
Ok(endpoint)
}