use crate::config::Resolved;
use secrecy::SecretString;
use std::collections::HashMap;
use std::fmt;
use tempfile::NamedTempFile;
pub struct ValidatedSecrets {
pub resolved: Resolved<HashMap<String, SecretString>>,
pub missing_optional: Vec<String>,
pub with_defaults: Vec<(String, String)>,
#[doc(hidden)]
pub(crate) temp_files: Vec<NamedTempFile>,
}
impl ValidatedSecrets {
pub fn keep_temp_files(&mut self) -> Result<Vec<std::path::PathBuf>, std::io::Error> {
let mut paths = Vec::new();
let temp_files = std::mem::take(&mut self.temp_files);
for temp_file in temp_files {
let temp_path = temp_file.into_temp_path();
let path = temp_path.keep().map_err(|e| {
std::io::Error::other(format!("Failed to persist temporary file: {}", e))
})?;
paths.push(path);
}
Ok(paths)
}
}
#[derive(Debug, Clone)]
pub struct ValidationErrors {
pub missing_required: Vec<String>,
pub missing_optional: Vec<String>,
pub with_defaults: Vec<(String, String)>,
pub provider: String,
pub profile: String,
}
impl ValidationErrors {
pub fn new(
missing_required: Vec<String>,
missing_optional: Vec<String>,
with_defaults: Vec<(String, String)>,
provider: String,
profile: String,
) -> Self {
Self {
missing_required,
missing_optional,
with_defaults,
provider,
profile,
}
}
pub fn has_errors(&self) -> bool {
!self.missing_required.is_empty()
}
}
impl fmt::Display for ValidationErrors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.missing_required.is_empty() {
write!(
f,
"Missing required secrets: {}",
self.missing_required.join(", ")
)?;
}
Ok(())
}
}
impl std::error::Error for ValidationErrors {}
#[cfg(test)]
mod tests {
use super::*;
fn errors(missing_required: Vec<&str>) -> ValidationErrors {
ValidationErrors::new(
missing_required.into_iter().map(String::from).collect(),
vec![],
vec![],
"keyring".to_string(),
"default".to_string(),
)
}
#[test]
fn has_errors_true_only_when_required_missing() {
assert!(errors(vec!["A", "B"]).has_errors());
assert!(!errors(vec![]).has_errors());
let only_optional = ValidationErrors::new(
vec![],
vec!["OPT".to_string()],
vec![("X".to_string(), "v".to_string())],
"keyring".to_string(),
"default".to_string(),
);
assert!(!only_optional.has_errors());
}
#[test]
fn display_lists_missing_required_or_is_empty() {
assert_eq!(
errors(vec!["A", "B"]).to_string(),
"Missing required secrets: A, B"
);
assert_eq!(errors(vec![]).to_string(), "");
}
}