provisioning/core/nulib/lib_provisioning/config/loader.nu

263 lines
7.6 KiB
Plaintext
Raw Normal View History

# Configuration Loader for Provisioning System
# Implements hierarchical configuration loading with variable interpolation
use std log
# Main configuration loader - loads and merges all config sources
export def load-provisioning-config [
--debug = false # Enable debug logging
--validate = true # Validate configuration
--environment: string # Override environment (dev/prod/test)
] {
if $debug {
# log debug "Loading provisioning configuration..."
}
# Define configuration sources in precedence order (lowest to highest)
let config_sources = [
# 1. System defaults (lowest precedence)
{
name: "defaults"
path: "/Users/Akasha/repo-cnz/src/provisioning/config.defaults.toml"
required: true
}
# 2. User configuration
{
name: "user"
path: ($env.HOME | path join ".config" | path join "provisioning" | path join "config.toml")
required: false
}
# 3. Project configuration
{
name: "project"
path: ($env.PWD | path join "provisioning.toml")
required: false
}
# 4. Infrastructure-specific configuration (highest precedence)
{
name: "infra"
path: ($env.PWD | path join ".provisioning.toml")
required: false
}
]
mut final_config = {}
# Load and merge configurations
for source in $config_sources {
let config_data = (load-config-file $source.path $source.required $debug)
if ($config_data | is-not-empty) {
if $debug {
# log debug $"Loaded ($source.name) config from ($source.path)"
}
$final_config = (deep-merge $final_config $config_data)
}
}
# Apply environment-specific overrides if specified
if ($environment | is-not-empty) {
let env_config = ($final_config | get -o $"environments.($environment)" | default {})
if ($env_config | is-not-empty) {
if $debug {
# log debug $"Applying environment overrides for: ($environment)"
}
$final_config = (deep-merge $final_config $env_config)
}
}
# Interpolate variables in the final configuration
$final_config = (interpolate-config $final_config)
# Validate configuration if requested
if $validate {
validate-config $final_config
}
if $debug {
# log debug "Configuration loading completed"
}
$final_config
}
# Load a single configuration file
export def load-config-file [
file_path: string
required = false
debug = false
] {
if not ($file_path | path exists) {
if $required {
error make {
msg: $"Required configuration file not found: ($file_path)"
}
} else {
if $debug {
# log debug $"Optional config file not found: ($file_path)"
}
return {}
}
}
if $debug {
# log debug $"Loading config file: ($file_path)"
}
# Load the file - use error handling without complex try-catch
if ($file_path | path exists) {
open $file_path
} else {
if $required {
error make {
msg: $"Configuration file not found: ($file_path)"
}
} else {
{}
}
}
}
# Deep merge two configuration records (right takes precedence)
export def deep-merge [
base: record
override: record
] {
mut result = $base
for key in ($override | columns) {
let override_value = ($override | get $key)
let base_value = ($base | get -o $key)
if ($base_value | is-empty) {
# Key doesn't exist in base, add it
$result = ($result | insert $key $override_value)
} else if (($base_value | describe) == "record") and (($override_value | describe) == "record") {
# Both are records, merge recursively
$result = ($result | upsert $key (deep-merge $base_value $override_value))
} else {
# Override the value
$result = ($result | upsert $key $override_value)
}
}
$result
}
# Interpolate variables in configuration values
export def interpolate-config [
config: record
] {
mut result = $config
# First pass: resolve simple base values
if ($config | get -o paths.base | is-not-empty) {
let base_path = ($config | get paths.base)
$result = ($result | update paths (
$result | get paths |
transpose key value |
each {|item|
if ($item.value | describe) == "string" and ($item.value | str contains "${paths.base}") {
{key: $item.key, value: ($item.value | str replace --all "${paths.base}" $base_path)}
} else {
$item
}
} |
transpose -r |
into record
))
}
$result
}
# Interpolate variables in a string using ${path.to.value} syntax
export def interpolate-string [
text: string
config: record
] {
mut result = $text
# Simple interpolation for ${paths.base} pattern
if ($result | str contains "${paths.base}") {
let base_path = (get-config-value $config "paths.base" "")
$result = ($result | str replace --all "${paths.base}" $base_path)
}
# Add more interpolation patterns as needed
# This is a basic implementation - a full template engine would be more robust
$result
}
# Get a nested configuration value using dot notation
export def get-config-value [
config: record
path: string
default_value: any = null
] {
let path_parts = ($path | split row ".")
mut current = $config
for part in $path_parts {
let next_value = ($current | get -o $part)
if ($next_value | is-empty) {
return $default_value
}
$current = $next_value
}
$current
}
# Validate the final configuration
export def validate-config [
config: record
] {
# Check required top-level sections
let required_sections = ["core", "paths", "debug"]
for section in $required_sections {
if ($config | get -o $section | is-empty) {
error make {
msg: $"Missing required configuration section: ($section)"
}
}
}
# Validate core section
let core = ($config | get core)
if ($core | get -o version | is-empty) {
error make {
msg: "Missing required core.version in configuration"
}
}
# Validate paths section
let paths = ($config | get paths)
if ($paths | get -o base | is-empty) {
error make {
msg: "Missing required paths.base in configuration"
}
}
# Additional validation can be added here
}
# Helper function to create directory structure for user config
export def init-user-config [] {
let config_dir = ($env.HOME | path join ".config" | path join "provisioning")
if not ($config_dir | path exists) {
mkdir $config_dir
print $"Created user config directory: ($config_dir)"
}
let user_config_path = ($config_dir | path join "config.toml")
if not ($user_config_path | path exists) {
let example_path = ($env.FILE_PWD | path join ".." | path join ".." | path join ".." | path join ".." | path join "config.user.toml.example")
if ($example_path | path exists) {
cp $example_path $user_config_path
print $"Created user config file: ($user_config_path)"
print "Edit this file to customize your provisioning settings"
}
}
}