use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{ModuleRefRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone, Default)]
pub struct ModuleRefBuilder {
name: Option<String>,
}
impl ModuleRefBuilder {
#[must_use]
pub fn new() -> Self {
Self { name: None }
}
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let name = self.name.ok_or_else(|| {
Error::ModificationInvalid("Module name is required for ModuleRef".to_string())
})?;
if name.is_empty() {
return Err(Error::ModificationInvalid(
"Module name cannot be empty for ModuleRef".to_string(),
));
}
let name_index = assembly.string_get_or_add(&name)?.placeholder();
let module_ref = ModuleRefRaw {
rid: 0,
token: Token::new(0),
offset: 0,
name: name_index,
};
assembly.table_row_add(TableId::ModuleRef, TableDataOwned::ModuleRef(module_ref))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::ChangeRefKind, test::factories::table::assemblyref::get_test_assembly,
};
#[test]
fn test_moduleref_builder_basic() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref_ = ModuleRefBuilder::new()
.name("System.Core.dll")
.build(&mut assembly)?;
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::ModuleRef));
Ok(())
}
#[test]
fn test_moduleref_builder_default() -> Result<()> {
let builder = ModuleRefBuilder::default();
assert!(builder.name.is_none());
Ok(())
}
#[test]
fn test_moduleref_builder_missing_name() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = ModuleRefBuilder::new().build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Module name is required"));
Ok(())
}
#[test]
fn test_moduleref_builder_empty_name() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = ModuleRefBuilder::new().name("").build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Module name cannot be empty"));
Ok(())
}
#[test]
fn test_moduleref_builder_multiple_modules() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref1 = ModuleRefBuilder::new()
.name("Module1.dll")
.build(&mut assembly)?;
let ref2 = ModuleRefBuilder::new()
.name("Module2.dll")
.build(&mut assembly)?;
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref2));
assert_eq!(ref1.kind(), ChangeRefKind::TableRow(TableId::ModuleRef));
assert_eq!(ref2.kind(), ChangeRefKind::TableRow(TableId::ModuleRef));
Ok(())
}
#[test]
fn test_moduleref_builder_fluent_api() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref_ = ModuleRefBuilder::new()
.name("FluentModule.dll")
.build(&mut assembly)?;
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::ModuleRef));
Ok(())
}
#[test]
fn test_moduleref_builder_various_names() -> Result<()> {
let mut assembly = get_test_assembly()?;
let test_names = [
"System.dll",
"Microsoft.Extensions.Logging.dll",
"MyCustomModule",
"Module.With.Dots.dll",
"VeryLongModuleNameThatExceedsTypicalLengths.dll",
];
for name in test_names.iter() {
let ref_ = ModuleRefBuilder::new().name(*name).build(&mut assembly)?;
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::ModuleRef));
}
Ok(())
}
#[test]
fn test_moduleref_builder_string_reuse() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref1 = ModuleRefBuilder::new()
.name("SharedModule.dll")
.build(&mut assembly)?;
let ref2 = ModuleRefBuilder::new()
.name("SharedModule.dll")
.build(&mut assembly)?;
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref2));
Ok(())
}
#[test]
fn test_moduleref_builder_clone() {
let builder1 = ModuleRefBuilder::new().name("Module.dll");
let builder2 = builder1.clone();
assert_eq!(builder1.name, builder2.name);
}
#[test]
fn test_moduleref_builder_debug() {
let builder = ModuleRefBuilder::new().name("DebugModule.dll");
let debug_str = format!("{builder:?}");
assert!(debug_str.contains("ModuleRefBuilder"));
assert!(debug_str.contains("DebugModule.dll"));
}
}