use crate::{
file::parser::Parser,
metadata::{
importscope::types::{ImportDeclaration, ImportKind, ImportsInfo},
streams::Blob,
tables::TableId,
token::Token,
},
Result,
};
pub struct ImportsParser<'a> {
parser: Parser<'a>,
blobs: &'a Blob<'a>,
}
impl<'a> ImportsParser<'a> {
#[must_use]
pub fn new(data: &'a [u8], blobs: &'a Blob) -> Self {
ImportsParser {
parser: Parser::new(data),
blobs,
}
}
pub fn parse_imports(&mut self) -> Result<ImportsInfo> {
let mut declarations = Vec::new();
while self.parser.has_more_data() {
let kind_value = self.parser.read_compressed_uint()?;
let kind = ImportKind::from_u32(kind_value)
.ok_or_else(|| malformed_error!("Invalid import kind: {}", kind_value))?;
let declaration = match kind {
ImportKind::ImportNamespace => {
let namespace = self.read_blob_string()?;
ImportDeclaration::ImportNamespace { namespace }
}
ImportKind::ImportAssemblyNamespace => {
let assembly_ref = self.read_assembly_ref_token()?;
let namespace = self.read_blob_string()?;
ImportDeclaration::ImportAssemblyNamespace {
assembly_ref,
namespace,
}
}
ImportKind::ImportType => {
let type_ref = self.parser.read_compressed_token()?;
ImportDeclaration::ImportType { type_ref }
}
ImportKind::ImportXmlNamespace => {
let alias = self.read_blob_string()?;
let namespace = self.read_blob_string()?;
ImportDeclaration::ImportXmlNamespace { alias, namespace }
}
ImportKind::ImportAssemblyReferenceAlias => {
let alias = self.read_blob_string()?;
ImportDeclaration::ImportAssemblyReferenceAlias { alias }
}
ImportKind::DefineAssemblyAlias => {
let alias = self.read_blob_string()?;
let assembly_ref = self.read_assembly_ref_token()?;
ImportDeclaration::DefineAssemblyAlias {
alias,
assembly_ref,
}
}
ImportKind::DefineNamespaceAlias => {
let alias = self.read_blob_string()?;
let namespace = self.read_blob_string()?;
ImportDeclaration::DefineNamespaceAlias { alias, namespace }
}
ImportKind::DefineAssemblyNamespaceAlias => {
let alias = self.read_blob_string()?;
let assembly_ref = self.read_assembly_ref_token()?;
let namespace = self.read_blob_string()?;
ImportDeclaration::DefineAssemblyNamespaceAlias {
alias,
assembly_ref,
namespace,
}
}
ImportKind::DefineTypeAlias => {
let alias = self.read_blob_string()?;
let type_ref = self.parser.read_compressed_token()?;
ImportDeclaration::DefineTypeAlias { alias, type_ref }
}
};
declarations.push(declaration);
}
Ok(ImportsInfo::with_declarations(declarations))
}
fn read_blob_string(&mut self) -> Result<String> {
let blob_index = self.parser.read_compressed_uint()?;
let blob_data = self.blobs.get(blob_index as usize)?;
Ok(String::from_utf8_lossy(blob_data).into_owned())
}
fn read_assembly_ref_token(&mut self) -> Result<Token> {
let row_id = self.parser.read_compressed_uint()?;
Ok(Token::new((TableId::AssemblyRef as u32) << 24 | row_id))
}
}
pub fn parse_imports_blob(data: &[u8], blobs: &Blob) -> Result<ImportsInfo> {
if data.is_empty() {
return Ok(ImportsInfo::new());
}
let mut parser = ImportsParser::new(data, blobs);
parser.parse_imports()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::streams::Blob;
fn create_empty_blob_stream() -> Blob<'static> {
Blob::from(&[0x00]).expect("Failed to create blob stream")
}
fn create_test_blob_stream() -> Vec<u8> {
let mut data = vec![0x00];
data.push(0x06); data.extend_from_slice(b"System");
data.push(0x09); data.extend_from_slice(b"TestAlias");
data.push(0x12); data.extend_from_slice(b"http://example.com");
data
}
#[test]
fn test_parse_empty_blob() {
let blobs = create_empty_blob_stream();
let result = parse_imports_blob(&[], &blobs).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_imports_parser_new() {
let blobs = create_empty_blob_stream();
let data = &[0x01, 0x00];
let parser = ImportsParser::new(data, &blobs);
assert_eq!(parser.parser.len(), 2);
}
#[test]
fn test_parse_import_namespace() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x01, 0x01];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::ImportNamespace { namespace } => {
assert_eq!(namespace, "System");
}
_ => panic!("Expected ImportNamespace declaration"),
}
}
#[test]
fn test_parse_import_assembly_namespace() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x02, 0x03, 0x01];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::ImportAssemblyNamespace {
assembly_ref,
namespace,
} => {
assert_eq!(assembly_ref.value(), 0x23000003); assert_eq!(namespace, "System");
}
_ => panic!("Expected ImportAssemblyNamespace declaration"),
}
}
#[test]
fn test_parse_import_type() {
let blobs = create_empty_blob_stream();
let import_data = &[0x03, 0x15];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::ImportType { type_ref } => {
assert_eq!(type_ref.value(), 0x01000005); }
_ => panic!("Expected ImportType declaration"),
}
}
#[test]
fn test_parse_import_xml_namespace() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x04, 0x08, 0x12];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::ImportXmlNamespace { alias, namespace } => {
assert_eq!(alias, "TestAlias");
assert_eq!(namespace, "http://example.com");
}
_ => panic!("Expected ImportXmlNamespace declaration"),
}
}
#[test]
fn test_parse_import_assembly_reference_alias() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x05, 0x08];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::ImportAssemblyReferenceAlias { alias } => {
assert_eq!(alias, "TestAlias");
}
_ => panic!("Expected ImportAssemblyReferenceAlias declaration"),
}
}
#[test]
fn test_parse_define_assembly_alias() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x06, 0x08, 0x02];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::DefineAssemblyAlias {
alias,
assembly_ref,
} => {
assert_eq!(alias, "TestAlias");
assert_eq!(assembly_ref.value(), 0x23000002);
}
_ => panic!("Expected DefineAssemblyAlias declaration"),
}
}
#[test]
fn test_parse_define_namespace_alias() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x07, 0x08, 0x01];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::DefineNamespaceAlias { alias, namespace } => {
assert_eq!(alias, "TestAlias");
assert_eq!(namespace, "System");
}
_ => panic!("Expected DefineNamespaceAlias declaration"),
}
}
#[test]
fn test_parse_define_assembly_namespace_alias() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x08, 0x08, 0x01, 0x01];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::DefineAssemblyNamespaceAlias {
alias,
assembly_ref,
namespace,
} => {
assert_eq!(alias, "TestAlias");
assert_eq!(assembly_ref.value(), 0x23000001);
assert_eq!(namespace, "System");
}
_ => panic!("Expected DefineAssemblyNamespaceAlias declaration"),
}
}
#[test]
fn test_parse_define_type_alias() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x09, 0x08, 0x28];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 1);
match &result.declarations[0] {
ImportDeclaration::DefineTypeAlias { alias, type_ref } => {
assert_eq!(alias, "TestAlias");
assert_eq!(type_ref.value(), 0x0200000A); }
_ => panic!("Expected DefineTypeAlias declaration"),
}
}
#[test]
fn test_parse_multiple_declarations() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x01, 0x01, 0x05, 0x08];
let result = parse_imports_blob(import_data, &blobs).unwrap();
assert_eq!(result.len(), 2);
match &result.declarations[0] {
ImportDeclaration::ImportNamespace { namespace } => {
assert_eq!(namespace, "System");
}
_ => panic!("Expected ImportNamespace as first declaration"),
}
match &result.declarations[1] {
ImportDeclaration::ImportAssemblyReferenceAlias { alias } => {
assert_eq!(alias, "TestAlias");
}
_ => panic!("Expected ImportAssemblyReferenceAlias as second declaration"),
}
}
#[test]
fn test_parse_invalid_kind() {
let blobs = create_empty_blob_stream();
let import_data = &[0x00];
let result = parse_imports_blob(import_data, &blobs);
assert!(result.is_err());
let import_data = &[0x0A];
let result = parse_imports_blob(import_data, &blobs);
assert!(result.is_err());
let import_data = &[0xFF];
let result = parse_imports_blob(import_data, &blobs);
assert!(result.is_err());
}
#[test]
fn test_parse_truncated_data() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x02];
let result = parse_imports_blob(import_data, &blobs);
assert!(result.is_err());
let import_data = &[0x02, 0x01];
let result = parse_imports_blob(import_data, &blobs);
assert!(result.is_err());
}
#[test]
fn test_imports_info_iteration() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
let import_data = &[0x01, 0x01, 0x05, 0x08];
let result = parse_imports_blob(import_data, &blobs).unwrap();
let mut count = 0;
for _decl in result.iter() {
count += 1;
}
assert_eq!(count, 2);
count = 0;
for _decl in &result {
count += 1;
}
assert_eq!(count, 2);
}
#[test]
fn test_assembly_ref_token_format() {
let blob_data = create_test_blob_stream();
let blobs = Blob::from(&blob_data).expect("Failed to create blob stream");
for row_id in [1u32, 10, 50, 127] {
let import_data = vec![0x06, 0x08, row_id as u8]; let result = parse_imports_blob(&import_data, &blobs).unwrap();
match &result.declarations[0] {
ImportDeclaration::DefineAssemblyAlias { assembly_ref, .. } => {
let expected = (TableId::AssemblyRef as u32) << 24 | row_id;
assert_eq!(assembly_ref.value(), expected);
}
_ => panic!("Expected DefineAssemblyAlias"),
}
}
}
}