use std::sync::Arc;
use crate::{
metadata::{
token::Token,
typesystem::{CilPrimitive, CilTypeReference},
},
Result,
};
pub struct Constant {
pub rid: u32,
pub token: Token,
pub offset: usize,
pub c_type: u8,
pub parent: CilTypeReference,
pub value: Arc<CilPrimitive>,
}
impl Constant {
pub fn apply(&self) -> Result<()> {
match &self.parent {
CilTypeReference::Field(field) => {
if !field.signature.base.accepts_constant(&self.value) {
return Err(malformed_error!(
"Constant type {:?} is not compatible with field type: {:?} (token: {})",
self.value.kind,
field.signature.base,
self.token.value()
));
}
field
.default
.set(self.value.as_ref().clone())
.map_err(|_| malformed_error!("Default value already set for field"))
}
CilTypeReference::Param(param) => {
if let Some(param_type) = param.base.get() {
if let Some(param_type_strong) = param_type.upgrade() {
if !param_type_strong.accepts_constant(&self.value) {
return Err(malformed_error!(
"Constant type {:?} is not compatible with parameter type {} (token: {})",
self.value.kind,
param_type_strong.fullname(),
self.token.value()
));
}
}
}
param
.default
.set(self.value.as_ref().clone())
.map_err(|_| malformed_error!("Default value already set for param"))
}
CilTypeReference::Property(property) => {
if !property.signature.base.accepts_constant(&self.value) {
return Err(malformed_error!(
"Constant type {:?} is not compatible with property type: {:?} (token: {})",
self.value.kind,
property.signature.base,
self.token.value()
));
}
property
.default
.set(self.value.as_ref().clone())
.map_err(|_| malformed_error!("Default value already set for property"))
}
_ => Err(malformed_error!(
"Invalid parent type for constant - {}",
self.token.value()
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
metadata::{
signatures::TypeSignature,
typesystem::{CilPrimitive, CilPrimitiveKind, ELEMENT_TYPE},
},
test::{
builders::ConstantBuilder,
factories::table::constant::{
create_boolean_field, create_i4_field, create_object_field, create_r4_field,
create_string_field, create_test_param, create_test_property,
},
},
};
#[test]
fn test_apply_field_constant_success() {
let field = create_i4_field("test_field");
let constant = ConstantBuilder::field_i4_constant(1, field.clone(), 42).build();
let result = constant.apply();
assert!(
result.is_ok(),
"Expected successful application of constant to field"
);
let default_value = field.default.get().unwrap();
assert_eq!(default_value.kind, CilPrimitiveKind::I4);
assert_eq!(default_value.as_i32(), Some(42));
}
#[test]
fn test_apply_field_string_constant_success() {
let field = create_string_field("test_field");
let constant =
ConstantBuilder::field_string_constant(1, field.clone(), "test_value").build();
let result = constant.apply();
assert!(
result.is_ok(),
"Expected successful application of string constant to field"
);
let default_value = field.default.get().unwrap();
assert_eq!(default_value.kind, CilPrimitiveKind::String);
assert_eq!(default_value.as_string(), Some("test_value".to_string()));
}
#[test]
fn test_apply_field_constant_already_set() {
let field = create_i4_field("test_field");
let _ = field.default.set(CilPrimitive::i4(100));
let constant = ConstantBuilder::field_i4_constant(1, field, 42).build();
let result = constant.apply();
assert!(
result.is_err(),
"Expected error when default value already set"
);
let error_message = result.unwrap_err().to_string();
assert!(error_message.contains("Default value already set for field"));
}
#[test]
fn test_apply_property_constant_success() {
let property = create_test_property("test_property", TypeSignature::I4);
let constant = ConstantBuilder::property_i4_constant(1, property.clone(), 123).build();
let result = constant.apply();
assert!(
result.is_ok(),
"Expected successful application of constant to property"
);
let default_value = property.default.get().unwrap();
assert_eq!(default_value.kind, CilPrimitiveKind::I4);
assert_eq!(default_value.as_i32(), Some(123));
}
#[test]
fn test_apply_property_constant_already_set() {
let property = create_test_property("test_property", TypeSignature::I4);
let _ = property.default.set(CilPrimitive::i4(200));
let constant = ConstantBuilder::property_i4_constant(1, property, 123).build();
let result = constant.apply();
assert!(
result.is_err(),
"Expected error when default value already set"
);
let error_message = result.unwrap_err().to_string();
assert!(error_message.contains("Default value already set for property"));
}
#[test]
fn test_apply_param_constant_success() {
let param = create_test_param("test_param");
let constant = ConstantBuilder::param_i4_constant(1, param.clone(), 456).build();
let result = constant.apply();
assert!(
result.is_ok(),
"Expected successful application of constant to parameter"
);
let default_value = param.default.get().unwrap();
assert_eq!(default_value.kind, CilPrimitiveKind::I4);
assert_eq!(default_value.as_i32(), Some(456));
}
#[test]
fn test_apply_param_constant_already_set() {
let param = create_test_param("test_param");
let _ = param.default.set(CilPrimitive::i4(300));
let constant = ConstantBuilder::param_i4_constant(1, param, 456).build();
let result = constant.apply();
assert!(
result.is_err(),
"Expected error when default value already set"
);
let error_message = result.unwrap_err().to_string();
assert!(error_message.contains("Default value already set for param"));
}
#[test]
fn test_apply_invalid_parent() {
let constant = ConstantBuilder::invalid_parent_constant(1, 42).build();
let result = constant.apply();
assert!(result.is_err(), "Expected error for invalid parent type");
let error_message = result.unwrap_err().to_string();
assert!(error_message.contains("Invalid parent type for constant"));
}
#[test]
fn test_multiple_constant_applications() {
let field1 = create_i4_field("field1");
let field2 = create_i4_field("field2");
let constant1 = ConstantBuilder::field_i4_constant(1, field1.clone(), 100).build();
let constant2 = ConstantBuilder::field_i4_constant(2, field2.clone(), 200).build();
assert!(constant1.apply().is_ok());
assert!(constant2.apply().is_ok());
assert_eq!(field1.default.get().unwrap().as_i32(), Some(100));
assert_eq!(field2.default.get().unwrap().as_i32(), Some(200));
}
#[test]
fn test_edge_case_values() {
let field_max = create_i4_field("field_max");
let constant_max =
ConstantBuilder::field_i4_constant(1, field_max.clone(), i32::MAX).build();
assert!(constant_max.apply().is_ok());
assert_eq!(field_max.default.get().unwrap().as_i32(), Some(i32::MAX));
let field_min = create_i4_field("field_min");
let constant_min =
ConstantBuilder::field_i4_constant(2, field_min.clone(), i32::MIN).build();
assert!(constant_min.apply().is_ok());
assert_eq!(field_min.default.get().unwrap().as_i32(), Some(i32::MIN));
let field_empty = create_string_field("field_empty");
let constant_empty =
ConstantBuilder::field_string_constant(3, field_empty.clone(), "").build();
assert!(constant_empty.apply().is_ok());
assert_eq!(
field_empty.default.get().unwrap().as_string(),
Some(String::new())
);
}
#[test]
fn test_apply_different_primitive_types() {
let field_bool = create_boolean_field("field_bool");
let constant_bool = ConstantBuilder::new(
1,
ELEMENT_TYPE::BOOLEAN,
CilTypeReference::Field(field_bool.clone()),
Arc::new(CilPrimitive::boolean(true)),
)
.build();
let result = constant_bool.apply();
assert!(result.is_ok());
let default_value = field_bool.default.get().unwrap();
assert_eq!(default_value.kind, CilPrimitiveKind::Boolean);
if let crate::metadata::typesystem::CilPrimitiveData::Boolean(value) = &default_value.data {
assert!(*value);
} else {
panic!("Expected Boolean primitive data");
}
let field_r4 = create_r4_field("field_r4");
let constant_r4 = ConstantBuilder::new(
2,
ELEMENT_TYPE::R4,
CilTypeReference::Field(field_r4.clone()),
Arc::new(CilPrimitive::r4(std::f32::consts::PI)),
)
.build();
let result = constant_r4.apply();
assert!(result.is_ok());
let default_value = field_r4.default.get().unwrap();
assert_eq!(default_value.kind, CilPrimitiveKind::R4);
if let crate::metadata::typesystem::CilPrimitiveData::R4(value) = &default_value.data {
assert!((value - std::f32::consts::PI).abs() < f32::EPSILON);
} else {
panic!("Expected R4 primitive data");
}
}
#[test]
fn test_apply_null_constant() {
let field = create_object_field("field_object");
let constant = ConstantBuilder::new(
1,
ELEMENT_TYPE::CLASS,
CilTypeReference::Field(field.clone()),
Arc::new(CilPrimitive::null()),
)
.build();
let result = constant.apply();
assert!(
result.is_ok(),
"Null constants should be applicable to reference types"
);
let default_value = field.default.get().unwrap();
assert_eq!(default_value.kind, CilPrimitiveKind::Null);
}
}