aboutsummaryrefslogtreecommitdiff
path: root/gen/src/nested.rs
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@gmail.com>2020-11-01 22:39:04 -0800
committerDavid Tolnay <dtolnay@gmail.com>2020-11-01 22:39:22 -0800
commitf7b81fb845a0aaa4238653af9f9a5624b290bfcb (patch)
tree9388b6f8df059132f0b715a3cce02bc9ec8e21e6 /gen/src/nested.rs
parentde4b55392752ae3688b06ce326f04bd541d1e4f7 (diff)
downloadcxx-f7b81fb845a0aaa4238653af9f9a5624b290bfcb.tar.gz
Move NamespaceEntries to nested:: module
Since the way we sort nested namespaces is no longer based on alphabetic order.
Diffstat (limited to 'gen/src/nested.rs')
-rw-r--r--gen/src/nested.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
new file mode 100644
index 00000000..143f6ec9
--- /dev/null
+++ b/gen/src/nested.rs
@@ -0,0 +1,137 @@
+use crate::syntax::Api;
+use proc_macro2::Ident;
+use std::collections::HashMap as Map;
+use std::iter::FromIterator;
+
+pub struct NamespaceEntries<'a> {
+ direct: Vec<&'a Api>,
+ nested: Vec<(&'a Ident, NamespaceEntries<'a>)>,
+}
+
+impl<'a> NamespaceEntries<'a> {
+ pub fn new(apis: &'a [Api]) -> Self {
+ let api_refs = Vec::from_iter(apis);
+ sort_by_inner_namespace(api_refs, 0)
+ }
+
+ pub fn direct_content(&self) -> &[&'a Api] {
+ &self.direct
+ }
+
+ pub 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().and_then(|ns| ns.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::namespace::Namespace;
+ use crate::syntax::{Api, Doc, ExternType, Pair};
+ use proc_macro2::{Ident, Span};
+ 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(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());
+
+ // ::G
+ 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());
+
+ // ::D
+ 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");
+
+ // ::D::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.ident.cxx.ident, 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 {
+ doc: Doc::new(),
+ type_token: Token![type](Span::call_site()),
+ ident: Pair::new(ns, Ident::new(ident, Span::call_site())),
+ semi_token: Token![;](Span::call_site()),
+ trusted: false,
+ })
+ }
+}