1use crate::syntax::map::UnorderedMap as Map;
2use crate::syntax::Api;
3use proc_macro2::Ident;
4
5pub(crate) struct NamespaceEntries<'a> {
6 direct: Vec<&'a Api>,
7 nested: Vec<(&'a Ident, NamespaceEntries<'a>)>,
8}
9
10impl<'a> NamespaceEntries<'a> {
11 pub(crate) fn new(apis: Vec<&'a Api>) -> Self {
12 sort_by_inner_namespace(apis, 0)
13 }
14
15 pub(crate) fn direct_content(&self) -> &[&'a Api] {
16 &self.direct
17 }
18
19 pub(crate) fn nested_content(
20 &self,
21 ) -> impl Iterator<Item = (&'a Ident, &NamespaceEntries<'a>)> {
22 self.nested.iter().map(|(k, entries)| (*k, entries))
23 }
24}
25
26fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
27 let mut direct = Vec::new();
28 let mut nested_namespaces = Vec::new();
29 let mut index_of_namespace = Map::new();
30
31 for api in &apis {
32 if let Some(first_ns_elem) = api.namespace().iter().nth(depth) {
33 match index_of_namespace.get(first_ns_elem) {
34 None => {
35 index_of_namespace.insert(first_ns_elem, nested_namespaces.len());
36 nested_namespaces.push((first_ns_elem, vec![*api]));
37 }
38 Some(&index) => nested_namespaces[index].1.push(*api),
39 }
40 continue;
41 }
42 direct.push(*api);
43 }
44
45 let nested = nested_namespaces
46 .into_iter()
47 .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1)))
48 .collect();
49
50 NamespaceEntries { direct, nested }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::NamespaceEntries;
56 use crate::syntax::attrs::OtherAttrs;
57 use crate::syntax::cfg::CfgExpr;
58 use crate::syntax::namespace::Namespace;
59 use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair};
60 use proc_macro2::{Ident, Span};
61 use syn::punctuated::Punctuated;
62 use syn::Token;
63
64 #[test]
65 fn test_ns_entries_sort() {
66 let apis = &[
67 make_api(None, "C"),
68 make_api(None, "A"),
69 make_api(Some("G"), "E"),
70 make_api(Some("D"), "F"),
71 make_api(Some("G"), "H"),
72 make_api(Some("D::K"), "L"),
73 make_api(Some("D::K"), "M"),
74 make_api(None, "B"),
75 make_api(Some("D"), "I"),
76 make_api(Some("D"), "J"),
77 ];
78
79 let root = NamespaceEntries::new(Vec::from_iter(apis));
80
81 let root_direct = root.direct_content();
83 assert_eq!(root_direct.len(), 3);
84 assert_ident(root_direct[0], "C");
85 assert_ident(root_direct[1], "A");
86 assert_ident(root_direct[2], "B");
87
88 let mut root_nested = root.nested_content();
89 let (id, g) = root_nested.next().unwrap();
90 assert_eq!(id, "G");
91 let (id, d) = root_nested.next().unwrap();
92 assert_eq!(id, "D");
93 assert!(root_nested.next().is_none());
94
95 let g_direct = g.direct_content();
97 assert_eq!(g_direct.len(), 2);
98 assert_ident(g_direct[0], "E");
99 assert_ident(g_direct[1], "H");
100
101 let mut g_nested = g.nested_content();
102 assert!(g_nested.next().is_none());
103
104 let d_direct = d.direct_content();
106 assert_eq!(d_direct.len(), 3);
107 assert_ident(d_direct[0], "F");
108 assert_ident(d_direct[1], "I");
109 assert_ident(d_direct[2], "J");
110
111 let mut d_nested = d.nested_content();
112 let (id, k) = d_nested.next().unwrap();
113 assert_eq!(id, "K");
114
115 let k_direct = k.direct_content();
117 assert_eq!(k_direct.len(), 2);
118 assert_ident(k_direct[0], "L");
119 assert_ident(k_direct[1], "M");
120 }
121
122 fn assert_ident(api: &Api, expected: &str) {
123 if let Api::CxxType(cxx_type) = api {
124 assert_eq!(cxx_type.name.cxx.to_string(), expected);
125 } else {
126 unreachable!()
127 }
128 }
129
130 fn make_api(ns: Option<&str>, ident: &str) -> Api {
131 let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
132 Api::CxxType(ExternType {
133 cfg: CfgExpr::Unconditional,
134 lang: Lang::Rust,
135 doc: Doc::new(),
136 derives: Vec::new(),
137 attrs: OtherAttrs::none(),
138 visibility: Token),
139 type_token: Token),
140 name: Pair {
141 namespace: ns,
142 cxx: ForeignName::parse(ident, Span::call_site()).unwrap(),
143 rust: Ident::new(ident, Span::call_site()),
144 },
145 generics: Lifetimes {
146 lt_token: None,
147 lifetimes: Punctuated::new(),
148 gt_token: None,
149 },
150 colon_token: None,
151 bounds: Vec::new(),
152 semi_token: Token),
153 trusted: false,
154 })
155 }
156}