use crate::models::{SecretKeyValue, SecretRequest};
use crate::ValidationError;
use dialoguer::{Confirm, Input, Password};
use std::collections::HashMap;
pub fn interactive_create() -> Result<SecretRequest, anyhow::Error> {
eprintln!("\n=== Interactive Sealed Secret Creation ===\n");
let name: String = Input::new()
.with_prompt("Secret name (lowercase alphanumeric, hyphens, dots)")
.validate_with(|input: &String| -> Result<(), &str> {
crate::models::validate_secret_name(input).map_err(|_| {
"Invalid secret name. Must follow Kubernetes naming rules:\n \
- lowercase alphanumeric, hyphens, or dots\n \
- must start and end with alphanumeric\n \
- max 253 characters"
})
})
.interact_text()?;
let namespace: String = Input::new()
.with_prompt("Namespace (lowercase alphanumeric with hyphens)")
.validate_with(|input: &String| -> Result<(), &str> {
crate::models::validate_namespace(input).map_err(|_| {
"Invalid namespace. Must follow Kubernetes naming rules:\n \
- lowercase alphanumeric with hyphens\n \
- must start and end with alphanumeric\n \
- max 63 characters"
})
})
.interact_text()?;
eprintln!("\n--- Secret Data ---");
eprintln!("Enter key-value pairs (leave key empty to finish)\n");
let mut data = Vec::new();
loop {
let key: String = Input::new()
.with_prompt("Secret key")
.allow_empty(true)
.interact_text()?;
if key.is_empty() {
if data.is_empty() {
eprintln!("⚠️ At least one key-value pair is required");
continue;
}
break;
}
if let Err(e) = crate::models::validate_key_name(&key) {
eprintln!("⚠️ Invalid key: {}", e);
eprintln!(" Keys must start with letter or underscore, followed by alphanumeric or underscores\n");
continue;
}
if data.iter().any(|kv: &SecretKeyValue| kv.key == key) {
eprintln!("⚠️ Key '{}' already exists\n", key);
continue;
}
let value: String = Password::new()
.with_prompt(format!("Secret value for '{}'", key))
.interact()?;
if value.is_empty() {
eprintln!("⚠️ Value cannot be empty\n");
continue;
}
data.push(SecretKeyValue::new(key, value)?);
eprintln!("✓ Added\n");
}
eprintln!("\n--- Labels (Optional) ---");
let add_labels = Confirm::new()
.with_prompt("Add labels to the secret?")
.default(false)
.interact()?;
let mut labels = HashMap::new();
if add_labels {
eprintln!("Enter labels (leave key empty to finish)\n");
loop {
let key: String = Input::new()
.with_prompt("Label key")
.allow_empty(true)
.interact_text()?;
if key.is_empty() {
break;
}
let value: String = Input::new()
.with_prompt(format!("Label value for '{}'", key))
.interact_text()?;
labels.insert(key, value);
eprintln!("✓ Added\n");
}
}
eprintln!("\n--- Annotations (Optional) ---");
let add_annotations = Confirm::new()
.with_prompt("Add annotations to the secret?")
.default(false)
.interact()?;
let mut annotations = HashMap::new();
if add_annotations {
eprintln!("Enter annotations (leave key empty to finish)\n");
loop {
let key: String = Input::new()
.with_prompt("Annotation key")
.allow_empty(true)
.interact_text()?;
if key.is_empty() {
break;
}
let value: String = Input::new()
.with_prompt(format!("Annotation value for '{}'", key))
.interact_text()?;
annotations.insert(key, value);
eprintln!("✓ Added\n");
}
}
eprintln!("\n=== Summary ===");
eprintln!("Name: {}", name);
eprintln!("Namespace: {}", namespace);
eprintln!(
"Keys: {}",
data.iter()
.map(|kv| kv.key.as_str())
.collect::<Vec<_>>()
.join(", ")
);
if !labels.is_empty() {
eprintln!(
"Labels: {}",
labels
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join(", ")
);
} else {
eprintln!("Labels: none");
}
if !annotations.is_empty() {
eprintln!(
"Annotations: {}",
annotations
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join(", ")
);
} else {
eprintln!("Annotations: none");
}
eprintln!();
let confirmed = Confirm::new()
.with_prompt("Create SealedSecret with this configuration?")
.default(true)
.interact()?;
if !confirmed {
return Err(anyhow::anyhow!("Cancelled by user"));
}
SecretRequest::new(name, namespace, labels, annotations, data)
.map_err(|e: ValidationError| anyhow::anyhow!("Validation error: {}", e))
}
#[cfg(test)]
mod tests {
#[test]
fn test_interactive_create_function_exists() {
}
}