cxx_qt_gen/syntax/
qtitem.rs

1// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5
6use crate::syntax::path::path_compare_str;
7use proc_macro2::TokenStream;
8use quote::ToTokens;
9use syn::parse::{Parse, ParseStream};
10use syn::{Attribute, Item, ItemMod, Result, Token, Visibility};
11
12#[derive(Clone, PartialEq, Eq)]
13/// Representation of either a Syn Item, a CXX module, or a CXX-Qt module
14pub enum CxxQtItem {
15    /// A normal syntax item that we pass through
16    Item(Item),
17    /// A CXX module that we need to generate code for
18    Cxx(ItemMod),
19    /// A CxxQt module block that we need to parse and later generate code for
20    CxxQt(ItemMod),
21}
22
23impl std::fmt::Debug for CxxQtItem {
24    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
25        match self {
26            CxxQtItem::Item(v0) => {
27                let mut formatter = formatter.debug_tuple("Item");
28                formatter.field(v0);
29                formatter.finish()
30            }
31            CxxQtItem::Cxx(v0) => {
32                let mut formatter = formatter.debug_tuple("Cxx");
33                formatter.field(v0);
34                formatter.finish()
35            }
36            CxxQtItem::CxxQt(v0) => {
37                let mut formatter = formatter.debug_tuple("CxxQt");
38                formatter.field(v0);
39                formatter.finish()
40            }
41        }
42    }
43}
44
45impl Parse for CxxQtItem {
46    fn parse(input: ParseStream) -> Result<Self> {
47        // Fork and skip over the attributes as we want to read the next token
48        let ahead = input.fork();
49        let attributes = ahead.call(Attribute::parse_outer)?;
50
51        // See if the next token is a mod
52        ahead.parse::<Visibility>()?;
53        ahead.parse::<Option<Token![unsafe]>>()?;
54
55        if ahead.peek(Token![mod]) {
56            for attribute in &attributes {
57                if path_compare_str(attribute.meta.path(), &["cxx", "bridge"]) {
58                    return input.parse().map(CxxQtItem::Cxx);
59                } else if path_compare_str(attribute.meta.path(), &["cxx_qt", "bridge"]) {
60                    return input.parse().map(CxxQtItem::CxxQt);
61                }
62            }
63        }
64
65        // Fallback to using normal Item
66        input.parse().map(CxxQtItem::Item)
67    }
68}
69
70impl ToTokens for CxxQtItem {
71    fn to_tokens(&self, tokens: &mut TokenStream) {
72        match self {
73            CxxQtItem::Item(item) => {
74                item.to_tokens(tokens);
75            }
76            CxxQtItem::Cxx(module) => {
77                module.to_tokens(tokens);
78            }
79            CxxQtItem::CxxQt(module) => {
80                module.to_tokens(tokens);
81            }
82        }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use syn::parse_quote;
90    #[test]
91    fn test_format_cxx() {
92        let cxx: CxxQtItem = parse_quote! {
93          #[cxx::bridge]
94          mod ffi {}
95        };
96        let debug_formatted = format!("{:?}", cxx);
97        assert!(debug_formatted.starts_with("Cxx(ItemMod"))
98    }
99
100    #[test]
101    fn test_format_cxx_qt() {
102        let cxx_qt: CxxQtItem = parse_quote! {
103          #[cxx_qt::bridge]
104          mod ffi {}
105        };
106        let debug_formatted = format!("{:?}", cxx_qt);
107        assert!(debug_formatted.starts_with("CxxQt(ItemMod"))
108    }
109
110    #[test]
111    fn test_format_non_cxx() {
112        let cxx: CxxQtItem = parse_quote! {
113            #[attr]
114            mod ffi {}
115        };
116        let debug_formatted = format!("{:?}", cxx);
117        assert!(debug_formatted.starts_with("Item(Item::Mod"))
118    }
119
120    #[test]
121    fn test_format_rust_item() {
122        let rust: CxxQtItem = parse_quote! {
123          struct MyStruct {
124                name: &str
125          }
126        };
127        let debug_formatted = format!("{:?}", rust);
128        assert!(debug_formatted.starts_with("Item(Item::Struct"))
129    }
130}