gaia-assembler 0.1.1

Universal assembler framework for Gaia project
Documentation
//! Configuration Management Module
//!
//! This module is responsible for loading and managing the Gaia assembler's configuration,
//! including platform mappings and adapter configurations.
//! It supports dynamic loading from TOML configuration files.

use gaia_types::{
    helpers::{AbiCompatible, ApiCompatible, Architecture, CompilationTarget},
    GaiaError, Result,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, path::Path};

/// Function mapping configuration.
///
/// Defines the mapping from generic function names to platform-specific function names.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionMapping {
    /// Generic function name.
    pub common_name: String,
    /// Platform-specific mappings.
    pub platform_mappings: HashMap<String, String>,
    /// Function description.
    pub description: Option<String>,
}

/// Platform configuration.
///
/// Contains configuration information for a specific platform.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformConfig {
    /// Compilation target.
    pub target: CompilationTarget,
    /// Platform description.
    pub description: Option<String>,
    /// Supported architectures.
    pub supported_architectures: Vec<String>,
    /// Default file extension.
    pub default_extension: String,
    /// Platform-specific parameters.
    pub parameters: HashMap<String, String>,
}

/// Adapter configuration.
///
/// Contains configuration information for an adapter.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdapterConfigEntry {
    /// Adapter name.
    pub name: String,
    /// Adapter type (export/import).
    pub adapter_type: String,
    /// Compilation target.
    pub compilation_target: CompilationTarget,
    /// Whether the adapter is enabled.
    pub enabled: bool,
    /// Adapter parameters.
    pub parameters: HashMap<String, String>,
}

/// Global configuration.
///
/// Contains global setting parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalConfig {
    /// Default output directory.
    pub default_output_dir: String,
    /// Whether debug mode is enabled.
    pub debug_mode: bool,
    /// Global parameters.
    pub parameters: HashMap<String, String>,
}

impl Default for GlobalConfig {
    fn default() -> Self {
        Self { default_output_dir: "./target".to_string(), debug_mode: false, parameters: HashMap::new() }
    }
}

/// Gaia configuration containing both static settings and compilation target.
#[derive(Debug, Clone)]
pub struct GaiaConfig {
    /// Static settings loaded from configuration files.
    pub setting: GaiaSettings,
    /// The compilation target for this configuration.
    pub target: CompilationTarget,
}

impl Default for GaiaConfig {
    fn default() -> Self {
        Self {
            setting: GaiaSettings::default(),
            target: CompilationTarget {
                build: Architecture::Unknown,
                host: AbiCompatible::Unknown,
                target: ApiCompatible::Unknown,
            },
        }
    }
}

/// Main configuration for the Gaia assembler
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GaiaSettings {
    /// Configuration version
    pub version: String,
    /// Global settings
    pub global: GlobalConfig,
    /// Platform configuration mappings
    #[serde(with = "target_map_serde")]
    pub platforms: HashMap<CompilationTarget, PlatformConfig>,
    /// Function mapping list
    pub function_mappings: Vec<FunctionMapping>,
    /// Adapter configuration list
    pub adapters: Vec<AdapterConfigEntry>,
}

mod target_map_serde {
    use super::*;
    use serde::{Deserializer, Serializer};
    use std::str::FromStr;

    pub fn serialize<S>(map: &HashMap<CompilationTarget, PlatformConfig>, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let string_map: HashMap<String, PlatformConfig> = map.iter().map(|(k, v)| (k.to_string(), v.clone())).collect();
        string_map.serialize(serializer)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> std::result::Result<HashMap<CompilationTarget, PlatformConfig>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let string_map: HashMap<String, PlatformConfig> = HashMap::deserialize(deserializer)?;
        let mut map = HashMap::new();
        for (k, v) in string_map {
            // NOTE: This assumes CompilationTarget::from_str is implemented or we have a way to parse it.
            // CompilationTarget has Display, but does it have FromStr?
            // Let's check gaia_types.
            let target = parse_target(&k).map_err(serde::de::Error::custom)?;
            map.insert(target, v);
        }
        Ok(map)
    }

    fn parse_target(s: &str) -> std::result::Result<CompilationTarget, String> {
        let parts: Vec<&str> = s.split('-').collect();
        if parts.len() != 3 {
            return Err(format!("Invalid compilation target string: {}", s));
        }

        let build = Architecture::from_str(parts[0])?;
        let host = AbiCompatible::from_str(parts[1])?;
        let target = ApiCompatible::from_str(parts[2])?;

        Ok(CompilationTarget { build, host, target })
    }
}

impl Default for GaiaSettings {
    fn default() -> Self {
        Self {
            version: "1.0.0".to_string(),
            global: GlobalConfig::default(),
            platforms: {
                let mut platforms = HashMap::new();
                let x64_target = CompilationTarget {
                    build: Architecture::X86_64,
                    host: AbiCompatible::PE,
                    target: ApiCompatible::MicrosoftVisualC,
                };
                platforms.insert(
                    x64_target.clone(),
                    PlatformConfig {
                        target: x64_target,
                        description: Some("Windows x64 Native".to_string()),
                        supported_architectures: vec!["x86_64".to_string()],
                        default_extension: ".exe".to_string(),
                        parameters: HashMap::new(),
                    },
                );

                let clr_target = CompilationTarget {
                    build: Architecture::CLR,
                    host: AbiCompatible::PE,
                    target: ApiCompatible::ClrRuntime(4),
                };
                platforms.insert(
                    clr_target.clone(),
                    PlatformConfig {
                        target: clr_target,
                        description: Some(".NET CLR v4.0".to_string()),
                        supported_architectures: vec!["msil".to_string()],
                        default_extension: ".exe".to_string(),
                        parameters: HashMap::new(),
                    },
                );
                platforms
            },
            function_mappings: vec![
                FunctionMapping {
                    common_name: "__builtin_print".to_string(),
                    platform_mappings: [
                        ("PE".to_string(), "printf".to_string()),
                        ("IL".to_string(), "System.Console.WriteLine".to_string()),
                        ("JVM".to_string(), "java.lang.System.out.println".to_string()),
                        ("WASI".to_string(), "fd_write".to_string()),
                    ]
                    .into_iter()
                    .collect(),
                    description: Some("Standard output function".to_string()),
                },
                FunctionMapping {
                    common_name: "malloc".to_string(),
                    platform_mappings: [
                        ("PE".to_string(), "malloc".to_string()),
                        ("IL".to_string(), "System.Runtime.InteropServices.Marshal.AllocHGlobal".to_string()),
                        ("JVM".to_string(), "java.nio.ByteBuffer.allocate".to_string()),
                        ("WASI".to_string(), "memory.grow".to_string()),
                    ]
                    .into_iter()
                    .collect(),
                    description: Some("Memory allocation function".to_string()),
                },
                FunctionMapping {
                    common_name: "free".to_string(),
                    platform_mappings: [
                        ("PE".to_string(), "free".to_string()),
                        ("IL".to_string(), "System.Runtime.InteropServices.Marshal.FreeHGlobal".to_string()),
                        ("JVM".to_string(), "System.gc".to_string()),
                        ("WASI".to_string(), "memory.shrink".to_string()),
                    ]
                    .into_iter()
                    .collect(),
                    description: Some("Memory deallocation function".to_string()),
                },
            ],
            adapters: vec![],
        }
    }
}

/// Configuration manager
///
/// Responsible for loading, saving and managing configuration information
pub struct ConfigManager {
    /// Current settings
    config: GaiaSettings,
    /// Configuration file path
    config_path: Option<String>,
}

impl ConfigManager {
    /// Create a new configuration manager
    pub fn new() -> Self {
        Self { config: GaiaSettings::default(), config_path: None }
    }

    /// Load configuration from file
    ///
    /// # Parameters
    /// * `path` - Path to the configuration file
    ///
    /// # Return Value
    /// Returns Ok(()) on success, or an error message on failure.
    pub fn load_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        let path = path.as_ref();
        let content = fs::read_to_string(path).map_err(|e| {
            GaiaError::config_error(Some(path.to_string_lossy()), format!("Failed to read configuration file: {}", e))
        })?;

        self.config = toml::from_str(&content).map_err(|e| {
            GaiaError::config_error(Some(path.to_string_lossy()), format!("Failed to parse configuration file: {}", e))
        })?;

        self.config_path = Some(path.to_string_lossy().to_string());
        Ok(())
    }

    /// Save configuration to file
    ///
    /// # Parameters
    /// * `path` - Path to the configuration file
    ///
    /// # Return Value
    /// Returns Ok(()) on success, or an error message on failure.
    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
        let path = path.as_ref();
        let content = toml::to_string_pretty(&self.config).map_err(|e| {
            GaiaError::config_error(Some(path.to_string_lossy()), format!("Failed to serialize configuration: {}", e))
        })?;

        fs::write(path, content).map_err(|e| {
            GaiaError::config_error(Some(path.to_string_lossy()), format!("Failed to write configuration file: {}", e))
        })?;

        Ok(())
    }

    /// Get current settings
    pub fn settings(&self) -> &GaiaSettings {
        &self.config
    }

    /// Get current settings (legacy compatibility)
    pub fn config(&self) -> &GaiaSettings {
        &self.config
    }

    /// Get global setting
    pub fn get_global_setting(&self, key: &str) -> Option<&str> {
        self.config.global.parameters.get(key).map(|s| s.as_str())
    }

    /// Set global setting
    pub fn set_global_setting(&mut self, key: String, value: String) {
        self.config.global.parameters.insert(key, value);
    }

    /// Get platform configuration
    pub fn get_platform_config(&self, target: &CompilationTarget) -> Option<&PlatformConfig> {
        self.config.platforms.get(target)
    }

    /// Add platform configuration
    pub fn add_platform_config(&mut self, target: CompilationTarget, config: PlatformConfig) {
        self.config.platforms.insert(target, config);
    }

    /// Add adapter configuration
    pub fn add_adapter_config(&mut self, config: AdapterConfigEntry) {
        self.config.adapters.push(config);
    }

    /// Get adapter configuration
    pub fn get_adapter_config(&self, name: &str) -> Option<&AdapterConfigEntry> {
        self.config.adapters.iter().find(|a| a.name == name)
    }

    /// Add function mapping
    pub fn add_function_mapping(&mut self, mapping: FunctionMapping) {
        self.config.function_mappings.push(mapping);
    }

    /// Get function mapping
    pub fn get_function_mapping(&self, common_name: &str, platform: &str) -> Option<&str> {
        self.config
            .function_mappings
            .iter()
            .find(|m| m.common_name == common_name)
            .and_then(|m| m.platform_mappings.get(platform).map(|s| s.as_str()))
    }

    /// Validate configuration
    pub fn validate(&self) -> Result<()> {
        Ok(())
    }

    /// Get current configuration file path
    pub fn config_path(&self) -> Option<&str> {
        self.config_path.as_deref()
    }
}