# 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" } } }