use std::sync::{Arc, OnceLock};
use crate::{
metadata::{
signatures::{SignatureProperty, TypeSignature},
tables::{Property, PropertyRc},
token::Token,
typesystem::CilPrimitive,
},
prelude::SignatureParameter,
};
#[derive(Debug, Clone)]
pub enum PropertyConstant {
Bool(bool),
I1(i8),
U1(u8),
I2(i16),
U2(u16),
I4(i32),
U4(u32),
I8(i64),
U8(u64),
R4(f32),
R8(f64),
String(String),
Null,
}
impl PropertyConstant {
pub fn to_primitive(&self) -> CilPrimitive {
match self {
PropertyConstant::Bool(val) => CilPrimitive::boolean(*val),
PropertyConstant::I1(val) => CilPrimitive::i1(*val),
PropertyConstant::U1(val) => CilPrimitive::u1(*val),
PropertyConstant::I2(val) => CilPrimitive::i2(*val),
PropertyConstant::U2(val) => CilPrimitive::u2(*val),
PropertyConstant::I4(val) => CilPrimitive::i4(*val),
PropertyConstant::U4(val) => CilPrimitive::u4(*val),
PropertyConstant::I8(val) => CilPrimitive::i8(*val),
PropertyConstant::U8(val) => CilPrimitive::u8(*val),
PropertyConstant::R4(val) => CilPrimitive::r4(*val),
PropertyConstant::R8(val) => CilPrimitive::r8(*val),
PropertyConstant::String(val) => CilPrimitive::string(val),
PropertyConstant::Null => CilPrimitive::null(),
}
}
}
pub struct PropertyBuilder {
rid: u32,
name: String,
signature: SignatureProperty,
flags: u32,
default_value: Option<PropertyConstant>,
}
impl PropertyBuilder {
pub fn new(name: &str, return_type: TypeSignature) -> Self {
Self {
rid: 1,
name: name.to_string(),
signature: SignatureProperty {
has_this: false,
modifiers: Vec::new(),
params: Vec::new(),
base: return_type,
},
flags: 0,
default_value: None,
}
}
pub fn with_rid(mut self, rid: u32) -> Self {
self.rid = rid;
self
}
pub fn with_flags(mut self, flags: u32) -> Self {
self.flags = flags;
self
}
pub fn with_params(mut self, params: Vec<SignatureParameter>) -> Self {
self.signature.params = params;
self
}
pub fn with_default(mut self, default: PropertyConstant) -> Self {
self.default_value = Some(default);
self
}
pub fn simple_property(name: &str, property_type: TypeSignature) -> Self {
Self::new(name, property_type)
}
pub fn property_with_default(
name: &str,
property_type: TypeSignature,
default: PropertyConstant,
) -> Self {
Self::new(name, property_type).with_default(default)
}
pub fn build(self) -> PropertyRc {
let token = Token::new(0x17000000 + self.rid);
let property = Property {
token,
flags: self.flags,
name: self.name,
signature: self.signature,
default: OnceLock::new(),
fn_setter: OnceLock::new(),
fn_getter: OnceLock::new(),
fn_other: OnceLock::new(),
custom_attributes: Arc::new(boxcar::Vec::new()),
};
let property_rc = Arc::new(property);
if let Some(default) = self.default_value {
let _ = property_rc.default.set(default.to_primitive());
}
property_rc
}
}
pub fn property_accepting_constants(name: &str) -> PropertyRc {
PropertyBuilder::simple_property(name, TypeSignature::I4).build()
}
pub fn property_rejecting_constants(name: &str) -> PropertyRc {
PropertyBuilder::simple_property(name, TypeSignature::Object).build()
}
pub fn property_with_default_set(name: &str, default: PropertyConstant) -> PropertyRc {
PropertyBuilder::property_with_default(name, TypeSignature::I4, default).build()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_property_builder_basic() {
let property = PropertyBuilder::new("TestProperty", TypeSignature::I4)
.with_rid(42)
.build();
assert_eq!(property.name, "TestProperty");
assert_eq!(property.signature.base, TypeSignature::I4);
assert_eq!(property.token.value(), 0x1700002A);
}
#[test]
fn test_property_with_default() {
let property = PropertyBuilder::property_with_default(
"DefaultProp",
TypeSignature::I4,
PropertyConstant::I4(123),
)
.build();
assert!(property.default.get().is_some());
let default = property.default.get().unwrap();
assert_eq!(
default.data,
crate::metadata::typesystem::CilPrimitiveData::I4(123)
);
}
#[test]
fn test_helper_functions() {
let accepting = property_accepting_constants("AcceptsProp");
assert_eq!(accepting.signature.base, TypeSignature::I4);
let rejecting = property_rejecting_constants("RejectsProp");
assert_eq!(rejecting.signature.base, TypeSignature::Object);
let with_default = property_with_default_set("DefaultProp", PropertyConstant::I4(456));
assert!(with_default.default.get().is_some());
}
}