use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, Example, LabeledError, PipelineData, Record, Signature, SyntaxShape, Type, Value,
};
use crate::config::{ConfigManager, PluginConfig, SecurityLevel};
pub struct SecretConfigureCommand;
fn apply_security_level_change(
plugin: &crate::SecretPlugin,
level_str: &str,
span: nu_protocol::Span,
) -> Result<PluginConfig, LabeledError> {
let mut config = plugin
.config_manager()
.read()
.map_err(|e| {
LabeledError::new("Configuration Error")
.with_label(format!("Failed to read configuration: {}", e), span)
})?
.config()
.clone();
let level = match level_str {
"minimal" => SecurityLevel::Minimal,
"standard" => SecurityLevel::Standard,
"paranoid" => SecurityLevel::Paranoid,
_ => {
return Err(LabeledError::new("Invalid Security Level").with_label(
format!(
"Unknown level '{}'. Valid options: minimal, standard, paranoid",
level_str
),
span,
));
}
};
config.security.level = level;
Ok(config)
}
fn build_config_summary_record(
config_manager: &ConfigManager,
config_changed: bool,
span: nu_protocol::Span,
) -> Record {
let mut record = Record::new();
record.push(
"redaction_template",
Value::string(
config_manager.config().redaction.get_redaction_template(),
span,
),
);
record.push(
"security_level",
Value::string(
format!("{:?}", config_manager.config().security.level).to_lowercase(),
span,
),
);
if config_changed {
record.push(
"status",
Value::string("Configuration updated successfully", span),
);
} else {
record.push("status", Value::string("No changes made", span));
}
record
}
impl PluginCommand for SecretConfigureCommand {
type Plugin = crate::SecretPlugin;
fn name(&self) -> &str {
"secret configure"
}
fn description(&self) -> &str {
"Configure secret plugin settings interactively"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.input_output_types(vec![(Type::Nothing, Type::Record(Box::new([])))])
.named(
"security-level",
SyntaxShape::String,
"Set security level (minimal, standard, paranoid)",
Some('s'),
)
.category(Category::Custom("secret".into()))
}
fn examples(&self) -> Vec<Example<'_>> {
vec![Example {
example: "secret configure --security-level paranoid",
description: "Set security level to paranoid (maximum security)",
result: None,
}]
}
fn run(
&self,
plugin: &Self::Plugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
_input: PipelineData,
) -> Result<PipelineData, LabeledError> {
let span = call.head;
let mut config_changed = false;
let mut new_config = None;
if let Some(level_str) = call.get_flag::<String>("security-level")? {
new_config = Some(apply_security_level_change(plugin, &level_str, span)?);
config_changed = true;
}
if let Some(config) = new_config {
if let Err(e) = ConfigManager::validate_config(&config) {
return Err(LabeledError::new("Configuration Validation Failed")
.with_label(format!("Invalid configuration: {}", e), span));
}
if let Ok(current_manager) = plugin.config_manager().read() {
if current_manager.config().security.audit_config_changes {
let _ = crate::config::audit_config_change(current_manager.config(), &config);
}
}
let mut config_manager = plugin.config_manager().write().map_err(|e| {
LabeledError::new("Configuration Error")
.with_label(format!("Failed to acquire write lock: {}", e), span)
})?;
*config_manager.config_mut() = config;
config_manager.save().map_err(|e| {
LabeledError::new("Save Error")
.with_label(format!("Failed to save configuration: {}", e), span)
})?;
}
let config_manager = plugin.config_manager().read().map_err(|e| {
LabeledError::new("Configuration Error")
.with_label(format!("Failed to read configuration: {}", e), span)
})?;
let record = build_config_summary_record(&config_manager, config_changed, span);
Ok(PipelineData::Value(Value::record(record, span), None))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_name() {
let command = SecretConfigureCommand;
assert_eq!(command.name(), "secret configure");
}
#[test]
fn test_signature() {
let command = SecretConfigureCommand;
let signature = command.signature();
assert_eq!(signature.name, "secret configure");
assert!(signature.named.iter().any(|n| n.long == "security-level"));
}
}