60 lines
2.1 KiB
Rust
60 lines
2.1 KiB
Rust
// --- External Crates ---
|
|
use serde::Deserialize;
|
|
use anyhow::{Context, Result}; // Removed unused 'anyhow' macro import if not used, but Context/Result likely used
|
|
use std::fmt::{self, Display};
|
|
|
|
// --- 1. CUSTOM ERROR DEFINITION ---
|
|
|
|
#[derive(Debug)]
|
|
pub struct ConfigError(String);
|
|
|
|
impl Display for ConfigError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "Configuration Error: {}", self.0)
|
|
}
|
|
}
|
|
impl std::error::Error for ConfigError {}
|
|
|
|
|
|
// --- 2. CONFIGURATION STRUCTS (The "Configured Opinion") ---
|
|
// We make these 'pub' (public) so src/lib.rs can use them.
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
pub struct ColumnConfig {
|
|
pub column_name: String,
|
|
pub arrow_type: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct ConnectorConfig {
|
|
pub connection_string: String,
|
|
pub query: String,
|
|
pub schema: Vec<ColumnConfig>,
|
|
}
|
|
|
|
// --- 3. CONFIG LOADING FUNCTION ---
|
|
// This is also 'pub' so src/lib.rs can call it.
|
|
pub fn load_and_validate_config(path: &str) -> Result<ConnectorConfig> {
|
|
let file_content = std::fs::read_to_string(path)
|
|
.context(format!("Failed to read config file at path: {}", path))?;
|
|
|
|
let config: ConnectorConfig = serde_yaml::from_str(&file_content)
|
|
.context("Failed to deserialize YAML configuration")?;
|
|
|
|
if config.connection_string.is_empty() {
|
|
return Err(ConfigError("Connection string cannot be empty.".to_string())).map_err(anyhow::Error::from)?;
|
|
}
|
|
if config.query.is_empty() {
|
|
return Err(ConfigError("Query cannot be empty.".to_string())).map_err(anyhow::Error::from)?;
|
|
}
|
|
// FIX: Make the COPY check more flexible (allows newlines)
|
|
let uppercase_query = config.query.trim().to_uppercase();
|
|
if !uppercase_query.starts_with("COPY") || !uppercase_query.contains("TO STDOUT") || !uppercase_query.contains("FORMAT BINARY") {
|
|
return Err(ConfigError("Query must be a 'COPY ... TO STDOUT (FORMAT binary)' command.".to_string())).map_err(anyhow::Error::from)?;
|
|
}
|
|
if config.schema.is_empty() {
|
|
return Err(ConfigError("Schema cannot be empty.".to_string())).map_err(anyhow::Error::from)?;
|
|
}
|
|
|
|
Ok(config)
|
|
} |