use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{ParamRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
pub struct ParamBuilder {
name: Option<String>,
flags: Option<u32>,
sequence: Option<u32>,
}
impl Default for ParamBuilder {
fn default() -> Self {
Self::new()
}
}
impl ParamBuilder {
#[must_use]
pub fn new() -> Self {
Self {
name: None,
flags: None,
sequence: None,
}
}
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn flags(mut self, flags: u32) -> Self {
self.flags = Some(flags);
self
}
#[must_use]
pub fn sequence(mut self, sequence: u32) -> Self {
self.sequence = Some(sequence);
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let flags = self.flags.ok_or_else(|| {
Error::ModificationInvalid("Parameter flags are required".to_string())
})?;
let sequence = self.sequence.ok_or_else(|| {
Error::ModificationInvalid("Parameter sequence is required".to_string())
})?;
let name_index = if let Some(name) = self.name {
assembly.string_get_or_add(&name)?.placeholder()
} else {
0 };
let rid = assembly.next_rid(TableId::Param)?;
let token = Token::from_parts(TableId::Param, rid);
let param_raw = ParamRaw {
rid,
token,
offset: 0, flags,
sequence,
name: name_index,
};
assembly.table_row_add(TableId::Param, TableDataOwned::Param(param_raw))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::{ChangeRefKind, CilAssembly},
metadata::{cilassemblyview::CilAssemblyView, tables::ParamAttributes},
};
use std::path::PathBuf;
#[test]
fn test_param_builder_basic() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let ref_ = ParamBuilder::new()
.name("testParam")
.flags(ParamAttributes::IN)
.sequence(1)
.build(&mut assembly)
.unwrap();
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::Param));
}
}
#[test]
fn test_param_builder_return_type() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let ref_ = ParamBuilder::new()
.flags(0) .sequence(0) .build(&mut assembly)
.unwrap();
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::Param));
}
}
#[test]
fn test_param_builder_with_attributes() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let ref_ = ParamBuilder::new()
.name("outParam")
.flags(ParamAttributes::OUT | ParamAttributes::OPTIONAL)
.sequence(2)
.build(&mut assembly)
.unwrap();
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::Param));
}
}
#[test]
fn test_param_builder_default_value() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let ref_ = ParamBuilder::new()
.name("defaultParam")
.flags(ParamAttributes::IN | ParamAttributes::HAS_DEFAULT)
.sequence(3)
.build(&mut assembly)
.unwrap();
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::Param));
}
}
#[test]
fn test_param_builder_missing_flags() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let result = ParamBuilder::new()
.name("testParam")
.sequence(1)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_param_builder_missing_sequence() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let result = ParamBuilder::new()
.name("testParam")
.flags(ParamAttributes::IN)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_param_builder_multiple_params() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let ref1 = ParamBuilder::new()
.name("param1")
.flags(ParamAttributes::IN)
.sequence(1)
.build(&mut assembly)
.unwrap();
let ref2 = ParamBuilder::new()
.name("param2")
.flags(ParamAttributes::OUT)
.sequence(2)
.build(&mut assembly)
.unwrap();
let ref3 = ParamBuilder::new()
.flags(0)
.sequence(0) .build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref2));
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref3));
assert!(!std::sync::Arc::ptr_eq(&ref2, &ref3));
assert_eq!(ref1.kind(), ChangeRefKind::TableRow(TableId::Param));
assert_eq!(ref2.kind(), ChangeRefKind::TableRow(TableId::Param));
assert_eq!(ref3.kind(), ChangeRefKind::TableRow(TableId::Param));
}
}
}