use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{FieldRvaRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone)]
pub struct FieldRVABuilder {
field: Option<u32>,
rva: Option<u32>,
}
impl Default for FieldRVABuilder {
fn default() -> Self {
Self::new()
}
}
impl FieldRVABuilder {
#[must_use]
pub fn new() -> Self {
Self {
field: None,
rva: None,
}
}
#[must_use]
pub fn field(mut self, field: u32) -> Self {
self.field = Some(field);
self
}
#[must_use]
pub fn rva(mut self, rva: u32) -> Self {
self.rva = Some(rva);
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let field = self.field.ok_or_else(|| {
Error::ModificationInvalid("Field row index is required for FieldRVA".to_string())
})?;
let rva = self.rva.ok_or_else(|| {
Error::ModificationInvalid("RVA is required for FieldRVA".to_string())
})?;
if field == 0 {
return Err(Error::ModificationInvalid(
"Field row index cannot be 0".to_string(),
));
}
if rva == 0 {
return Err(Error::ModificationInvalid("RVA cannot be 0".to_string()));
}
let field_rva = FieldRvaRaw {
rid: 0,
token: Token::new(0),
offset: 0,
rva,
field,
};
assembly.table_row_add(TableId::FieldRVA, TableDataOwned::FieldRVA(field_rva))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::ChangeRefKind,
metadata::tables::{FieldAttributes, TableId},
test::factories::table::assemblyref::get_test_assembly,
};
#[test]
fn test_field_rva_builder_basic() -> Result<()> {
let mut assembly = get_test_assembly()?;
let field_ref = crate::metadata::tables::FieldBuilder::new()
.name("StaticData")
.flags(FieldAttributes::STATIC | FieldAttributes::PRIVATE)
.signature(&[0x06]) .build(&mut assembly)?;
let rva_ref = FieldRVABuilder::new()
.field(field_ref.placeholder())
.rva(0x2000)
.build(&mut assembly)?;
assert_eq!(rva_ref.kind(), ChangeRefKind::TableRow(TableId::FieldRVA));
Ok(())
}
#[test]
fn test_field_rva_builder_default() -> Result<()> {
let builder = FieldRVABuilder::default();
assert!(builder.field.is_none());
assert!(builder.rva.is_none());
Ok(())
}
#[test]
fn test_field_rva_builder_missing_field() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = FieldRVABuilder::new().rva(0x2000).build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Field row index is required"));
Ok(())
}
#[test]
fn test_field_rva_builder_missing_rva() -> Result<()> {
let mut assembly = get_test_assembly()?;
let field_ref = crate::metadata::tables::FieldBuilder::new()
.name("StaticData")
.flags(FieldAttributes::STATIC | FieldAttributes::PRIVATE)
.signature(&[0x06])
.build(&mut assembly)?;
let result = FieldRVABuilder::new()
.field(field_ref.placeholder())
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("RVA is required"));
Ok(())
}
#[test]
fn test_field_rva_builder_zero_field() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = FieldRVABuilder::new()
.field(0)
.rva(0x2000)
.build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Field row index cannot be 0"));
Ok(())
}
#[test]
fn test_field_rva_builder_zero_rva() -> Result<()> {
let mut assembly = get_test_assembly()?;
let field_ref = crate::metadata::tables::FieldBuilder::new()
.name("StaticData")
.flags(FieldAttributes::STATIC | FieldAttributes::PRIVATE)
.signature(&[0x06])
.build(&mut assembly)?;
let result = FieldRVABuilder::new()
.field(field_ref.placeholder())
.rva(0) .build(&mut assembly);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("RVA cannot be 0"));
Ok(())
}
#[test]
fn test_field_rva_builder_multiple_entries() -> Result<()> {
let mut assembly = get_test_assembly()?;
let field1_ref = crate::metadata::tables::FieldBuilder::new()
.name("StaticData1")
.flags(FieldAttributes::STATIC | FieldAttributes::PRIVATE)
.signature(&[0x06])
.build(&mut assembly)?;
let field2_ref = crate::metadata::tables::FieldBuilder::new()
.name("StaticData2")
.flags(FieldAttributes::STATIC | FieldAttributes::PRIVATE)
.signature(&[0x06])
.build(&mut assembly)?;
let rva1_ref = FieldRVABuilder::new()
.field(field1_ref.placeholder())
.rva(0x2000)
.build(&mut assembly)?;
let rva2_ref = FieldRVABuilder::new()
.field(field2_ref.placeholder())
.rva(0x3000)
.build(&mut assembly)?;
assert!(!std::sync::Arc::ptr_eq(&rva1_ref, &rva2_ref));
assert_eq!(rva1_ref.kind(), ChangeRefKind::TableRow(TableId::FieldRVA));
assert_eq!(rva2_ref.kind(), ChangeRefKind::TableRow(TableId::FieldRVA));
Ok(())
}
#[test]
fn test_field_rva_builder_various_rva_values() -> Result<()> {
let mut assembly = get_test_assembly()?;
let test_rvas = [0x1000, 0x2000, 0x4000, 0x8000, 0x10000];
for (i, &rva) in test_rvas.iter().enumerate() {
let field_ref = crate::metadata::tables::FieldBuilder::new()
.name(format!("StaticData{i}"))
.flags(FieldAttributes::STATIC | FieldAttributes::PRIVATE)
.signature(&[0x06])
.build(&mut assembly)?;
let rva_ref = FieldRVABuilder::new()
.field(field_ref.placeholder())
.rva(rva)
.build(&mut assembly)?;
assert_eq!(rva_ref.kind(), ChangeRefKind::TableRow(TableId::FieldRVA));
}
Ok(())
}
#[test]
fn test_field_rva_builder_fluent_api() -> Result<()> {
let mut assembly = get_test_assembly()?;
let field_ref = crate::metadata::tables::FieldBuilder::new()
.name("FluentTestField")
.flags(FieldAttributes::STATIC | FieldAttributes::PRIVATE)
.signature(&[0x06])
.build(&mut assembly)?;
let rva_ref = FieldRVABuilder::new()
.field(field_ref.placeholder())
.rva(0x5000)
.build(&mut assembly)?;
assert_eq!(rva_ref.kind(), ChangeRefKind::TableRow(TableId::FieldRVA));
Ok(())
}
#[test]
fn test_field_rva_builder_clone() {
let builder1 = FieldRVABuilder::new().field(1).rva(0x2000);
let builder2 = builder1.clone();
assert_eq!(builder1.field, builder2.field);
assert_eq!(builder1.rva, builder2.rva);
}
#[test]
fn test_field_rva_builder_debug() {
let builder = FieldRVABuilder::new().field(1).rva(0x2000);
let debug_str = format!("{builder:?}");
assert!(debug_str.contains("FieldRVABuilder"));
}
}