use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{NestedClassRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone)]
pub struct NestedClassBuilder {
nested_class: Option<u32>,
enclosing_class: Option<u32>,
}
impl Default for NestedClassBuilder {
fn default() -> Self {
Self::new()
}
}
impl NestedClassBuilder {
#[must_use]
pub fn new() -> Self {
Self {
nested_class: None,
enclosing_class: None,
}
}
#[must_use]
pub fn nested_class(mut self, nested_class_row: u32) -> Self {
self.nested_class = Some(nested_class_row);
self
}
#[must_use]
pub fn enclosing_class(mut self, enclosing_class_row: u32) -> Self {
self.enclosing_class = Some(enclosing_class_row);
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let nested_class_row = self.nested_class.ok_or_else(|| {
Error::ModificationInvalid("Nested class row is required for NestedClass".to_string())
})?;
let enclosing_class_row = self.enclosing_class.ok_or_else(|| {
Error::ModificationInvalid(
"Enclosing class row is required for NestedClass".to_string(),
)
})?;
if nested_class_row == 0 {
return Err(Error::ModificationInvalid(
"Nested class row cannot be 0".to_string(),
));
}
if enclosing_class_row == 0 {
return Err(Error::ModificationInvalid(
"Enclosing class row cannot be 0".to_string(),
));
}
if nested_class_row == enclosing_class_row {
return Err(Error::ModificationInvalid(
"A type cannot be nested within itself".to_string(),
));
}
let nested_class = NestedClassRaw {
rid: 0,
token: Token::new(0),
offset: 0,
nested_class: nested_class_row,
enclosing_class: enclosing_class_row,
};
assembly.table_row_add(
TableId::NestedClass,
TableDataOwned::NestedClass(nested_class),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::ChangeRefKind,
metadata::tables::{TableId, TypeAttributes},
test::factories::table::assemblyref::get_test_assembly,
};
#[test]
fn test_nested_class_builder_basic() -> Result<()> {
let mut assembly = get_test_assembly()?;
let outer_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("OuterClass")
.public_class()
.build(&mut assembly)?;
let inner_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("InnerClass")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let nested_ref = NestedClassBuilder::new()
.nested_class(inner_ref.placeholder())
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly)?;
assert_eq!(
nested_ref.kind(),
ChangeRefKind::TableRow(TableId::NestedClass)
);
Ok(())
}
#[test]
fn test_nested_class_builder_default() -> Result<()> {
let builder = NestedClassBuilder::default();
assert!(builder.nested_class.is_none());
assert!(builder.enclosing_class.is_none());
Ok(())
}
#[test]
fn test_nested_class_builder_missing_nested_class() -> Result<()> {
let mut assembly = get_test_assembly()?;
let outer_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("OuterClass")
.public_class()
.build(&mut assembly)?;
let result = NestedClassBuilder::new()
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Nested class row is required"));
Ok(())
}
#[test]
fn test_nested_class_builder_missing_enclosing_class() -> Result<()> {
let mut assembly = get_test_assembly()?;
let inner_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("InnerClass")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let result = NestedClassBuilder::new()
.nested_class(inner_ref.placeholder())
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Enclosing class row is required"));
Ok(())
}
#[test]
fn test_nested_class_builder_zero_nested_row() -> Result<()> {
let mut assembly = get_test_assembly()?;
let outer_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("OuterClass")
.public_class()
.build(&mut assembly)?;
let result = NestedClassBuilder::new()
.nested_class(0)
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Nested class row cannot be 0"));
Ok(())
}
#[test]
fn test_nested_class_builder_zero_enclosing_row() -> Result<()> {
let mut assembly = get_test_assembly()?;
let inner_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("InnerClass")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let result = NestedClassBuilder::new()
.nested_class(inner_ref.placeholder())
.enclosing_class(0)
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Enclosing class row cannot be 0"));
Ok(())
}
#[test]
fn test_nested_class_builder_self_nesting() -> Result<()> {
let mut assembly = get_test_assembly()?;
let type_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("SelfNestingClass")
.public_class()
.build(&mut assembly)?;
let type_placeholder = type_ref.placeholder();
let result = NestedClassBuilder::new()
.nested_class(type_placeholder)
.enclosing_class(type_placeholder)
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("A type cannot be nested within itself"));
Ok(())
}
#[test]
fn test_nested_class_builder_zero_row_nested() -> Result<()> {
let mut assembly = get_test_assembly()?;
let outer_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("OuterClass")
.public_class()
.build(&mut assembly)?;
let result = NestedClassBuilder::new()
.nested_class(0)
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Nested class row cannot be 0"));
Ok(())
}
#[test]
fn test_nested_class_builder_zero_row_enclosing() -> Result<()> {
let mut assembly = get_test_assembly()?;
let inner_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("InnerClass")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let result = NestedClassBuilder::new()
.nested_class(inner_ref.placeholder())
.enclosing_class(0)
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Enclosing class row cannot be 0"));
Ok(())
}
#[test]
fn test_nested_class_builder_multiple_relationships() -> Result<()> {
let mut assembly = get_test_assembly()?;
let outer_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("OuterClass")
.public_class()
.build(&mut assembly)?;
let inner1_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("InnerClass1")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let inner2_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("InnerClass2")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let nesting1_ref = NestedClassBuilder::new()
.nested_class(inner1_ref.placeholder())
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly)?;
let nesting2_ref = NestedClassBuilder::new()
.nested_class(inner2_ref.placeholder())
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly)?;
assert!(!std::sync::Arc::ptr_eq(&nesting1_ref, &nesting2_ref));
assert_eq!(
nesting1_ref.kind(),
ChangeRefKind::TableRow(TableId::NestedClass)
);
assert_eq!(
nesting2_ref.kind(),
ChangeRefKind::TableRow(TableId::NestedClass)
);
Ok(())
}
#[test]
fn test_nested_class_builder_deep_nesting() -> Result<()> {
let mut assembly = get_test_assembly()?;
let outer_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("OuterClass")
.public_class()
.build(&mut assembly)?;
let middle_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("MiddleClass")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let inner_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("InnerClass")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let nesting1_ref = NestedClassBuilder::new()
.nested_class(middle_ref.placeholder())
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly)?;
let nesting2_ref = NestedClassBuilder::new()
.nested_class(inner_ref.placeholder())
.enclosing_class(middle_ref.placeholder())
.build(&mut assembly)?;
assert_eq!(
nesting1_ref.kind(),
ChangeRefKind::TableRow(TableId::NestedClass)
);
assert_eq!(
nesting2_ref.kind(),
ChangeRefKind::TableRow(TableId::NestedClass)
);
Ok(())
}
#[test]
fn test_nested_class_builder_fluent_api() -> Result<()> {
let mut assembly = get_test_assembly()?;
let outer_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("FluentOuter")
.public_class()
.build(&mut assembly)?;
let inner_ref = crate::metadata::tables::TypeDefBuilder::new()
.name("FluentInner")
.flags(TypeAttributes::NESTED_PUBLIC | TypeAttributes::CLASS)
.build(&mut assembly)?;
let nested_ref = NestedClassBuilder::new()
.nested_class(inner_ref.placeholder())
.enclosing_class(outer_ref.placeholder())
.build(&mut assembly)?;
assert_eq!(
nested_ref.kind(),
ChangeRefKind::TableRow(TableId::NestedClass)
);
Ok(())
}
#[test]
fn test_nested_class_builder_clone() {
let nested_row = 1u32;
let enclosing_row = 2u32;
let builder1 = NestedClassBuilder::new()
.nested_class(nested_row)
.enclosing_class(enclosing_row);
let builder2 = builder1.clone();
assert_eq!(builder1.nested_class, builder2.nested_class);
assert_eq!(builder1.enclosing_class, builder2.enclosing_class);
}
#[test]
fn test_nested_class_builder_debug() {
let nested_row = 1u32;
let enclosing_row = 2u32;
let builder = NestedClassBuilder::new()
.nested_class(nested_row)
.enclosing_class(enclosing_row);
let debug_str = format!("{builder:?}");
assert!(debug_str.contains("NestedClassBuilder"));
}
}