use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{LocalConstantRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone)]
pub struct LocalConstantBuilder {
name: Option<String>,
signature: Option<Vec<u8>>,
}
impl LocalConstantBuilder {
#[must_use]
pub fn new() -> Self {
Self {
name: None,
signature: None,
}
}
#[must_use]
pub fn name<T: Into<String>>(mut self, name: T) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn signature(mut self, signature: &[u8]) -> Self {
self.signature = Some(signature.to_vec());
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let name = self.name.ok_or_else(|| {
Error::ModificationInvalid(
"Constant name is required for LocalConstant (use empty string for anonymous)"
.to_string(),
)
})?;
let signature = self.signature.ok_or_else(|| {
Error::ModificationInvalid(
"Constant signature is required for LocalConstant".to_string(),
)
})?;
let name_index = if name.is_empty() {
0
} else {
assembly.string_add(&name)?.placeholder()
};
let signature_index = if signature.is_empty() {
0
} else {
assembly.blob_add(&signature)?.placeholder()
};
let local_constant = LocalConstantRaw {
rid: 0,
token: Token::new(0),
offset: 0,
name: name_index,
signature: signature_index,
};
assembly.table_row_add(
TableId::LocalConstant,
TableDataOwned::LocalConstant(local_constant),
)
}
}
impl Default for LocalConstantBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::ChangeRefKind, metadata::tables::TableId,
test::factories::table::assemblyref::get_test_assembly,
};
#[test]
fn test_localconstant_builder_new() {
let builder = LocalConstantBuilder::new();
assert!(builder.name.is_none());
assert!(builder.signature.is_none());
}
#[test]
fn test_localconstant_builder_default() {
let builder = LocalConstantBuilder::default();
assert!(builder.name.is_none());
assert!(builder.signature.is_none());
}
#[test]
fn test_localconstant_builder_basic() -> Result<()> {
let mut assembly = get_test_assembly()?;
let signature_bytes = vec![0x08]; let ref_ = LocalConstantBuilder::new()
.name("testConstant")
.signature(&signature_bytes)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::LocalConstant));
Ok(())
}
#[test]
fn test_localconstant_builder_anonymous_constant() -> Result<()> {
let mut assembly = get_test_assembly()?;
let signature_bytes = vec![0x0E]; let ref_ = LocalConstantBuilder::new()
.name("") .signature(&signature_bytes)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::LocalConstant));
Ok(())
}
#[test]
fn test_localconstant_builder_missing_name() -> Result<()> {
let mut assembly = get_test_assembly()?;
let signature_bytes = vec![0x08]; let result = LocalConstantBuilder::new()
.signature(&signature_bytes)
.build(&mut assembly);
assert!(result.is_err());
match result.unwrap_err() {
Error::ModificationInvalid(details) => {
assert!(details.contains("Constant name is required"));
}
_ => panic!("Expected ModificationInvalid error"),
}
Ok(())
}
#[test]
fn test_localconstant_builder_missing_signature() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = LocalConstantBuilder::new()
.name("testConstant")
.build(&mut assembly);
assert!(result.is_err());
match result.unwrap_err() {
Error::ModificationInvalid(details) => {
assert!(details.contains("Constant signature is required"));
}
_ => panic!("Expected ModificationInvalid error"),
}
Ok(())
}
#[test]
fn test_localconstant_builder_clone() {
let signature_bytes = vec![0x08]; let builder = LocalConstantBuilder::new()
.name("testConstant")
.signature(&signature_bytes);
let cloned = builder.clone();
assert_eq!(builder.name, cloned.name);
assert_eq!(builder.signature, cloned.signature);
}
#[test]
fn test_localconstant_builder_debug() {
let signature_bytes = vec![0x08]; let builder = LocalConstantBuilder::new()
.name("testConstant")
.signature(&signature_bytes);
let debug_str = format!("{builder:?}");
assert!(debug_str.contains("LocalConstantBuilder"));
assert!(debug_str.contains("name"));
assert!(debug_str.contains("signature"));
}
#[test]
fn test_localconstant_builder_fluent_interface() -> Result<()> {
let mut assembly = get_test_assembly()?;
let signature_bytes = vec![0x02];
let ref_ = LocalConstantBuilder::new()
.name("chainedConstant")
.signature(&signature_bytes)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::LocalConstant));
Ok(())
}
#[test]
fn test_localconstant_builder_multiple_builds() -> Result<()> {
let mut assembly = get_test_assembly()?;
let signature1 = vec![0x08]; let signature2 = vec![0x0E];
let ref1 = LocalConstantBuilder::new()
.name("constant1")
.signature(&signature1)
.build(&mut assembly)
.expect("Should build first constant");
let ref2 = LocalConstantBuilder::new()
.name("constant2")
.signature(&signature2)
.build(&mut assembly)
.expect("Should build second constant");
assert_eq!(ref1.kind(), ChangeRefKind::TableRow(TableId::LocalConstant));
assert_eq!(ref2.kind(), ChangeRefKind::TableRow(TableId::LocalConstant));
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref2));
Ok(())
}
}