use crate::syntax::map::UnorderedMap as Map;
use crate::syntax::Api;
use proc_macro2::Ident;
pub(crate) struct NamespaceEntries<'a> {
direct: Vec<&'a Api>,
nested: Vec<(&'a Ident, NamespaceEntries<'a>)>,
}
impl<'a> NamespaceEntries<'a> {
pub(crate) fn new(apis: Vec<&'a Api>) -> Self {
sort_by_inner_namespace(apis, 0)
}
pub(crate) fn direct_content(&self) -> &[&'a Api] {
&self.direct
}
pub(crate) fn nested_content(
&self,
) -> impl Iterator<Item = (&'a Ident, &NamespaceEntries<'a>)> {
self.nested.iter().map(|(k, entries)| (*k, entries))
}
}
fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
let mut direct = Vec::new();
let mut nested_namespaces = Vec::new();
let mut index_of_namespace = Map::new();
for api in &apis {
if let Some(first_ns_elem) = api.namespace().iter().nth(depth) {
match index_of_namespace.get(first_ns_elem) {
None => {
index_of_namespace.insert(first_ns_elem, nested_namespaces.len());
nested_namespaces.push((first_ns_elem, vec![*api]));
}
Some(&index) => nested_namespaces[index].1.push(*api),
}
continue;
}
direct.push(*api);
}
let nested = nested_namespaces
.into_iter()
.map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1)))
.collect();
NamespaceEntries { direct, nested }
}
#[cfg(test)]
mod tests {
use super::NamespaceEntries;
use crate::syntax::attrs::OtherAttrs;
use crate::syntax::cfg::CfgExpr;
use crate::syntax::namespace::Namespace;
use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair};
use proc_macro2::{Ident, Span};
use syn::punctuated::Punctuated;
use syn::Token;
#[test]
fn test_ns_entries_sort() {
let apis = &[
make_api(None, "C"),
make_api(None, "A"),
make_api(Some("G"), "E"),
make_api(Some("D"), "F"),
make_api(Some("G"), "H"),
make_api(Some("D::K"), "L"),
make_api(Some("D::K"), "M"),
make_api(None, "B"),
make_api(Some("D"), "I"),
make_api(Some("D"), "J"),
];
let root = NamespaceEntries::new(Vec::from_iter(apis));
let root_direct = root.direct_content();
assert_eq!(root_direct.len(), 3);
assert_ident(root_direct[0], "C");
assert_ident(root_direct[1], "A");
assert_ident(root_direct[2], "B");
let mut root_nested = root.nested_content();
let (id, g) = root_nested.next().unwrap();
assert_eq!(id, "G");
let (id, d) = root_nested.next().unwrap();
assert_eq!(id, "D");
assert!(root_nested.next().is_none());
let g_direct = g.direct_content();
assert_eq!(g_direct.len(), 2);
assert_ident(g_direct[0], "E");
assert_ident(g_direct[1], "H");
let mut g_nested = g.nested_content();
assert!(g_nested.next().is_none());
let d_direct = d.direct_content();
assert_eq!(d_direct.len(), 3);
assert_ident(d_direct[0], "F");
assert_ident(d_direct[1], "I");
assert_ident(d_direct[2], "J");
let mut d_nested = d.nested_content();
let (id, k) = d_nested.next().unwrap();
assert_eq!(id, "K");
let k_direct = k.direct_content();
assert_eq!(k_direct.len(), 2);
assert_ident(k_direct[0], "L");
assert_ident(k_direct[1], "M");
}
fn assert_ident(api: &Api, expected: &str) {
if let Api::CxxType(cxx_type) = api {
assert_eq!(cxx_type.name.cxx.to_string(), expected);
} else {
unreachable!()
}
}
fn make_api(ns: Option<&str>, ident: &str) -> Api {
let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
Api::CxxType(ExternType {
cfg: CfgExpr::Unconditional,
lang: Lang::Rust,
doc: Doc::new(),
derives: Vec::new(),
attrs: OtherAttrs::none(),
visibility: Token![pub](Span::call_site()),
type_token: Token![type](Span::call_site()),
name: Pair {
namespace: ns,
cxx: ForeignName::parse(ident, Span::call_site()).unwrap(),
rust: Ident::new(ident, Span::call_site()),
},
generics: Lifetimes {
lt_token: None,
lifetimes: Punctuated::new(),
gt_token: None,
},
colon_token: None,
bounds: Vec::new(),
semi_token: Token![;](Span::call_site()),
trusted: false,
})
}
}