pub mod manager;
pub mod secrets;
pub mod validation;
pub use manager::{
AuthConfig, ConfigManager, CorsConfig, DatasetConfig, GeneralConfig, OxirsConfig, QueryConfig,
RiotConfig, ServerConfig, TdbConfig, ToolsConfig, ValidationConfig,
};
pub use secrets::{SecretBackend, SecretManager};
pub use validation::{validate_config, validate_config_strict, ConfigValidator, ValidationError};
pub type Config = OxirsConfig;
pub struct CliConfig {
pub default_dataset: Option<String>,
pub default_format: String,
pub server_defaults: ServerDefaults,
}
pub struct ServerDefaults {
pub host: String,
pub port: u16,
pub enable_graphql: bool,
}
impl Default for CliConfig {
fn default() -> Self {
CliConfig {
default_dataset: None,
default_format: "turtle".to_string(),
server_defaults: ServerDefaults {
host: "localhost".to_string(),
port: 3030,
enable_graphql: false,
},
}
}
}
impl From<&OxirsConfig> for CliConfig {
fn from(config: &OxirsConfig) -> Self {
let default_dataset = config.datasets.keys().next().cloned();
CliConfig {
default_dataset,
default_format: config.general.default_format.clone(),
server_defaults: ServerDefaults {
host: config.server.host.clone(),
port: config.server.port,
enable_graphql: config.server.enable_graphql,
},
}
}
}
use crate::cli::error::{CliError, CliResult};
use std::path::{Path, PathBuf};
pub fn load_dataset_from_config<P: AsRef<Path>>(dataset_dir: P) -> CliResult<PathBuf> {
let dataset_dir = dataset_dir.as_ref();
let config_path = dataset_dir.join("oxirs.toml");
if !config_path.exists() {
return Err(CliError::config_error(format!(
"Configuration file '{}' not found",
config_path.display()
)));
}
let content = std::fs::read_to_string(&config_path).map_err(|e| {
CliError::config_error(format!(
"Failed to read config file '{}': {e}",
config_path.display()
))
})?;
let config: OxirsConfig = toml::from_str(&content).map_err(|e| {
CliError::config_error(format!(
"Failed to parse TOML in '{}': {e}",
config_path.display()
))
})?;
validate_config(&config, Some(dataset_dir))?;
let dataset_config = config
.datasets
.get("default")
.or_else(|| config.datasets.values().next())
.ok_or_else(|| {
CliError::config_error(format!(
"No dataset configuration found in '{}'",
config_path.display()
))
})?;
let storage_path = PathBuf::from(&dataset_config.location);
let storage_path = if storage_path.is_absolute() {
storage_path
} else {
dataset_dir.join(storage_path)
};
Ok(storage_path)
}
pub fn load_named_dataset<P: AsRef<Path>>(
dataset_dir: P,
dataset_name: &str,
) -> CliResult<(PathBuf, DatasetConfig)> {
let dataset_dir = dataset_dir.as_ref();
let config_path = dataset_dir.join("oxirs.toml");
if !config_path.exists() {
return Err(CliError::config_error(format!(
"Configuration file '{}' not found",
config_path.display()
)));
}
let content = std::fs::read_to_string(&config_path).map_err(|e| {
CliError::config_error(format!(
"Failed to read config file '{}': {e}",
config_path.display()
))
})?;
let config: OxirsConfig = toml::from_str(&content).map_err(|e| {
CliError::config_error(format!(
"Failed to parse TOML in '{}': {e}",
config_path.display()
))
})?;
validate_config(&config, Some(dataset_dir))?;
let dataset_config = config
.datasets
.get(dataset_name)
.ok_or_else(|| {
CliError::config_error(format!(
"Dataset '{dataset_name}' not found in configuration"
))
})?
.clone();
let storage_path = PathBuf::from(&dataset_config.location);
let storage_path = if storage_path.is_absolute() {
storage_path
} else {
dataset_dir.join(storage_path)
};
Ok((storage_path, dataset_config))
}
pub fn load_config_with_profile(
config_path: Option<&Path>,
profile: Option<&str>,
) -> CliResult<OxirsConfig> {
let mut manager = ConfigManager::new()?;
let profile = profile.unwrap_or("default");
if let Some(path) = config_path {
if !path.exists() {
return Err(CliError::config_error(format!(
"Configuration file '{}' not found",
path.display()
)));
}
let content = std::fs::read_to_string(path).map_err(|e| {
CliError::config_error(format!(
"Failed to read config file '{}': {e}",
path.display()
))
})?;
let config: OxirsConfig = toml::from_str(&content)
.map_err(|e| CliError::config_error(format!("Failed to parse TOML: {e}")))?;
validate_config(&config, path.parent())?;
Ok(config)
} else {
let config = manager.load_profile(profile)?;
Ok(config.clone())
}
}
#[cfg(test)]
mod config_tests {
use super::*;
use std::fs;
#[test]
fn test_load_dataset_from_config() -> CliResult<()> {
let temp_dir = std::env::temp_dir().join(format!("oxirs_test_{}", uuid::Uuid::new_v4()));
fs::create_dir_all(&temp_dir)?;
let config_content = r#"
[datasets.default]
name = "default"
location = "./data/default"
dataset_type = "tdb2"
read_only = false
"#;
fs::write(temp_dir.join("oxirs.toml"), config_content)?;
let storage_path = load_dataset_from_config(&temp_dir)?;
assert!(storage_path.ends_with("data/default"));
fs::remove_dir_all(&temp_dir).ok();
Ok(())
}
#[test]
fn test_load_named_dataset() -> CliResult<()> {
let temp_dir = std::env::temp_dir().join(format!("oxirs_test_{}", uuid::Uuid::new_v4()));
fs::create_dir_all(&temp_dir)?;
let config_content = r#"
[datasets.test_db]
name = "test_db"
location = "/absolute/path/to/db"
dataset_type = "tdb2"
read_only = true
"#;
fs::write(temp_dir.join("oxirs.toml"), config_content)?;
let (storage_path, config) = load_named_dataset(&temp_dir, "test_db")?;
assert_eq!(storage_path, PathBuf::from("/absolute/path/to/db"));
assert_eq!(config.dataset_type, "tdb2");
assert!(config.read_only);
fs::remove_dir_all(&temp_dir).ok();
Ok(())
}
#[test]
fn test_missing_config_file() {
let temp_dir = std::env::temp_dir().join(format!("oxirs_test_{}", uuid::Uuid::new_v4()));
let result = load_dataset_from_config(&temp_dir);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Configuration file"));
}
#[test]
fn test_invalid_toml() -> std::io::Result<()> {
let temp_dir = std::env::temp_dir().join(format!("oxirs_test_{}", uuid::Uuid::new_v4()));
fs::create_dir_all(&temp_dir)?;
fs::write(temp_dir.join("oxirs.toml"), "invalid toml [[")?;
let result = load_dataset_from_config(&temp_dir);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("parse TOML"));
fs::remove_dir_all(&temp_dir).ok();
Ok(())
}
#[test]
fn test_missing_dataset() -> std::io::Result<()> {
let temp_dir = std::env::temp_dir().join(format!("oxirs_test_{}", uuid::Uuid::new_v4()));
fs::create_dir_all(&temp_dir)?;
fs::write(
temp_dir.join("oxirs.toml"),
"[server]\nhost = \"localhost\"",
)?;
let result = load_dataset_from_config(&temp_dir);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("No dataset configuration"));
fs::remove_dir_all(&temp_dir).ok();
Ok(())
}
}