use gaia_types::{
helpers::{AbiCompatible, ApiCompatible, Architecture, CompilationTarget},
GaiaError, Result,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, path::Path};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionMapping {
pub common_name: String,
pub platform_mappings: HashMap<String, String>,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformConfig {
pub target: CompilationTarget,
pub description: Option<String>,
pub supported_architectures: Vec<String>,
pub default_extension: String,
pub parameters: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdapterConfigEntry {
pub name: String,
pub adapter_type: String,
pub compilation_target: CompilationTarget,
pub enabled: bool,
pub parameters: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalConfig {
pub default_output_dir: String,
pub debug_mode: bool,
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() }
}
}
#[derive(Debug, Clone)]
pub struct GaiaConfig {
pub setting: GaiaSettings,
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,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GaiaSettings {
pub version: String,
pub global: GlobalConfig,
#[serde(with = "target_map_serde")]
pub platforms: HashMap<CompilationTarget, PlatformConfig>,
pub function_mappings: Vec<FunctionMapping>,
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 {
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![],
}
}
}
pub struct ConfigManager {
config: GaiaSettings,
config_path: Option<String>,
}
impl ConfigManager {
pub fn new() -> Self {
Self { config: GaiaSettings::default(), config_path: None }
}
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(())
}
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(())
}
pub fn settings(&self) -> &GaiaSettings {
&self.config
}
pub fn config(&self) -> &GaiaSettings {
&self.config
}
pub fn get_global_setting(&self, key: &str) -> Option<&str> {
self.config.global.parameters.get(key).map(|s| s.as_str())
}
pub fn set_global_setting(&mut self, key: String, value: String) {
self.config.global.parameters.insert(key, value);
}
pub fn get_platform_config(&self, target: &CompilationTarget) -> Option<&PlatformConfig> {
self.config.platforms.get(target)
}
pub fn add_platform_config(&mut self, target: CompilationTarget, config: PlatformConfig) {
self.config.platforms.insert(target, config);
}
pub fn add_adapter_config(&mut self, config: AdapterConfigEntry) {
self.config.adapters.push(config);
}
pub fn get_adapter_config(&self, name: &str) -> Option<&AdapterConfigEntry> {
self.config.adapters.iter().find(|a| a.name == name)
}
pub fn add_function_mapping(&mut self, mapping: FunctionMapping) {
self.config.function_mappings.push(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()))
}
pub fn validate(&self) -> Result<()> {
Ok(())
}
pub fn config_path(&self) -> Option<&str> {
self.config_path.as_deref()
}
}