use crate::*;
use rayon::prelude::*;
use squote::TokenStream;
#[derive(Default)]
pub struct TypeTree {
pub types: Vec<TypeDefinition>,
pub namespaces: TypeNamespaces,
pub include_foundation: bool,
}
impl TypeTree {
pub fn from_limits(reader: &'static winmd::TypeReader, limits: &TypeLimits) -> Self {
let mut tree = TypeTree::default();
let mut set = std::collections::BTreeSet::new();
for limit in limits.limits() {
match &limit.limit {
TypeLimit::All => {
for def in reader.namespace_types(&limit.namespace) {
tree.insert_if(reader, &mut set, &def);
}
}
TypeLimit::Some(types) => {
for name in types {
tree.insert_if(
reader,
&mut set,
&reader.expect_type((&limit.namespace, name)),
);
}
}
}
}
tree
}
fn insert_if(
&mut self,
reader: &winmd::TypeReader,
set: &mut std::collections::BTreeSet<winmd::TypeDef>,
def: &winmd::Type,
) {
match def {
winmd::Type::TypeDef(def) => match def.category() {
winmd::TypeCategory::Contract | winmd::TypeCategory::Attribute => {}
_ => {
if set.insert(*def) {
let t = TypeDefinition::from_type_def(def);
for def in t.dependencies() {
self.insert_if(reader, set, &winmd::Type::TypeDef(def));
}
self.insert(t.name().namespace, t);
}
}
},
winmd::Type::MethodDef((def, method)) => {
let t = TypeDefinition::from_method_def(def, method);
for def in t.dependencies() {
self.insert_if(reader, set, &winmd::Type::TypeDef(def));
}
self.insert(t.name().namespace, t);
}
winmd::Type::Field((def, field)) => {
let t = TypeDefinition::from_field(def, field);
self.insert(t.name().namespace, t);
}
}
}
pub fn insert(&mut self, namespace: &'static str, t: TypeDefinition) {
if let Some(pos) = namespace.find('.') {
self.namespaces
.0
.entry(&namespace[..pos])
.or_default()
.insert(&namespace[pos + 1..], t);
} else {
self.namespaces
.0
.entry(namespace)
.or_default()
.types
.push(t);
}
}
pub fn remove(&mut self, namespace: &str) {
if let Some(pos) = namespace.find('.') {
if let Some(tree) = self.namespaces.0.get_mut(&namespace[..pos]) {
tree.remove(&namespace[pos + 1..])
}
} else {
self.namespaces.0.remove(namespace);
}
}
pub fn reexport(&mut self) {
self.namespaces
.0
.entry("Windows")
.or_default()
.include_foundation = true;
}
pub fn gen<'a>(&'a self) -> impl ParallelIterator<Item = TokenStream> + 'a {
self.types
.par_iter()
.map(|t| t.gen())
.chain(self.namespaces.gen())
}
}
#[cfg(test)]
mod tests {
use crate::{NamespaceTypes, TypeLimit, TypeLimits, TypeTree};
#[test]
fn test_dependency_inclusion() {
let reader = winmd::TypeReader::get();
let mut limits = TypeLimits::new(reader);
limits
.insert(NamespaceTypes {
namespace: "windows.foundation",
limit: TypeLimit::All,
})
.unwrap();
limits
.insert(NamespaceTypes {
namespace: "windows.ui",
limit: TypeLimit::All,
})
.unwrap();
let root = TypeTree::from_limits(reader, &limits);
assert!(root.namespaces.0.len() == 1);
let windows = &root.namespaces.0["Windows"];
assert!(windows.namespaces.0.len() == 2);
let foundation = &windows.namespaces.0["Foundation"];
let ui = &windows.namespaces.0["UI"];
assert!(ui.namespaces.0.is_empty());
assert!(foundation.namespaces.0.len() == 1);
let collections = &foundation.namespaces.0["Collections"];
assert!(collections.namespaces.0.is_empty());
assert!(root.types.is_empty());
assert!(windows.types.is_empty());
assert!(ui.types.iter().any(|t| t.name().name == "Colors"));
assert!(ui.types.iter().any(|t| t.name().name == "IColorsStatics"));
assert!(foundation.types.iter().any(|t| t.name().name == "Uri"));
assert!(foundation
.types
.iter()
.any(|t| t.name().name == "IStringable"));
assert!(collections
.types
.iter()
.any(|t| t.name().name == "IVectorView`1"));
assert!(
collections
.types
.iter()
.any(|t| t.name().name == "PropertySet")
== false
);
}
}