use std::sync::Arc;
use crossbeam_skiplist::map::Entry;
use crate::{
metadata::{
tables::{ExportedTypeList, ExportedTypeMap, ExportedTypeRc},
token::Token,
typesystem::CilTypeReference,
},
Result,
};
pub struct Exports {
data: ExportedTypeMap,
}
impl Exports {
#[must_use]
pub fn new() -> Self {
Exports {
data: ExportedTypeMap::new(),
}
}
pub fn insert(&self, token: Token, export: ExportedTypeRc) -> Result<()> {
self.data.insert(token, export);
Ok(())
}
pub fn get(&self, token: &Token) -> Option<Entry<'_, Token, ExportedTypeRc>> {
self.data.get(token)
}
pub fn types(&self) -> &ExportedTypeMap {
&self.data
}
pub fn iter(&self) -> crossbeam_skiplist::map::Iter<'_, Token, ExportedTypeRc> {
self.data.iter()
}
pub fn find_by_name(&self, name: &str, namespace: Option<&str>) -> Option<ExportedTypeRc> {
for exported_type in &self.data {
let exported = exported_type.value();
if exported.name == name {
if let Some(ns) = namespace {
if let Some(exported_ns) = &exported.namespace {
if exported_ns == ns {
return Some(exported.clone());
}
} else if ns.is_empty() {
return Some(exported.clone());
}
} else if exported.namespace.is_none() {
return Some(exported.clone());
}
}
}
None
}
pub fn find_by_implementation(&self, reference: &CilTypeReference) -> ExportedTypeList {
let result = Arc::new(boxcar::Vec::new());
for exported_type in &self.data {
let borrowed = exported_type.value();
if let Some(implementation) = borrowed.get_implementation() {
match (implementation, reference) {
(CilTypeReference::File(a), CilTypeReference::File(b)) => {
if a.token == b.token {
result.push(borrowed.clone());
}
}
(CilTypeReference::AssemblyRef(a), CilTypeReference::AssemblyRef(b)) => {
if a.token == b.token {
result.push(borrowed.clone());
}
}
(CilTypeReference::ExportedType(a), CilTypeReference::ExportedType(b)) => {
if a.token == b.token {
result.push(borrowed.clone());
}
}
_ => {}
}
}
}
result
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl<'a> IntoIterator for &'a Exports {
type Item = crossbeam_skiplist::map::Entry<'a, Token, ExportedTypeRc>;
type IntoIter = crossbeam_skiplist::map::Iter<'a, Token, ExportedTypeRc>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl Default for Exports {
fn default() -> Self {
Self::new()
}
}
impl Clone for Exports {
fn clone(&self) -> Self {
let new_exports = Self::new();
for entry in &self.data {
new_exports.data.insert(*entry.key(), entry.value().clone());
}
new_exports
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
metadata::{identity::AssemblyIdentity, token::Token, typesystem::TypeRegistry},
test::{create_cil_type, create_exportedtype},
};
#[test]
fn new_exports_is_empty() {
let exports = Exports::new();
assert_eq!(exports.len(), 0);
assert!(exports.is_empty());
}
#[test]
fn find_by_name_works() {
let exports = Exports::new();
let test_identity = AssemblyIdentity::parse("TestAssembly, Version=1.0.0.0").unwrap();
let type_registry = TypeRegistry::new(test_identity).unwrap();
let dummy_type = create_cil_type(Token::new(0x02000001), "TestNamespace", "TestType", None);
type_registry.insert(&dummy_type);
let exported_type = create_exportedtype(dummy_type);
exports
.data
.insert(Token::new(0x27000001), exported_type.clone());
let found = exports.find_by_name("ExportedType", Some("Test.Namespace"));
assert!(found.is_some());
assert_eq!(found.unwrap().token, Token::new(0x27000001));
let not_found = exports.find_by_name("ExportedType", Some("Wrong.Namespace"));
assert!(not_found.is_none());
let not_found = exports.find_by_name("WrongName", Some("Test.Namespace"));
assert!(not_found.is_none());
}
#[test]
fn iter_works() {
let exports = Exports::new();
let test_identity = AssemblyIdentity::parse("TestAssembly, Version=1.0.0.0").unwrap();
let type_registry = TypeRegistry::new(test_identity).unwrap();
let dummy_type1 =
create_cil_type(Token::new(0x02000001), "TestNamespace", "TestType1", None);
let dummy_type2 =
create_cil_type(Token::new(0x02000002), "TestNamespace", "TestType2", None);
type_registry.insert(&dummy_type1);
type_registry.insert(&dummy_type2);
let exported_type1 = create_exportedtype(dummy_type1);
let exported_type2 = create_exportedtype(dummy_type2);
exports.data.insert(Token::new(0x27000001), exported_type1);
exports.data.insert(Token::new(0x27000002), exported_type2);
let mut count = 0;
let mut tokens = Vec::new();
for entry in exports.iter() {
count += 1;
tokens.push(*entry.key());
}
assert_eq!(count, 2);
assert!(tokens.contains(&Token::new(0x27000001)));
assert!(tokens.contains(&Token::new(0x27000002)));
}
}