use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{CodedIndex, CodedIndexType, InterfaceImplRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
pub struct InterfaceImplBuilder {
class: Option<u32>,
interface: Option<CodedIndex>,
}
impl Default for InterfaceImplBuilder {
fn default() -> Self {
Self::new()
}
}
impl InterfaceImplBuilder {
#[must_use]
pub fn new() -> Self {
Self {
class: None,
interface: None,
}
}
#[must_use]
pub fn class(mut self, class: u32) -> Self {
self.class = Some(class);
self
}
#[must_use]
pub fn interface(mut self, interface: CodedIndex) -> Self {
self.interface = Some(interface);
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let class = self.class.ok_or_else(|| {
Error::ModificationInvalid("InterfaceImpl class is required".to_string())
})?;
let interface = self.interface.ok_or_else(|| {
Error::ModificationInvalid("InterfaceImpl interface is required".to_string())
})?;
if class == 0 {
return Err(Error::ModificationInvalid(
"InterfaceImpl class RID cannot be 0".to_string(),
));
}
let valid_interface_tables = CodedIndexType::TypeDefOrRef.tables();
if !valid_interface_tables.contains(&interface.tag) {
return Err(Error::ModificationInvalid(format!(
"Interface must be a TypeDefOrRef coded index (TypeDef/TypeRef/TypeSpec), got {:?}",
interface.tag
)));
}
let rid = assembly.next_rid(TableId::InterfaceImpl)?;
let token_value = ((TableId::InterfaceImpl as u32) << 24) | rid;
let token = Token::new(token_value);
let interface_impl_raw = InterfaceImplRaw {
rid,
token,
offset: 0, class,
interface,
};
assembly.table_row_add(
TableId::InterfaceImpl,
TableDataOwned::InterfaceImpl(interface_impl_raw),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::ChangeRefKind, test::factories::table::assemblyref::get_test_assembly,
};
#[test]
fn test_interface_impl_builder_basic() {
if let Ok(mut assembly) = get_test_assembly() {
let implementing_class = 1; let target_interface =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let impl_ref = InterfaceImplBuilder::new()
.class(implementing_class)
.interface(target_interface)
.build(&mut assembly)
.unwrap();
assert_eq!(
impl_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
}
}
#[test]
fn test_interface_impl_builder_interface_extension() {
if let Ok(mut assembly) = get_test_assembly() {
let derived_interface = 2; let base_interface = CodedIndex::new(TableId::TypeDef, 1, CodedIndexType::TypeDefOrRef);
let impl_ref = InterfaceImplBuilder::new()
.class(derived_interface)
.interface(base_interface)
.build(&mut assembly)
.unwrap();
assert_eq!(
impl_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
}
}
#[test]
fn test_interface_impl_builder_generic_interface() {
if let Ok(mut assembly) = get_test_assembly() {
let implementing_class = 3; let generic_interface =
CodedIndex::new(TableId::TypeSpec, 1, CodedIndexType::TypeDefOrRef);
let impl_ref = InterfaceImplBuilder::new()
.class(implementing_class)
.interface(generic_interface)
.build(&mut assembly)
.unwrap();
assert_eq!(
impl_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
}
}
#[test]
fn test_interface_impl_builder_missing_class() {
if let Ok(mut assembly) = get_test_assembly() {
let target_interface =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let result = InterfaceImplBuilder::new()
.interface(target_interface)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_interface_impl_builder_missing_interface() {
if let Ok(mut assembly) = get_test_assembly() {
let implementing_class = 1;
let result = InterfaceImplBuilder::new()
.class(implementing_class)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_interface_impl_builder_zero_class_rid() {
if let Ok(mut assembly) = get_test_assembly() {
let target_interface =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let result = InterfaceImplBuilder::new()
.class(0) .interface(target_interface)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_interface_impl_builder_invalid_interface_type() {
if let Ok(mut assembly) = get_test_assembly() {
let implementing_class = 1; let invalid_interface =
CodedIndex::new(TableId::Field, 1, CodedIndexType::TypeDefOrRef);
let result = InterfaceImplBuilder::new()
.class(implementing_class)
.interface(invalid_interface)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_interface_impl_builder_multiple_implementations() {
if let Ok(mut assembly) = get_test_assembly() {
let class1 = 1; let class2 = 2; let class3 = 3;
let interface1 = CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef); let interface2 = CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::TypeDefOrRef); let interface3 = CodedIndex::new(TableId::TypeSpec, 1, CodedIndexType::TypeDefOrRef);
let impl1_ref = InterfaceImplBuilder::new()
.class(class1)
.interface(interface1.clone())
.build(&mut assembly)
.unwrap();
let impl2_ref = InterfaceImplBuilder::new()
.class(class1) .interface(interface2.clone())
.build(&mut assembly)
.unwrap();
let impl3_ref = InterfaceImplBuilder::new()
.class(class2)
.interface(interface1) .build(&mut assembly)
.unwrap();
let impl4_ref = InterfaceImplBuilder::new()
.class(class3)
.interface(interface3)
.build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(&impl1_ref, &impl2_ref));
assert!(!std::sync::Arc::ptr_eq(&impl1_ref, &impl3_ref));
assert!(!std::sync::Arc::ptr_eq(&impl1_ref, &impl4_ref));
assert!(!std::sync::Arc::ptr_eq(&impl2_ref, &impl3_ref));
assert!(!std::sync::Arc::ptr_eq(&impl2_ref, &impl4_ref));
assert!(!std::sync::Arc::ptr_eq(&impl3_ref, &impl4_ref));
assert_eq!(
impl1_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
assert_eq!(
impl2_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
assert_eq!(
impl3_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
assert_eq!(
impl4_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
}
}
#[test]
fn test_interface_impl_builder_complex_inheritance() {
if let Ok(mut assembly) = get_test_assembly() {
let base_class = 1; let derived_class = 2; let interface1 = CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef); let interface2 = CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::TypeDefOrRef);
let base_impl_ref = InterfaceImplBuilder::new()
.class(base_class)
.interface(interface1)
.build(&mut assembly)
.unwrap();
let derived_impl_ref = InterfaceImplBuilder::new()
.class(derived_class)
.interface(interface2)
.build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(&base_impl_ref, &derived_impl_ref));
assert_eq!(
base_impl_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
assert_eq!(
derived_impl_ref.kind(),
ChangeRefKind::TableRow(TableId::InterfaceImpl)
);
}
}
}