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