use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{ImportScopeRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone)]
pub struct ImportScopeBuilder {
parent: Option<u32>,
imports: Option<Vec<u8>>,
}
impl ImportScopeBuilder {
#[must_use]
pub fn new() -> Self {
Self {
parent: None,
imports: None,
}
}
#[must_use]
pub fn parent(mut self, parent: u32) -> Self {
self.parent = Some(parent);
self
}
#[must_use]
pub fn imports(mut self, imports: &[u8]) -> Self {
self.imports = Some(imports.to_vec());
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let parent = self.parent.ok_or_else(|| {
Error::ModificationInvalid(
"Parent scope index is required for ImportScope (use 0 for root scope)".to_string(),
)
})?;
let imports = self.imports.ok_or_else(|| {
Error::ModificationInvalid("Import blob data is required for ImportScope".to_string())
})?;
let imports_index = if imports.is_empty() {
0
} else {
assembly.blob_add(&imports)?.placeholder()
};
let import_scope = ImportScopeRaw {
rid: 0,
token: Token::new(0),
offset: 0,
parent,
imports: imports_index,
};
assembly.table_row_add(
TableId::ImportScope,
TableDataOwned::ImportScope(import_scope),
)
}
}
impl Default for ImportScopeBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::ChangeRefKind, test::factories::table::assemblyref::get_test_assembly,
};
#[test]
fn test_importscope_builder_new() {
let builder = ImportScopeBuilder::new();
assert!(builder.parent.is_none());
assert!(builder.imports.is_none());
}
#[test]
fn test_importscope_builder_default() {
let builder = ImportScopeBuilder::default();
assert!(builder.parent.is_none());
assert!(builder.imports.is_none());
}
#[test]
fn test_importscope_builder_root_scope() -> Result<()> {
let mut assembly = get_test_assembly()?;
let imports_data = vec![0x01, 0x10, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D]; let ref_ = ImportScopeBuilder::new()
.parent(0) .imports(&imports_data)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::ImportScope));
Ok(())
}
#[test]
fn test_importscope_builder_child_scope() -> Result<()> {
let mut assembly = get_test_assembly()?;
let imports_data = vec![0x01, 0x02, 0x03];
let ref_ = ImportScopeBuilder::new()
.parent(1) .imports(&imports_data)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::ImportScope));
Ok(())
}
#[test]
fn test_importscope_builder_empty_imports() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref_ = ImportScopeBuilder::new()
.parent(0)
.imports(&[]) .build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::ImportScope));
Ok(())
}
#[test]
fn test_importscope_builder_missing_parent() -> Result<()> {
let mut assembly = get_test_assembly()?;
let imports_data = vec![0x01, 0x02];
let result = ImportScopeBuilder::new()
.imports(&imports_data)
.build(&mut assembly);
assert!(result.is_err());
match result.unwrap_err() {
Error::ModificationInvalid(details) => {
assert!(details.contains("Parent scope index is required"));
}
_ => panic!("Expected ModificationInvalid error"),
}
Ok(())
}
#[test]
fn test_importscope_builder_missing_imports() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = ImportScopeBuilder::new().parent(0).build(&mut assembly);
assert!(result.is_err());
match result.unwrap_err() {
Error::ModificationInvalid(details) => {
assert!(details.contains("Import blob data is required"));
}
_ => panic!("Expected ModificationInvalid error"),
}
Ok(())
}
#[test]
fn test_importscope_builder_clone() {
let imports_data = vec![0x01, 0x02, 0x03];
let builder = ImportScopeBuilder::new().parent(0).imports(&imports_data);
let cloned = builder.clone();
assert_eq!(builder.parent, cloned.parent);
assert_eq!(builder.imports, cloned.imports);
}
#[test]
fn test_importscope_builder_debug() {
let imports_data = vec![0x01, 0x02, 0x03];
let builder = ImportScopeBuilder::new().parent(1).imports(&imports_data);
let debug_str = format!("{builder:?}");
assert!(debug_str.contains("ImportScopeBuilder"));
assert!(debug_str.contains("parent"));
assert!(debug_str.contains("imports"));
}
#[test]
fn test_importscope_builder_fluent_interface() -> Result<()> {
let mut assembly = get_test_assembly()?;
let imports_data = vec![0x01, 0x05, 0x54, 0x65, 0x73, 0x74, 0x73];
let ref_ = ImportScopeBuilder::new()
.parent(0)
.imports(&imports_data)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::ImportScope));
Ok(())
}
#[test]
fn test_importscope_builder_multiple_builds() -> Result<()> {
let mut assembly = get_test_assembly()?;
let imports1 = vec![0x01, 0x02];
let imports2 = vec![0x03, 0x04];
let ref1 = ImportScopeBuilder::new()
.parent(0)
.imports(&imports1)
.build(&mut assembly)
.expect("Should build first scope");
let ref2 = ImportScopeBuilder::new()
.parent(1) .imports(&imports2)
.build(&mut assembly)
.expect("Should build second scope");
assert_eq!(ref1.kind(), ChangeRefKind::TableRow(TableId::ImportScope));
assert_eq!(ref2.kind(), ChangeRefKind::TableRow(TableId::ImportScope));
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref2));
Ok(())
}
}