use crate::diagnostics::{Error as DiagnosticError, Result};
use crate::eval::value::{Value, PrimitiveProcedure, PrimitiveImpl, ThreadSafeEnvironment};
use crate::effects::Effect;
use crate::stdlib::exceptions::{ExceptionObject, ErrorObject};
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
pub struct SRFI23ErrorObject {
pub message: String,
pub irritants: Vec<Value>,
pub error_object: ErrorObject,
}
impl SRFI23ErrorObject {
pub fn new(message: String, irritants: Vec<Value>) -> Self {
let error_object = ErrorObject::new(message.clone(), irritants.clone());
Self {
message,
irritants,
error_object,
}
}
pub fn validate_message(value: &Value) -> Result<String> {
match value {
Value::Literal(crate::ast::Literal::String(s)) => Ok(s.clone()),
_ => Err(Box::new(DiagnosticError::runtime_error(
"SRFI-23 error: message must be a string".to_string(),
None,
)))
}
}
pub fn to_exception(&self) -> ExceptionObject {
ExceptionObject::error(self.message.clone(), self.irritants.clone())
}
}
pub fn enhanced_error_procedure(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Box::new(DiagnosticError::runtime_error(
"SRFI-23 error: error procedure requires at least one argument (message)".to_string(),
None,
)));
}
let message = SRFI23ErrorObject::validate_message(&args[0])?;
let irritants = args[1..].to_vec();
let srfi23_error = SRFI23ErrorObject::new(message, irritants);
let exception = srfi23_error.to_exception();
Err(Box::new(DiagnosticError::exception(exception)))
}
pub fn enhanced_error_predicate(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("error? expects exactly 1 argument, got {}", args.len()),
None,
)));
}
let is_error = match &args[0] {
Value::ErrorObject(_) => true,
_ => false,
};
Ok(Value::boolean(is_error))
}
pub fn enhanced_error_object_message(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("error-object-message expects exactly 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::ErrorObject(error) => {
Ok(Value::string(error.message.clone()))
},
_ => Err(Box::new(DiagnosticError::runtime_error(
"SRFI-23 error: error-object-message requires an error object".to_string(),
None,
)))
}
}
pub fn enhanced_error_object_irritants(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("error-object-irritants expects exactly 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::ErrorObject(error) => {
Ok(Value::list(error.irritants.clone()))
},
_ => Err(Box::new(DiagnosticError::runtime_error(
"SRFI-23 error: error-object-irritants requires an error object".to_string(),
None,
)))
}
}
pub fn enhanced_raise_procedure(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("raise expects exactly 1 argument, got {}", args.len()),
None,
)));
}
let obj = &args[0];
let exception = match obj {
Value::ErrorObject(_) => {
ExceptionObject::new("error".to_string(), obj.clone(), false)
},
_ => {
ExceptionObject::new("exception".to_string(), obj.clone(), false)
}
};
Err(Box::new(DiagnosticError::exception(exception)))
}
pub fn create_enhanced_srfi23_bindings(env: &Arc<ThreadSafeEnvironment>) {
env.define("error".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "error".to_string(),
arity_min: 1,
arity_max: None, implementation: PrimitiveImpl::RustFn(enhanced_error_procedure),
effects: vec![Effect::Error],
})));
env.define("error?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "error?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(enhanced_error_predicate),
effects: vec![Effect::Pure],
})));
env.define("error-object?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "error-object?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(enhanced_error_predicate),
effects: vec![Effect::Pure],
})));
env.define("error-object-message".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "error-object-message".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(enhanced_error_object_message),
effects: vec![Effect::Pure],
})));
env.define("error-object-irritants".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "error-object-irritants".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(enhanced_error_object_irritants),
effects: vec![Effect::Pure],
})));
env.define("raise".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "raise".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(enhanced_raise_procedure),
effects: vec![Effect::Error],
})));
}
pub fn validate_srfi23_compliance() -> Result<()> {
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::Literal;
#[test]
fn test_srfi23_error_object_creation() {
let message = "Test error message".to_string();
let irritants = vec![
Value::integer(42),
Value::string("irritant"),
Value::symbol(crate::utils::intern_symbol("symbol")),
];
let error_obj = SRFI23ErrorObject::new(message.clone(), irritants.clone());
assert_eq!(error_obj.message, message);
assert_eq!(error_obj.irritants, irritants);
assert_eq!(error_obj.error_object.message, message);
assert_eq!(error_obj.error_object.irritants, irritants);
}
#[test]
fn test_srfi23_message_validation() {
let valid_msg = Value::string("Valid message");
let result = SRFI23ErrorObject::validate_message(&valid_msg);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Valid message");
let invalid_msg = Value::integer(42);
let result = SRFI23ErrorObject::validate_message(&invalid_msg);
assert!(result.is_err());
if let Err(boxed_err) = result {
if let DiagnosticError::RuntimeError { message, .. } = boxed_err.as_ref() {
assert!(message.contains("SRFI-23"));
assert!(message.contains("string"));
}
}
}
#[test]
fn test_enhanced_error_procedure() {
let args = vec![Value::string("Test error")];
let result = enhanced_error_procedure(&args);
assert!(result.is_err());
if let Err(boxed_err) = result {
if let DiagnosticError::Exception { exception, .. } = boxed_err.as_ref() {
assert_eq!(exception.exception_type, "error");
assert_eq!(exception.message, Some("Test error".to_string()));
assert!(exception.irritants.is_empty());
} else {
panic!("Expected exception error");
}
}
let args = vec![
Value::string("Error with irritants"),
Value::integer(1),
Value::string("irritant"),
];
let result = enhanced_error_procedure(&args);
assert!(result.is_err());
if let Err(boxed_err) = result {
if let DiagnosticError::Exception { exception, .. } = boxed_err.as_ref() {
assert_eq!(exception.message, Some("Error with irritants".to_string()));
assert_eq!(exception.irritants.len(), 2);
}
}
}
#[test]
fn test_enhanced_error_procedure_validation() {
let result = enhanced_error_procedure(&[]);
assert!(result.is_err());
if let Err(boxed_err) = result {
if let DiagnosticError::RuntimeError { message, .. } = boxed_err.as_ref() {
assert!(message.contains("SRFI-23"));
assert!(message.contains("at least one argument"));
}
}
let args = vec![Value::integer(42)];
let result = enhanced_error_procedure(&args);
assert!(result.is_err());
if let Err(boxed_err) = result {
if let DiagnosticError::RuntimeError { message, .. } = boxed_err.as_ref() {
assert!(message.contains("SRFI-23"));
assert!(message.contains("string"));
}
}
}
#[test]
fn test_enhanced_error_predicate() {
let error_obj = Value::ErrorObject(Arc::new(ErrorObject::new(
"test".to_string(),
vec![],
)));
let result = enhanced_error_predicate(&[error_obj]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::boolean(true));
let non_error = Value::integer(42);
let result = enhanced_error_predicate(&[non_error]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::boolean(false));
let result = enhanced_error_predicate(&[]);
assert!(result.is_err());
let result = enhanced_error_predicate(&[Value::integer(1), Value::integer(2)]);
assert!(result.is_err());
}
#[test]
fn test_enhanced_error_object_accessors() {
let error_obj = Value::ErrorObject(Arc::new(ErrorObject::new(
"test message".to_string(),
vec![Value::integer(1), Value::string("irritant")],
)));
let result = enhanced_error_object_message(&[error_obj.clone()]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::string("test message"));
let result = enhanced_error_object_irritants(&[error_obj]);
assert!(result.is_ok());
if let Ok(Value::Pair(first, rest)) = result {
assert_eq!(*first, Value::integer(1));
if let Value::Pair(second, _) = rest.as_ref() {
assert_eq!(**second, Value::string("irritant"));
}
}
}
#[test]
fn test_enhanced_raise_procedure() {
let error_obj = Value::ErrorObject(Arc::new(ErrorObject::new(
"test".to_string(),
vec![],
)));
let result = enhanced_raise_procedure(&[error_obj]);
assert!(result.is_err());
if let Err(boxed_err) = result {
if let DiagnosticError::Exception { exception, .. } = boxed_err.as_ref() {
assert_eq!(exception.exception_type, "error");
}
}
let general_obj = Value::string("general exception");
let result = enhanced_raise_procedure(&[general_obj]);
assert!(result.is_err());
if let Err(boxed_err) = result {
if let DiagnosticError::Exception { exception, .. } = boxed_err.as_ref() {
assert_eq!(exception.exception_type, "exception");
}
}
}
}