Files
foxhunt/crates/config/src/error.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
Move 17 library crates into crates/, CLI binary into bin/fxt,
consolidate 10 test crates into testing/, split config crate
from deployment config files.

Root directory reduced from 38+ to ~17 directories.
All Cargo.toml paths and build.rs proto refs updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:56:00 +01:00

119 lines
3.7 KiB
Rust

//! Configuration error types and result handling.
//!
//! This module defines comprehensive error types for configuration management
//! operations, including database errors, vault integration failures, parsing
//! errors, and validation issues. Uses thiserror for ergonomic error handling.
use thiserror::Error;
/// Comprehensive error type for configuration management operations.
///
/// Covers all possible error conditions that can occur during configuration
/// loading, validation, and management operations. Each variant provides
/// specific context about the failure to aid in debugging and error handling.
#[derive(Error, Debug)]
#[allow(clippy::module_name_repetitions)]
pub enum ConfigError {
/// Database operation failed (connection, query, or transaction error)
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
/// HashiCorp Vault integration error (authentication, secret retrieval, etc.)
#[error("Vault error: {0}")]
Vault(String),
/// Configuration parsing error (invalid TOML, JSON, or environment variables)
#[error("Parse error: {0}")]
Parse(String),
/// Requested configuration key or resource was not found
#[error("Not found: {0}")]
NotFound(String),
/// Configuration validation failed (invalid values, missing required fields)
#[error("Invalid configuration: {0}")]
Invalid(String),
}
/// Result type alias for configuration operations.
///
/// Provides a convenient Result type that uses ConfigError as the error type.
///
/// Used throughout the configuration system for consistent error handling.
pub type ConfigResult<T> = Result<T, ConfigError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vault_error_display() {
let error = ConfigError::Vault("Connection failed".to_owned());
assert_eq!(format!("{}", error), "Vault error: Connection failed");
}
#[test]
fn test_parse_error_display() {
let error = ConfigError::Parse("Invalid JSON".to_owned());
assert_eq!(format!("{}", error), "Parse error: Invalid JSON");
}
#[test]
fn test_not_found_error_display() {
let error = ConfigError::NotFound("config_key".to_owned());
assert_eq!(format!("{}", error), "Not found: config_key");
}
#[test]
fn test_invalid_error_display() {
let error = ConfigError::Invalid("Missing required field".to_owned());
assert_eq!(
format!("{}", error),
"Invalid configuration: Missing required field"
);
}
#[test]
fn test_error_debug_format() {
let error = ConfigError::Vault("Test error".to_owned());
let debug_output = format!("{:?}", error);
assert!(debug_output.contains("Vault"));
assert!(debug_output.contains("Test error"));
}
#[test]
fn test_config_result_ok() {
let result: ConfigResult<i32> = Ok(42);
assert!(result.is_ok());
match result {
Ok(value) => assert_eq!(value, 42),
Err(_) => panic!("Expected Ok value"),
}
}
#[test]
fn test_config_result_err() {
let result: ConfigResult<i32> = Err(ConfigError::NotFound("test".to_owned()));
assert!(result.is_err());
}
#[test]
fn test_error_type_matching() {
let error = ConfigError::Parse("syntax error".to_owned());
match error {
ConfigError::Parse(msg) => assert_eq!(msg, "syntax error"),
_ => panic!("Expected Parse error"),
}
}
#[test]
fn test_vault_error_creation() {
let error = ConfigError::Vault("Token expired".to_owned());
if let ConfigError::Vault(msg) = error {
assert_eq!(msg, "Token expired");
} else {
panic!("Expected Vault error");
}
}
}