swift_bridge_ir/
parse.rs

1use crate::bridge_module_attributes::CfgAttr;
2use crate::bridged_type::BridgedType;
3use crate::errors::{ParseError, ParseErrors};
4use crate::parse::parse_enum::SharedEnumDeclarationParser;
5use crate::parse::parse_extern_mod::ForeignModParser;
6use crate::parse::parse_struct::SharedStructDeclarationParser;
7use crate::SwiftBridgeModule;
8use proc_macro2::TokenTree;
9use quote::{quote, ToTokens};
10use syn::parse::{Parse, ParseStream};
11use syn::{Item, ItemMod, Token};
12
13mod parse_enum;
14mod parse_extern_mod;
15mod parse_struct;
16
17mod type_declarations;
18pub(crate) use self::type_declarations::*;
19
20impl Parse for SwiftBridgeModule {
21    fn parse(input: ParseStream) -> syn::Result<Self> {
22        let module_and_errors: SwiftBridgeModuleAndErrors = input.parse()?;
23
24        module_and_errors.errors.combine_all()?;
25
26        Ok(module_and_errors.module)
27    }
28}
29
30pub(crate) struct SwiftBridgeModuleAndErrors {
31    pub module: SwiftBridgeModule,
32    pub errors: ParseErrors,
33}
34
35/// The language that a bridge type or function's implementation lives in.
36#[derive(Debug, PartialEq, Copy, Clone)]
37pub(crate) enum HostLang {
38    /// The type or function is defined Rust.
39    Rust,
40    /// The type or function is defined Swift.
41    Swift,
42}
43
44impl HostLang {
45    pub fn is_rust(&self) -> bool {
46        matches!(self, HostLang::Rust)
47    }
48
49    pub fn is_swift(&self) -> bool {
50        matches!(self, HostLang::Swift)
51    }
52}
53
54impl Parse for SwiftBridgeModuleAndErrors {
55    fn parse(input: ParseStream) -> syn::Result<Self> {
56        let mut errors = ParseErrors::new();
57
58        if let Ok(item_mod) = input.parse::<ItemMod>() {
59            let module_name = item_mod.ident;
60            let vis = item_mod.vis;
61
62            let mut functions = vec![];
63            let mut type_declarations = TypeDeclarations::default();
64            let mut unresolved_types = vec![];
65            let mut cfg_attrs = vec![];
66
67            for attr in item_mod.attrs {
68                match attr.path.to_token_stream().to_string().as_str() {
69                    "cfg" => {
70                        let cfg: CfgAttr = syn::parse2(attr.tokens)?;
71                        cfg_attrs.push(cfg);
72                    }
73                    _ => {}
74                };
75            }
76
77            for outer_mod_item in item_mod.content.unwrap().1 {
78                match outer_mod_item {
79                    Item::ForeignMod(foreign_mod) => {
80                        ForeignModParser {
81                            errors: &mut errors,
82                            type_declarations: &mut type_declarations,
83                            functions: &mut functions,
84                            unresolved_types: &mut unresolved_types,
85                        }
86                        .parse(foreign_mod)?;
87                    }
88                    Item::Struct(item_struct) => {
89                        let shared_struct = SharedStructDeclarationParser {
90                            item_struct,
91                            errors: &mut errors,
92                        }
93                        .parse()?;
94                        type_declarations.insert(
95                            shared_struct.name.to_string(),
96                            TypeDeclaration::Shared(SharedTypeDeclaration::Struct(shared_struct)),
97                        );
98                    }
99                    Item::Enum(item_enum) => {
100                        let shared_enum = SharedEnumDeclarationParser {
101                            item_enum,
102                            errors: &mut errors,
103                        }
104                        .parse()?;
105                        type_declarations.insert(
106                            shared_enum.name.to_string(),
107                            TypeDeclaration::Shared(SharedTypeDeclaration::Enum(shared_enum)),
108                        );
109                    }
110                    invalid_item => {
111                        let error = ParseError::InvalidModuleItem { item: invalid_item };
112                        errors.push(error);
113                    }
114                };
115            }
116
117            for unresolved_type in unresolved_types.into_iter() {
118                if BridgedType::new_with_type(&unresolved_type, &type_declarations).is_some() {
119                    continue;
120                }
121
122                errors.push(ParseError::UndeclaredType {
123                    ty: unresolved_type.clone(),
124                });
125            }
126
127            let module = SwiftBridgeModule {
128                name: module_name,
129                vis,
130                types: type_declarations,
131                functions,
132                swift_bridge_path: syn::parse2(quote! { swift_bridge }).unwrap(),
133                cfg_attrs,
134            };
135            Ok(SwiftBridgeModuleAndErrors { module, errors })
136        } else {
137            return Err(syn::Error::new_spanned(
138                input.to_string(),
139                "Only modules are supported.",
140            ));
141        }
142    }
143}
144
145// Used to fast-forward our attribute parsing to the next attribute when we've run into an
146// issue parsing the current attribute.
147fn move_input_cursor_to_next_comma(input: ParseStream) {
148    if !input.peek(Token![,]) {
149        let _ = input.step(|cursor| {
150            let mut current_cursor = *cursor;
151
152            while let Some((tt, next)) = current_cursor.token_tree() {
153                match &tt {
154                    TokenTree::Punct(punct) if punct.as_char() == ',' => {
155                        return Ok(((), current_cursor));
156                    }
157                    _ => current_cursor = next,
158                }
159            }
160
161            Ok(((), current_cursor))
162        });
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169    use crate::test_utils::{parse_errors, parse_ok};
170
171    /// Verify that we can parse a cfg feature from a module.
172    #[test]
173    fn parse_module_cfg_feature() {
174        let tokens = quote! {
175            #[swift_bridge::bridge]
176            #[cfg(feature = "some-feature")]
177            mod foo {}
178        };
179
180        let module = parse_ok(tokens);
181
182        assert_eq!(module.cfg_attrs.len(), 1);
183
184        match &module.cfg_attrs[0] {
185            CfgAttr::Feature(feature) => {
186                assert_eq!(feature.value(), "some-feature")
187            }
188        };
189    }
190
191    /// Verify that we get an error when parsing an unsupported module item, such as a
192    /// `use` statement.
193    #[test]
194    fn invalid_module_item() {
195        let tokens = quote! {
196            #[swift_bridge::bridge]
197            mod foo {
198                use std;
199            }
200        };
201
202        let errors = parse_errors(tokens);
203
204        assert_eq!(errors.len(), 1);
205        match &errors[0] {
206            ParseError::InvalidModuleItem { item } => {
207                assert!(matches!(item, Item::Use(_)))
208            }
209            _ => panic!(),
210        }
211    }
212}