use crate::types::Effect;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum OptimizationLevel {
O0,
O1,
O2,
#[default]
O3,
}
#[derive(Debug, Clone)]
pub struct ExternalBuiltin {
pub seq_name: String,
pub symbol: String,
pub effect: Option<Effect>,
}
impl ExternalBuiltin {
fn validate_symbol(symbol: &str) -> Result<(), String> {
if symbol.is_empty() {
return Err("Symbol name cannot be empty".to_string());
}
for c in symbol.chars() {
if !c.is_alphanumeric() && c != '_' && c != '.' {
return Err(format!(
"Invalid character '{}' in symbol '{}'. \
Symbols may only contain alphanumeric characters, underscores, and periods.",
c, symbol
));
}
}
Ok(())
}
#[deprecated(
since = "2.0.0",
note = "Use with_effect instead - effects are now required"
)]
pub fn new(seq_name: impl Into<String>, symbol: impl Into<String>) -> Self {
let symbol = symbol.into();
Self::validate_symbol(&symbol).expect("Invalid symbol name");
ExternalBuiltin {
seq_name: seq_name.into(),
symbol,
effect: None,
}
}
pub fn with_effect(
seq_name: impl Into<String>,
symbol: impl Into<String>,
effect: Effect,
) -> Self {
let symbol = symbol.into();
Self::validate_symbol(&symbol).expect("Invalid symbol name");
ExternalBuiltin {
seq_name: seq_name.into(),
symbol,
effect: Some(effect),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct CompilerConfig {
pub external_builtins: Vec<ExternalBuiltin>,
pub library_paths: Vec<String>,
pub libraries: Vec<String>,
pub ffi_manifest_paths: Vec<PathBuf>,
pub pure_inline_test: bool,
pub optimization_level: OptimizationLevel,
}
impl CompilerConfig {
pub fn new() -> Self {
CompilerConfig::default()
}
pub fn with_builtin(mut self, builtin: ExternalBuiltin) -> Self {
self.external_builtins.push(builtin);
self
}
pub fn with_builtins(mut self, builtins: impl IntoIterator<Item = ExternalBuiltin>) -> Self {
self.external_builtins.extend(builtins);
self
}
pub fn with_library_path(mut self, path: impl Into<String>) -> Self {
self.library_paths.push(path.into());
self
}
pub fn with_library(mut self, lib: impl Into<String>) -> Self {
self.libraries.push(lib.into());
self
}
pub fn with_ffi_manifest(mut self, path: impl Into<PathBuf>) -> Self {
self.ffi_manifest_paths.push(path.into());
self
}
pub fn with_ffi_manifests(mut self, paths: impl IntoIterator<Item = PathBuf>) -> Self {
self.ffi_manifest_paths.extend(paths);
self
}
pub fn with_optimization_level(mut self, level: OptimizationLevel) -> Self {
self.optimization_level = level;
self
}
pub fn external_names(&self) -> Vec<&str> {
self.external_builtins
.iter()
.map(|b| b.seq_name.as_str())
.collect()
}
}
#[cfg(test)]
#[allow(deprecated)] mod tests {
use super::*;
#[test]
fn test_external_builtin_new() {
let builtin = ExternalBuiltin::new("my-func", "runtime_my_func");
assert_eq!(builtin.seq_name, "my-func");
assert_eq!(builtin.symbol, "runtime_my_func");
assert!(builtin.effect.is_none());
}
#[test]
fn test_config_builder() {
let config = CompilerConfig::new()
.with_builtin(ExternalBuiltin::new("func-a", "sym_a"))
.with_builtin(ExternalBuiltin::new("func-b", "sym_b"))
.with_library_path("/custom/lib")
.with_library("myruntime");
assert_eq!(config.external_builtins.len(), 2);
assert_eq!(config.library_paths, vec!["/custom/lib"]);
assert_eq!(config.libraries, vec!["myruntime"]);
}
#[test]
fn test_external_names() {
let config = CompilerConfig::new()
.with_builtin(ExternalBuiltin::new("func-a", "sym_a"))
.with_builtin(ExternalBuiltin::new("func-b", "sym_b"));
let names = config.external_names();
assert_eq!(names, vec!["func-a", "func-b"]);
}
#[test]
fn test_symbol_validation_valid() {
let _ = ExternalBuiltin::new("test", "valid_symbol");
let _ = ExternalBuiltin::new("test", "valid.symbol.123");
let _ = ExternalBuiltin::new("test", "ValidCamelCase");
let _ = ExternalBuiltin::new("test", "seq_actors_journal_append");
}
#[test]
#[should_panic(expected = "Invalid symbol name")]
fn test_symbol_validation_rejects_hyphen() {
let _ = ExternalBuiltin::new("test", "invalid-symbol");
}
#[test]
#[should_panic(expected = "Invalid symbol name")]
fn test_symbol_validation_rejects_at() {
let _ = ExternalBuiltin::new("test", "@malicious");
}
#[test]
#[should_panic(expected = "Invalid symbol name")]
fn test_symbol_validation_rejects_empty() {
let _ = ExternalBuiltin::new("test", "");
}
}