ruchy 4.2.1

A systems scripting language that transpiles to idiomatic Rust with extreme quality engineering
Documentation
// 28_configuration.ruchy - Configuration management and environment handling

import std::config
import std::env

fn main() {
    println("=== Configuration Management ===\n")

    // Environment variables
    println("=== Environment Variables ===")

    // Get environment variable
    let home = env::get("HOME")
    println(f"HOME: {home}")

    // Get with default
    let port = env::get_or("PORT", "3000")
    println(f"PORT: {port}")

    // Set environment variable
    env::set("MY_APP_VERSION", "1.0.0")
    println(f"MY_APP_VERSION: {env::get('MY_APP_VERSION')}")

    // Check if exists
    if env::exists("PATH") {
        println("PATH is set")
    }

    // Configuration file loading
    println("\n=== Configuration Files ===")

    // Load from JSON
    let json_config = config::load_json("config.json")
    println(f"JSON config: {json_config}")

    // Load from YAML
    let yaml_config = config::load_yaml("config.yaml")
    println(f"YAML config: {yaml_config}")

    // Load from TOML
    let toml_config = config::load_toml("config.toml")
    println(f"TOML config: {toml_config}")

    // Load from environment-specific file
    let env_name = env::get_or("APP_ENV", "development")
    let config_file = f"config.{env_name}.json"
    let env_config = config::load_json(config_file)

    // Configuration builder pattern
    println("\n=== Configuration Builder ===")

    struct Config {
        database: DatabaseConfig,
        server: ServerConfig,
        logging: LoggingConfig,
        features: FeatureFlags
    }

    struct DatabaseConfig {
        host: string,
        port: int,
        name: string,
        user: string,
        password: string,
        pool_size: int = 10
    }

    struct ServerConfig {
        host: string = "0.0.0.0",
        port: int = 3000,
        workers: int = 4,
        timeout: int = 30000
    }

    struct LoggingConfig {
        level: string = "info",
        format: string = "json",
        output: string = "stdout"
    }

    struct FeatureFlags {
        new_ui: bool = false,
        beta_features: bool = false,
        debug_mode: bool = false
    }

    fn build_config() {
        let mut config = Config {
            database: DatabaseConfig {
                host: env::get_or("DB_HOST", "localhost"),
                port: env::get_or("DB_PORT", "5432").to_int(),
                name: env::get_or("DB_NAME", "myapp"),
                user: env::get_or("DB_USER", "postgres"),
                password: env::get_or("DB_PASSWORD", ""),
                pool_size: env::get_or("DB_POOL_SIZE", "10").to_int()
            },
            server: ServerConfig {
                host: env::get_or("SERVER_HOST", "0.0.0.0"),
                port: env::get_or("SERVER_PORT", "3000").to_int(),
                workers: env::get_or("SERVER_WORKERS", "4").to_int(),
                timeout: env::get_or("SERVER_TIMEOUT", "30000").to_int()
            },
            logging: LoggingConfig {
                level: env::get_or("LOG_LEVEL", "info"),
                format: env::get_or("LOG_FORMAT", "json"),
                output: env::get_or("LOG_OUTPUT", "stdout")
            },
            features: FeatureFlags {
                new_ui: env::get_or("FEATURE_NEW_UI", "false") == "true",
                beta_features: env::get_or("FEATURE_BETA", "false") == "true",
                debug_mode: env::get_or("DEBUG_MODE", "false") == "true"
            }
        }

        config
    }

    let app_config = build_config()
    println(f"App config: {app_config}")

    // Configuration validation
    println("\n=== Configuration Validation ===")

    fn validate_config(config) {
        let errors = []

        // Validate database config
        if config.database.host == "" {
            errors.append("Database host is required")
        }
        if config.database.port < 1 || config.database.port > 65535 {
            errors.append("Invalid database port")
        }
        if config.database.pool_size < 1 {
            errors.append("Pool size must be at least 1")
        }

        // Validate server config
        if config.server.port < 1 || config.server.port > 65535 {
            errors.append("Invalid server port")
        }
        if config.server.workers < 1 {
            errors.append("Must have at least 1 worker")
        }

        // Validate logging config
        let valid_levels = ["trace", "debug", "info", "warn", "error"]
        if !valid_levels.contains(config.logging.level) {
            errors.append(f"Invalid log level: {config.logging.level}")
        }

        if errors.len() > 0 {
            Err(errors)
        } else {
            Ok(config)
        }
    }

    match validate_config(app_config) {
        Ok(_) => println("Configuration is valid"),
        Err(errors) => {
            println("Configuration errors:")
            for error in errors {
                println(f"  - {error}")
            }
        }
    }

    // Hierarchical configuration
    println("\n=== Hierarchical Configuration ===")

    struct ConfigManager {
        sources: list,
        cache: map
    }

    impl ConfigManager {
        fn new() {
            ConfigManager {
                sources: [],
                cache: {}
            }
        }

        fn add_source(mut self, source) {
            self.sources.append(source)
        }

        fn get(self, key, default = None) {
            // Check cache first
            if key in self.cache {
                return self.cache[key]
            }

            // Check sources in order
            for source in self.sources {
                if let Some(value) = source.get(key) {
                    self.cache[key] = value
                    return value
                }
            }

            default
        }

        fn reload(mut self) {
            self.cache.clear()
            for source in self.sources {
                source.reload()
            }
        }
    }

    let config_manager = ConfigManager::new()
    config_manager.add_source(EnvSource::new())
    config_manager.add_source(FileSource::new("config.json"))
    config_manager.add_source(DefaultsSource::new())

    // Secret management
    println("\n=== Secret Management ===")

    struct SecretManager {
        provider: string
    }

    impl SecretManager {
        fn get_secret(self, key) {
            match self.provider {
                "env" => env::get(key),
                "file" => {
                    let secrets_file = f".secrets/{key}"
                    fs::read_to_string(secrets_file).trim()
                },
                "vault" => {
                    // HashiCorp Vault integration
                    vault::get_secret(key)
                },
                "aws" => {
                    // AWS Secrets Manager
                    aws::secrets_manager::get_secret(key)
                },
                _ => None
            }
        }

        fn rotate_secret(self, key) {
            println(f"Rotating secret: {key}")
            // Implementation depends on provider
        }
    }

    // Feature flags
    println("\n=== Feature Flags ===")

    struct FeatureManager {
        flags: map,
        user_overrides: map
    }

    impl FeatureManager {
        fn is_enabled(self, feature, user_id = None) {
            // Check user-specific override
            if user_id && user_id in self.user_overrides {
                if feature in self.user_overrides[user_id] {
                    return self.user_overrides[user_id][feature]
                }
            }

            // Check global flag
            self.flags.get(feature, false)
        }

        fn enable(mut self, feature) {
            self.flags[feature] = true
        }

        fn disable(mut self, feature) {
            self.flags[feature] = false
        }

        fn set_user_override(mut self, user_id, feature, enabled) {
            if user_id not in self.user_overrides {
                self.user_overrides[user_id] = {}
            }
            self.user_overrides[user_id][feature] = enabled
        }
    }

    let features = FeatureManager {
        flags: {
            "new_dashboard": true,
            "dark_mode": true,
            "beta_api": false
        },
        user_overrides: {}
    }

    // Configuration hot reload
    println("\n=== Hot Reload ===")

    fn watch_config(file_path, callback) {
        let watcher = fs::watch(file_path)

        watcher.on_change(|| {
            println(f"Config file changed: {file_path}")
            let new_config = config::load_json(file_path)
            callback(new_config)
        })

        watcher
    }

    // Profile-based configuration
    println("\n=== Profiles ===")

    let profiles = {
        "development": {
            debug: true,
            log_level: "debug",
            database: "dev.db",
            cache: false
        },
        "staging": {
            debug: false,
            log_level: "info",
            database: "staging.db",
            cache: true
        },
        "production": {
            debug: false,
            log_level: "warn",
            database: "prod.db",
            cache: true,
            monitoring: true
        }
    }

    let active_profile = env::get_or("PROFILE", "development")
    let profile_config = profiles[active_profile]
    println(f"Active profile: {active_profile}")
    println(f"Profile config: {profile_config}")

    // Configuration export
    println("\n=== Configuration Export ===")

    fn export_config(config, format) {
        match format {
            "json" => config.to_json(),
            "yaml" => config.to_yaml(),
            "toml" => config.to_toml(),
            "env" => {
                let lines = []
                for (key, value) in config.flatten() {
                    lines.append(f"{key.upper()}={value}")
                }
                lines.join("\n")
            },
            _ => config.to_string()
        }
    }

    let exported = export_config(app_config, "env")
    println("Exported as env vars:")
    println(exported)
}